This game is not designed to run on your device. Add it to a collection to play later, or you can try to run it anyway.

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?)