Play this game with your friends to see who is the ULTIMATE SHAPE!

StatusReleased
PlatformsHTML5
Rating
Rated 1.0 out of 5 stars
(1 total ratings)
AuthorDeBug
Made withGodot
Tags2D, Arcade, Local multiplayer, Top-Down
Average sessionA few seconds
LanguagesEnglish
InputsGamepad (any), Joystick
MultiplayerLocal multiplayer
Player count2 - 4

Comments

Log in with itch.io to leave a comment.

Online would be cool :D Do you know about WebRTC?

I have always wanted to look into online gameplay, as it is one of my favorite features in games, but I havent found a way that is simple enough to work well in godot! I will definitely look into WebRTC!

Some code for WebRTC if you wanna learn:


Signaling server (js):

import WebSocket, { WebSocketServer } from "ws";

const wss = new WebSocketServer({ port: 9396 });

const messages = {
ASSIGN_ID: 0,
JOIN_LOBBY: 1,
PLAYERS_FOUND: 2,
SEND_ICE_CANDIDATE: 3,
SEND_DESCRIPTION: 4,
RECEIVE_ICE_CANDIDATE: 5,
RECEIVE_DESCRIPTION: 6,

};

let players = {};

let waiting_players = [];

let waiting_player_ids = [];

let next_player_id = 2;

const PLAYERS_IN_GAME = 3;

wss.on("connection", function connection(ws) {
const player_id = next_player_id;
players[player_id] = ws;
next_player_id += 1;

ws.on("close", function close() {
delete players[player_id];
console.log("Player left :(");
});

ws.on("error", console.error);

ws.on("message", function message(data) {
try {
const json_data = JSON.parse(data);
console.log(JSON.stringify(json_data));
if (json_data.message == messages.JOIN_LOBBY) {
console.log("Player wants to join lobby.");
waiting_players.push(ws);
waiting_player_ids.push(player_id);
if (waiting_players.length >= PLAYERS_IN_GAME) {
console.log("Lobby found!");
console.log(waiting_players);
/*
let lowest_id = -1;
for (let i = 0; i < PLAYERS_IN_GAME; i++) {
if (waiting_player_ids[i] < lowest_id || lowest_id == -1) {
lowest_id = waiting_player_ids[i];
}
}
for (let i = 0; i < PLAYERS_IN_GAME; i++) {
if (waiting_player_ids[i] == lowest_id) {
waiting_player_ids[i] = 1;
}
}
*/
for (let i = 0; i < PLAYERS_IN_GAME; i++) {
console.log(waiting_players[i]);
waiting_players[i].send(
JSON.stringify({
message: messages.PLAYERS_FOUND,
players: waiting_player_ids,
})
);
}
waiting_players = [];
waiting_player_ids = [];
}
}
if (json_data.message == messages.SEND_ICE_CANDIDATE) {
players[json_data.target].send(
JSON.stringify({
message: messages.RECEIVE_ICE_CANDIDATE,
from: player_id,
mid_name: json_data.mid_name,
index_name: json_data.index_name,
sdp_name: json_data.sdp_name,
})
);
}
if (json_data.message == messages.SEND_DESCRIPTION) {
console.log(json_data);
players[json_data.target].send(
JSON.stringify({
message: messages.RECEIVE_DESCRIPTION,
from: player_id,
data: json_data.data,
type: json_data.type,
})
);
}
} catch (e) {
console.log('Got strange data: "' + data + '"');
console.error(e);
}
});
ws.send(JSON.stringify({ message: messages.ASSIGN_ID, id: player_id }));

});






Client signaling (gdscript):

extends Node

class_name Signaling

@onready var web_rtc_manager : WebRTCManager = $"../WebRTC"

@onready var network_manager : NetworkManager = get_parent()

var socket = WebSocketPeer.new()

var waiting_for_join_request : bool = false

var my_id : int

var is_host : bool

var host_id : int = -1

enum messages {

ASSIGN_ID,

JOIN_LOBBY,

PLAYERS_FOUND,

SEND_ICE_CANDIDATE,

SEND_DESCRIPTION,

RECEIVE_ICE_CANDIDATE,

RECEIVE_DESCRIPTION,

}

func _ready():

socket.connect_to_url("ws://localhost:9396")

waiting_for_join_request = true

func _process(_delta):

socket.poll()

var state = socket.get_ready_state()

if state == WebSocketPeer.STATE_OPEN:

