21: Add the following to “connection_display.gd”.
[CODEBLOCK]
extends Control @onready var url_input: TextEdit = %UrlInput
@onready var port_input: TextEdit = %PortInput
@onready var connect_button: Button = %ConnectButton
@onready var disconnect_button: Button = %DisconnectButton func _ready() -> void:
disconnect_button.visible = false
multiplayer.connection_failed.connect(_on_connection_failed) func _on_connect_button_pressed() -> void:
connect_button.visible = false
disconnect_button.visible = true
url_input.editable = false
port_input.editable = false func _on_disconnect_button_pressed() -> void:
connect_button.visible = true
disconnect_button.visible = false
url_input.editable = true
port_input.editable = true # If we are unable to connect then we enable the connect button and text inputs again.
func _on_connection_failed() -> void:
connect_button.visible = true
disconnect_button.visible = false
url_input.editable = true
port_input.editable = true
[CODEBLOCK] [DISCLAIMER]
We will revisit this script later after creating the “MultiplayerManager”. 22: Connect the “pressed()” signal on “ConnectButton” to “ConnectionDisplay”. Repeat step on “DisconnectButton”.
https://docs.godotengine.org/en/stable/getting_started/step_by_step/signals.html#connecting-a-signal-in-the-editor 23: Add a child node to “Main” of type “MeshInstance3D” and rename it to “Ground”. 24: Assign a new “PlaneMesh” to “MeshInstance3D” and set the “Size” to X:10m and Y:10m.
This covers creating a Multiplayer Manager which handles the networking aspects of our game. 1: Create a new. Save it as “multiplayer_manager.tscn” and select the “Node” root type.
[CODEBLOCK]
extends Node # Internal server port
const PORT: int = 8080 @onready var peer = WebSocketMultiplayerPeer.new() func _ready() -> void:
# The server should always automatically start
if is_server():
start_server() func start_server() -> void:
var err: Error = peer.create_server(PORT)
if err == 0:
multiplayer.multiplayer_peer = peer
multiplayer.peer_disconnected.connect(_on_peer_disconnected) func start_client(url: String, port: String) -> void:
# If you want to use localhost (i.e you run a windows server export on your PC for testing), then it must be ws:// instead.
var err: Error = peer.create_client(“wss://” + url + ”:” + port)
if err == 0:
multiplayer.multiplayer_peer = peer func disconnect_from_server() -> void:
peer.close() func is_server() -> bool:
# Exports have tags and this lets us identify if the build is a dedicated server.
return OS.has_feature(“dedicated_server”) func _on_peer_disconnected(id: int) -> void:
var nodes = get_tree().get_nodes_in_group(“Players”)
for node in nodes:
if node.get_multiplayer_authority() == id:
node.queue_free()
break
[CODEBLOCK] 4: Add “multiplayer_manager.tscn” as an Autoload.
https://docs.godotengine.org/en/latest/tutorials/scripting/singletons_autoload.html#autoload
5.a: Add [CODE] MultiplayerManager.start_client(url_input.text, port_input.text) [CODE] to “_on_connect_button_pressed()”.
5.b: Add [CODE] MultiplayerManager.disconnect_from_server() [CODE] to “_on_disconnect_button_pressed()”. It should look like this below. [CODEBLOCK]
extends Control @onready var url_input: TextEdit = %UrlInput
@onready var port_input: TextEdit = %PortInput
@onready var connect_button: Button = %ConnectButton
@onready var disconnect_button: Button = %DisconnectButton func _ready() -> void:
disconnect_button.visible = false func _on_connect_button_pressed() -> void:
connect_button.visible = false
disconnect_button.visible = true
url_input.editable = false
port_input.editable = false
MultiplayerManager.start_client(url_input.text, port_input.text) func _on_disconnect_button_pressed() -> void:
connect_button.visible = true
disconnect_button.visible = false
url_input.editable = true
port_input.editable = true
MultiplayerManager.disconnect_from_server()
[CODEBLOCK] Part 3 This covers creating the player scene and adding movement inputs to the project. 1: Create a new save. Save it as “player.tscn” and select the “3D Scene” root type. 2: Open “player.tscn” and add a child node to “Player” of type “MeshInstance3D”.
6a: Choose “Player”.
6b: Select “position” under “Node3D”.
https://docs.godotengine.org/en/stable/tutorials/scripting/groups.html#using-the-node-dock
[CODEBLOCK]
extends Node3D @export var speed: float = 5.0 func _process(delta: float) -> void:
if not is_multiplayer_authority(): return
var input_dir: Vector2 = Input.get_vector(“move_left”, “move_right”, “move_forward”, “move_backward”)
var offset: Vector3 = (transform.basis * Vector3(input_dir.x, 0, input_dir.y)).normalized() * speed * delta
translate(offset)
[CODEBLOCK] 10: Add the following actions to the “Input Map” (Project Settings/Input Map).
10a: “move_forward”, “move_backward”, “move_left” and “move_right”.
[CODEBLOCK]
extends MultiplayerSpawner func _ready() -> void:
spawn_function = spawn_player
# We only want the server spawning new players when new peers connect
if MultiplayerManager.is_server():
multiplayer.peer_connected.connect(_on_peer_connected) func spawn_player(peer_id: int) -> Node:
var player: Node = load(get_spawnable_scene(0)).instantiate()
player.set_multiplayer_authority(peer_id)
return player func _on_peer_connected(id: int) -> void:
spawn(id)
[CODEBLOCK] 3: Select “MultiplayerSpawner” and set “Spawn Path” to “Players”.
https://documentation.playflowcloud.com/quickstart [IMPORTANT DISCLAIMER]
When adding the port to your configuration, select TCP for the “Protocol”, and toggle on Enable TLS. We need to have TLS enabled because we’re using “wss://” protocol for our client connection, which is required if the game is played in the browser. You can skip steps 1 and 2 if you don’t want to run multiple instances (windows) when playing the game. 1: Back in Godot, open the “Run Instances” window (Debug/Customize Run Instances…” and toggle on Enable Multiple Instances. 2: Set the number of instances to 2.