From 384d7bac31cbe100b241fb5813f86c25593fc9ca Mon Sep 17 00:00:00 2001 From: Daniel Flanagan Date: Wed, 15 Dec 2021 16:14:36 -0600 Subject: [PATCH] Basic multiplayer working? --- objects/player.tscn | 22 +++++----- project.godot | 34 ++++++++++++++ screens/game.tscn | 43 ++++++++++++------ screens/main_menu.tscn | 24 +++++----- scripts/global/global.gd | 4 +- .../global/signalled_webrtc_multiplayer.gd | 24 +++++++++- scripts/objects/player.gd | 25 ++++++----- scripts/screens/game.gd | 44 ++++++++++++++----- scripts/screens/main_menu.gd | 2 + scripts/screens/multiplayer_lobby.gd | 8 ++-- 10 files changed, 166 insertions(+), 64 deletions(-) diff --git a/objects/player.tscn b/objects/player.tscn index f7792cc..1c7cf50 100644 --- a/objects/player.tscn +++ b/objects/player.tscn @@ -24,11 +24,6 @@ flags = 4 atlas = ExtResource( 1 ) region = Rect2( 54, 468, 18, 36 ) -[sub_resource type="AtlasTexture" id=9] -flags = 4 -atlas = ExtResource( 1 ) -region = Rect2( 144, 468, 18, 36 ) - [sub_resource type="AtlasTexture" id=5] flags = 4 atlas = ExtResource( 1 ) @@ -49,6 +44,11 @@ flags = 4 atlas = ExtResource( 1 ) region = Rect2( 126, 468, 18, 36 ) +[sub_resource type="AtlasTexture" id=9] +flags = 4 +atlas = ExtResource( 1 ) +region = Rect2( 144, 468, 18, 36 ) + [sub_resource type="SpriteFrames" id=10] animations = [ { "frames": [ SubResource( 1 ), SubResource( 2 ), SubResource( 3 ), SubResource( 4 ) ], @@ -56,15 +56,15 @@ animations = [ { "name": "idle", "speed": 6.0 }, { -"frames": [ SubResource( 9 ) ], -"loop": true, -"name": "hurt", -"speed": 5.0 -}, { "frames": [ SubResource( 5 ), SubResource( 6 ), SubResource( 7 ), SubResource( 8 ) ], "loop": true, "name": "run", "speed": 12.0 +}, { +"frames": [ SubResource( 9 ) ], +"loop": true, +"name": "hurt", +"speed": 5.0 } ] [sub_resource type="ConvexPolygonShape2D" id=11] @@ -81,7 +81,7 @@ modulate = Color( 0.384314, 0.796078, 1, 1 ) position = Vector2( 0, -18 ) frames = SubResource( 10 ) animation = "idle" -frame = 1 +frame = 2 playing = true [node name="collider" type="CollisionShape2D" parent="."] diff --git a/project.godot b/project.godot index 58b0109..7fd50fb 100644 --- a/project.godot +++ b/project.godot @@ -38,6 +38,40 @@ Global="*res://scripts/global/global.gd" singletons=[ "res://webrtc/webrtc.tres" ] +[input] + +ui_cancel={ +"deadzone": 0.5, +"events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":16777217,"physical_scancode":0,"unicode":0,"echo":false,"script":null) +, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":0,"button_index":1,"pressure":0.0,"pressed":false,"script":null) +, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":16777220,"physical_scancode":0,"unicode":0,"echo":false,"script":null) + ] +} +up={ +"deadzone": 0.5, +"events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":87,"physical_scancode":0,"unicode":0,"echo":false,"script":null) +, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":16777232,"physical_scancode":0,"unicode":0,"echo":false,"script":null) + ] +} +down={ +"deadzone": 0.5, +"events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":83,"physical_scancode":0,"unicode":0,"echo":false,"script":null) +, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":16777234,"physical_scancode":0,"unicode":0,"echo":false,"script":null) + ] +} +left={ +"deadzone": 0.5, +"events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":65,"physical_scancode":0,"unicode":0,"echo":false,"script":null) +, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":16777231,"physical_scancode":0,"unicode":0,"echo":false,"script":null) + ] +} +right={ +"deadzone": 0.5, +"events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":68,"physical_scancode":0,"unicode":0,"echo":false,"script":null) +, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":16777233,"physical_scancode":0,"unicode":0,"echo":false,"script":null) + ] +} + [physics] common/enable_pause_aware_picking=true diff --git a/screens/game.tscn b/screens/game.tscn index e3ae6b0..96cadb9 100644 --- a/screens/game.tscn +++ b/screens/game.tscn @@ -1,32 +1,49 @@ -[gd_scene load_steps=2 format=2] +[gd_scene load_steps=3 format=2] [ext_resource path="res://scripts/screens/game.gd" type="Script" id=1] +[ext_resource path="res://assets/theme.tres" type="Theme" id=2] [node name="game" type="Node2D"] script = ExtResource( 1 ) [node name="camera" type="Camera2D" parent="."] +offset = Vector2( 100, 0 ) current = true -zoom = Vector2( 0.5, 0.5 ) +drag_margin_left = 0.0 +drag_margin_top = 0.0 +drag_margin_right = 0.0 +drag_margin_bottom = 0.0 +editor_draw_limits = true -[node name="Label" type="Label" parent="."] -margin_left = 93.0 -margin_top = 109.0 -margin_right = 253.0 -margin_bottom = 123.0 +[node name="CanvasLayer" type="CanvasLayer" parent="."] + +[node name="ui" type="MarginContainer" parent="CanvasLayer"] +anchor_right = 1.0 +anchor_bottom = 1.0 +theme = ExtResource( 2 ) +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="v" type="VBoxContainer" parent="CanvasLayer/ui"] +margin_right = 1024.0 +margin_bottom = 600.0 + +[node name="Label" type="Label" parent="CanvasLayer/ui/v"] +margin_right = 1024.0 +margin_bottom = 21.0 text = "You are playing the game" __meta__ = { "_edit_use_anchors_": false } -[node name="Button" type="Button" parent="Label"] -margin_left = 29.0 -margin_top = 177.0 -margin_right = 120.0 -margin_bottom = 197.0 +[node name="Button" type="Button" parent="CanvasLayer/ui/v"] +margin_top = 25.0 +margin_right = 1024.0 +margin_bottom = 52.0 text = "Leave Game" __meta__ = { "_edit_use_anchors_": false } -[connection signal="pressed" from="Label/Button" to="." method="_on_Button_pressed"] +[connection signal="pressed" from="CanvasLayer/ui/v/Button" to="." method="_on_Button_pressed"] diff --git a/screens/main_menu.tscn b/screens/main_menu.tscn index 02f80b4..dfca1ae 100644 --- a/screens/main_menu.tscn +++ b/screens/main_menu.tscn @@ -1,20 +1,22 @@ -[gd_scene load_steps=5 format=2] +[gd_scene load_steps=6 format=2] [ext_resource path="res://assets/fonts/iosevkalyte/iosevkalyte-regular.ttf" type="DynamicFontData" id=1] [ext_resource path="res://assets/img/cross.png" type="Texture" id=2] [ext_resource path="res://scripts/screens/main_menu.gd" type="Script" id=3] +[ext_resource path="res://assets/theme.tres" type="Theme" id=4] [sub_resource type="DynamicFont" id=1] size = 70 font_data = ExtResource( 1 ) -[node name="Control" type="MarginContainer"] +[node name="main_menu" type="MarginContainer"] anchor_right = 1.0 anchor_bottom = 1.0 margin_left = 20.0 margin_top = 20.0 margin_right = -20.0 margin_bottom = -20.0 +theme = ExtResource( 4 ) script = ExtResource( 3 ) __meta__ = { "_edit_use_anchors_": false @@ -77,9 +79,9 @@ size_flags_vertical = 3 [node name="v" type="VBoxContainer" parent="v/c"] margin_left = 124.0 -margin_top = 153.0 +margin_top = 150.0 margin_right = 859.0 -margin_bottom = 352.0 +margin_bottom = 356.0 [node name="title" type="Label" parent="v/c/v"] margin_right = 735.0 @@ -93,19 +95,19 @@ valign = 1 [node name="h" type="HBoxContainer" parent="v/c/v"] margin_top = 185.0 margin_right = 735.0 -margin_bottom = 199.0 +margin_bottom = 206.0 alignment = 1 [node name="made_by" type="Label" parent="v/c/v/h"] -margin_left = 312.0 -margin_right = 371.0 -margin_bottom = 14.0 +margin_left = 305.0 +margin_right = 369.0 +margin_bottom = 21.0 text = "made by " [node name="link" type="LinkButton" parent="v/c/v/h"] -margin_left = 375.0 -margin_right = 422.0 -margin_bottom = 14.0 +margin_left = 373.0 +margin_right = 429.0 +margin_bottom = 21.0 hint_tooltip = "Open https://lyte.dev" text = "lytedev" diff --git a/scripts/global/global.gd b/scripts/global/global.gd index 6aa1b97..a2656ea 100644 --- a/scripts/global/global.gd +++ b/scripts/global/global.gd @@ -5,12 +5,14 @@ const SignalledWebRTCMultiplayer = preload("./signalled_webrtc_multiplayer.gd") onready var client = SignalledWebRTCMultiplayer.new() onready var onetime_cmd_flags = { + "--singleplayer": false, "--multiplayer": false, "--create-lobby": false, "--join-first-available-lobby": false, } func _ready(): + get_tree().use_font_oversampling = true add_child(client) for flag in onetime_cmd_flags.keys(): if flag in OS.get_cmdline_args(): @@ -29,7 +31,7 @@ func main_menu(): goto_scene("main_menu") func start_singleplayer_game(): - client.close() + client.singleplayer() goto_game() func goto_game(): diff --git a/scripts/global/signalled_webrtc_multiplayer.gd b/scripts/global/signalled_webrtc_multiplayer.gd index d0a00c2..573f4f9 100644 --- a/scripts/global/signalled_webrtc_multiplayer.gd +++ b/scripts/global/signalled_webrtc_multiplayer.gd @@ -78,9 +78,17 @@ func connect_to_signaller(url = signaller_url): if result != OK: print("Failed to connect to WebSocket signalling server at %s: %s" % [signaller_url, result]) +func singleplayer(): + close() + webrtc = WebRTCMultiplayer.new() + webrtc.initialize(1, false) + get_tree().network_peer = webrtc + _create_peer({id = 1}) + func close(): if webrtc != null: webrtc.close() websocket.disconnect_from_host() + get_tree().network_peer = null func set_display_name(new_display_name: String): display_name = new_display_name @@ -125,9 +133,11 @@ func send_answer(id, answer) -> int: return _send_json({"id": id, "answer": answer }, "answer") func _webrtc_peer_connected(id): + peers[id].connected = true emit_signal("webrtc_peer_connected", id) func _webrtc_peer_disconnected(id): + peers[id].connected = false emit_signal("webrtc_peer_disconnected", id) func _webrtc_connection_succeeded(): @@ -222,8 +232,17 @@ func _create_peer(data): # this guarantees only one peer sends the offer and that offers are never # sent to ourselves? if int(id) > webrtc.get_unique_id(): peer.create_offer() + peers[int(id)] = { + connected = false, + ready = false, + name = "", + } emit_signal("peer_created", data) +func _delete_peer(id): + if webrtc.has_peer(id): webrtc.remove_peer(id) + if peers.has(id): peers.erase(id) + func _webrtc_offer_created(type, data, id): print("WebRTC %s created for peer %s" % [type, id]) if not webrtc.has_peer(int(id)): return @@ -237,7 +256,8 @@ func _new_ice_candidate(mid, index, sdp, id): print("New ICE candidate for peer %s" % id) send_candidate(id, mid, index, sdp) -func _signaller_peer_left(id): if peers.has(id): peers.delete(id) +func _signaller_peer_left(id): + _delete_peer(id) func _signaller_peer_data(peers): for peer in peers: @@ -262,7 +282,7 @@ func _webrtc_answer_received(data): func _webrtc_candidate_received(data): if webrtc.has_peer(data.id): print("Adding ice candidate for peer %s" % data.id) - print("--> Candidate: %s" % JSON.print(data.candidate)) + print("--> Candidate: %s" % JSON.print(data)) webrtc.get_peer(data.id).connection.add_ice_candidate(data.mid, data.index, data.sdp) else: print("Received candidate for non-existant peer %s" % data.id) diff --git a/scripts/objects/player.gd b/scripts/objects/player.gd index 2439ac5..112a5f2 100644 --- a/scripts/objects/player.gd +++ b/scripts/objects/player.gd @@ -77,18 +77,21 @@ func maybe_hide_health_bar(): func _ready(): # rset_config("acceleration", MultiplayerAPI.RPC_MODE_REMOTESYNC) # rset_config("velocity", MultiplayerAPI.RPC_MODE_REMOTESYNC) - rset_config("position", MultiplayerAPI.RPC_MODE_SLAVE) + # rset_config("position", MultiplayerAPI.RPC_MODE_SLAVE) set_display_name(name) func set_display_name(i_name): display_name = i_name - if len(get_tree().get_network_connected_peers()) > 1: - name_displayer.text = display_name - else: - name_displayer.text = "" + name_displayer.text = display_name -static func int_input_action(action): - return int(Input.is_action_pressed(action)) +static func int_input_action(actions) -> int: + if typeof(actions) == TYPE_ARRAY: + var result = false + for action in actions: + result = result or Input.is_action_pressed(action) + return int(result) + else: + return int(Input.is_action_pressed(actions)) func _physics_process(delta): self.mana += (10*delta) @@ -101,8 +104,8 @@ func _physics_process(delta): if collision.collider is RigidBody2D: collision.collider.apply_central_impulse(-collision.normal * weight) - if is_network_master(): - rset_unreliable("position", position) + # if is_network_master(): + # rset_unreliable("position", position) func _process_input(): if is_network_master(): @@ -110,8 +113,8 @@ func _process_input(): # probably want to implement a "whichever one was pressed last" so that # players move as fast as possible without having to worry about switching # key frame perfectly - iv.x = int_input_action("ui_right") - int_input_action("ui_left") - iv.y = int_input_action("ui_down") - int_input_action("ui_up") + iv.x = int_input_action(["ui_right", "right"]) - int_input_action(["ui_left", "left"]) + iv.y = int_input_action(["ui_down", "down"]) - int_input_action(["ui_up", "up"]) rset("acceleration", iv.normalized() * speed) func _process_animation(): diff --git a/scripts/screens/game.gd b/scripts/screens/game.gd index c5df8ea..bcb00ef 100644 --- a/scripts/screens/game.gd +++ b/scripts/screens/game.gd @@ -1,26 +1,48 @@ extends Node2D -onready var player = preload("res://objects/player.tscn") +onready var player_obj = preload("res://objects/player.tscn") onready var camera = $camera -onready var camera_target = null onready var zoom_levels = [0.25, 0.5, 1, 1.5, 2, 2.5, 3] onready var current_zoom_level_index = 2 +onready var player = null +onready var players = null func _ready(): # TODO: probably have to wait for all peers to be ready before we add players - rpc("add_player", get_tree().get_network_unique_id()) + if Global.client.is_host(): + for id in get_tree().get_network_connected_peers(): + Global.client.peers[id].ready = false + ready(1) + else: + rpc("ready", get_tree().get_network_unique_id()) -func _process(delta): - pass +remotesync func ready(id): + print("Ready %d" % id) + Global.client.peers[id].ready = true + var all_ready = true + for id in Global.client.peers: all_ready = all_ready and Global.client.peers[id].ready + if all_ready: start() -remotesync func add_player(peer_id): - var new_player = player.instance() - new_player.name = Global.negotiator.peer_by_peer_id(peer_id).name - new_player.set_network_master(peer_id) +func start(): + print("Starting Game...") + # for each peer + for id in get_tree().get_network_connected_peers(): + print("Creating player for peer %d" % id) + rpc("add_player", id) + rpc("add_player", 1) + +func _process(_delta): + if player: camera.offset = player.position + +remotesync func add_player(id): + var new_player = player_obj.instance() + if id == get_tree().get_network_unique_id(): player = new_player + new_player.name = Global.client.peers[id].name + new_player.set_network_master(id) add_child(new_player) - new_player.global_position = Vector2(100, 100) - print("Added player: %s for peer %s" % [new_player, peer_id]) + new_player.global_position = Vector2(-100 + (id % 200), -100 + (id % 200)) + print("Added player: %s for peer %s" % [new_player, id]) func _on_Button_pressed(): Global.main_menu() diff --git a/scripts/screens/main_menu.gd b/scripts/screens/main_menu.gd index 962ce7f..7f2a380 100644 --- a/scripts/screens/main_menu.gd +++ b/scripts/screens/main_menu.gd @@ -6,6 +6,8 @@ func _ready(): $v/h/singleplayer.shortcut = Global.key_shortcut(KEY_S) if Global.check_onetime_flag("multiplayer"): _on_multiplayer_pressed() + if Global.check_onetime_flag("singleplayer"): + _on_Singleplayer_pressed() func _on_Singleplayer_pressed(): Global.start_singleplayer_game() diff --git a/scripts/screens/multiplayer_lobby.gd b/scripts/screens/multiplayer_lobby.gd index cec830b..fc49f6e 100644 --- a/scripts/screens/multiplayer_lobby.gd +++ b/scripts/screens/multiplayer_lobby.gd @@ -61,7 +61,7 @@ func _add_peer(id, p): if p.id == 1: p.ready = true new_peer.set_with_dict(p) peers_grid.add_child(new_peer) - peers[id] = new_peer + peers[int(id)] = new_peer add_chat("> %s joined the lobby" % new_peer.display_name) func _update_peer(id, peer_data): @@ -123,14 +123,14 @@ func _on_start_pressed(): if is_host: rpc("start_game") remotesync func start_game(): - Global.negotiator.peers = peers - Global.negotiator.peers_id_mappings = peers_id_mappings + # TODO: seal lobby + get_tree().refuse_new_network_connections = true Global.goto_game() remotesync func set_ready(ready): var from = get_tree().get_rpc_sender_id() print("Set Ready: %s %s" % [from, ready]) - Global.client.peers[from].ready = ready + peers[from].ready = ready if is_host: update_can_start() func _on_TextEdit_text_entered(_new_text):