if waiting_for_join_request:

socket.send_text(JSON.stringify({"message":messages.JOIN_LOBBY}))

waiting_for_join_request = false

while socket.get_available_packet_count():

var packet_data : Dictionary = JSON.parse_string(socket.get_packet().get_string_from_utf8())

if packet_data.message == messages.ASSIGN_ID:

my_id = packet_data.id

web_rtc_manager.my_id = my_id

network_manager.id = my_id

web_rtc_manager.connected(my_id)

elif packet_data.message == messages.PLAYERS_FOUND:

web_rtc_manager.players_in_game = len(packet_data.players)

is_host = true

for player_id : int in packet_data.players:

web_rtc_manager.connect_to_player(player_id)

if player_id > my_id:

is_host = false

if player_id < host_id or host_id == -1:

host_id = player_id

network_manager.is_host = is_host

network_manager.host_id = host_id

print(packet_data.players)

print("Found players!")

elif packet_data.message == messages.RECEIVE_ICE_CANDIDATE:

web_rtc_manager.add_ice_candidate(packet_data.mid_name, packet_data.index_name, packet_data.sdp_name, packet_data.from)

elif packet_data.message == messages.RECEIVE_DESCRIPTION:

web_rtc_manager.set_remote_description(packet_data.type, packet_data.data, packet_data.from)

elif state == WebSocketPeer.STATE_CLOSING:

# Keep polling to achieve proper close.

pass

elif state == WebSocketPeer.STATE_CLOSED:

var code = socket.get_close_code()

var reason = socket.get_close_reason()

print("WebSocket closed with code: %d, reason %s. Clean: %s" % [code, reason, code != -1])

set_process(false) # Stop processing.

func send(message : messages, data : Dictionary, target : int):

data["message"] = message

data["target"] = target

socket.send_text(JSON.stringify(data))

func send_description(type : String, data : String, target : int):

send(messages.SEND_DESCRIPTION, {"data": data, "type": type}, target)

func send_ice_candidate(mid_name, index_name, sdp_name, target : int):

send(messages.SEND_ICE_CANDIDATE, {"mid_name":mid_name, "index_name":index_name, "sdp_name":sdp_name}, target)






WebRTC manager (gdscript):

extends Node

class_name WebRTCManager

@onready var signaling : Signaling = $"../Signaling"

var peer : WebRTCMultiplayerPeer = WebRTCMultiplayerPeer.new()

var my_id : int

var players_in_game : int

var connected_peers : int = 0

func _ready():

multiplayer.peer_connected.connect(peer_connected)

func connect_to_player(id : int):

var player_peer : WebRTCPeerConnection = WebRTCPeerConnection.new()

player_peer.initialize({

"iceServers" : [{ "urls": ["stun:stun.l.google.com:19302"] }]

})

player_peer.session_description_created.connect(offer_created.bind(id))

player_peer.ice_candidate_created.connect(ice_candidate_created.bind(id))

peer.add_peer(player_peer, id)

if id < my_id:

player_peer.create_offer()

func offer_created(type : String, data : String, id : int):

signaling.send_description(type, data, id)

func ice_candidate_created(mid_name, index_name, sdp_name, id : int):

signaling.send_ice_candidate(mid_name, index_name, sdp_name, id)

func add_ice_candidate(mid_name, index_name, sdp_name, id : int):

peer.get_peer(id).connection.add_ice_candidate(mid_name, index_name, sdp_name)

func set_remote_description(type : String, data : String, id : int):

peer.get_peer(id).connection.set_remote_description(type, data)

func connected(id):

peer.create_mesh(id)

multiplayer.multiplayer_peer = peer

func peer_connected(id):

print("Connection with " + str(id))

connected_peers += 1

if connected_peers == players_in_game - 1:

get_parent().connected_to_everyone()

@rpc("any_peer", "call_local")

func ping():

print("I got pinged!")

func _on_ping_button_pressed():

ping.rpc()


Yes, it's kinda complicated but it works.

Where do I paste this in godot? would it matter where I put this?

The signaling server should not be pasted into godot but into vscode or  anything and the run it on a raspberry pi or anything, the project don't work without it.

This is my multiplayer tree.

I have used vs code in the past for unity development, but are you implying that the game I make must be set up with C# in mind? how do I connect the  scripts so that the multiplayer works? (does it matter what I name the vs code project or anything, or does the code just need to be somewhere?)