OK so to recap.
Player.tscn
Player.gd
## more code
func _enter_tree() -> void:
set_multiplayer_authority(str(name).to_int())
pass
func _ready():
if not is_multiplayer_authority(): return
## more code
camera_3d.current = true
func _unhandled_input(_event):
if not is_multiplayer_authority(): return
## more code
if Input.is_action_just_pressed("action_throw"):
spawn_item.rpc()
## more code
func _input(event):
if not is_multiplayer_authority(): return
## more code
func _physics_process(delta):
if not is_multiplayer_authority(): return
## more code
@rpc("any_peer","call_local")
func spawn_item():
var basis_z = camera_3d.global_transform.basis.z
var g_t = global_transform
spawn_item_signal.emit(g_t, basis_z)
pass
## more code
Note the spawn_item
is my own function, so just insert your code there or adjust to your needs. Its just a demonstration how it works.
MultiplayerSynchronizer
Had to add Camera3D
global_transform
to get the spawn_item
to receive the correct transform value, otherwise i was getting vec3(0,0,1)
Box.tscn
No script in that scene
MultiplayerSynchronizer
world.tscn
I messed up here but it still works. I placed the Players
node underMultiplayerSpawner
. Players
node should be a child of the World
node. But if you reference it in the code it still works as you saw in the video above.
Add scenes to the spawner list. You can add player scene too but i instantiate players on _ready() function of the World node so i dont need it atm. but you will need it once you start adding functionality for when you want players join mid game, when server is running.
World script just contains code to spawn player -> instantiate player scene, add child to Players
node. Also i got code here for spawn_item,
world.gd
extends Node3D
const PLAYER = preload("res://Assets/Player/player.tscn")
const BOX = preload("res://Assets/Items/box.tscn")
@onready var players: Node = $MultiplayerSpawner/Players
const start_pos: Vector3 = Vector3(0,3,0)
# Called when the node enters the scene tree for the first time.
func _ready() -> void:
var index =0
for i in GameManager.players:
spawn_player(GameManager.players[i])
index +=1
pass # Replace with function body.
# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta: float) -> void:
pass
func spawn_item_world(player_transform:Transform3D, basis_z:Vector3):
var new_box = BOX.instantiate()
new_box.position = player_transform.origin - (basis_z * 3)
add_child(new_box)
pass
func spawn_player(player_data:PlayerData):
var new_pos:Vector3
var new_player = PLAYER.instantiate()
new_player.name = str(player_data.id)
new_player.player_data=player_data
players.add_child(new_player)
new_player.spawn_item_signal.connect(spawn_item_world)
var player_count = players.get_children().size()
if player_count > 1:
var last_player = players.get_children()[player_count-1]
new_pos = last_player.position + Vector3(3,3,3)
new_player.position = new_pos
else:
new_player.position = start_pos
MultiplayerUI.tscn <- entry point for the game
MultiplayerUI.gd
extends Control
@export var address = "127.0.0.1"
@export var port = 8910
var peer:ENetMultiplayerPeer
@onready var host: Button = $VBoxContainer/Host
@onready var join: Button = $VBoxContainer/Join
@onready var start: Button = $VBoxContainer/Start
@onready var player_name: LineEdit = $VBoxContainer/HBoxContainer/PlayerName
const WORLD = preload("res://Assets/World/world.tscn")
# Called when the node enters the scene tree for the first time.
func _ready() -> void:
multiplayer.peer_connected.connect(peer_connected)
multiplayer.peer_disconnected.connect(peer_disconnected)
multiplayer.connected_to_server.connect(connected_to_server)
multiplayer.connection_failed.connect(connection_failed)
pass # Replace with function body.
# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta: float) -> void:
pass
# this gets called on server and clients
func peer_connected(id):
print("Player connected: ", id)
pass
# this gets called on server and clients
func peer_disconnected(id):
print("Player disconnected: ", id)
pass
# called only from clients
func connected_to_server():
print("Connected to server!")
send_player_info.rpc_id(1,player_name.text,multiplayer.get_unique_id())
pass
# called only from clients
func connection_failed(id):
print("Player connection failed: ", id)
pass
@rpc("any_peer")
func send_player_info(name, id):
if !GameManager.players.has(id):
var new_player_data:PlayerData = PlayerData.new()
new_player_data.id=id
new_player_data.player_name=name
new_player_data.score = 0.0
GameManager.players[id]=new_player_data
#GameManager.players[id] = {
#"name":name,
#"id":id,
#"score": 0
#}
if multiplayer.is_server():
for i in GameManager.players:
send_player_info.rpc(GameManager.players[i].player_name,i)
pass
@rpc("any_peer","call_local")
func start_game():
var scene = WORLD.instantiate()
get_tree().root.add_child(scene)
self.hide()
pass
func _on_host_button_down() -> void:
peer = ENetMultiplayerPeer.new()
var error = peer.create_server(port, 3)
if error != OK:
print("ERROR - Cannot host: ", error)
return
peer.get_host().compress(ENetConnection.COMPRESS_RANGE_CODER)
multiplayer.set_multiplayer_peer(peer)
print("Waiting for players!")
send_player_info(player_name.text,multiplayer.get_unique_id())
pass # Replace with function body.
func _on_join_button_down() -> void:
peer = ENetMultiplayerPeer.new()
peer.create_client(address,port)
peer.get_host().compress(ENetConnection.COMPRESS_RANGE_CODER)
multiplayer.set_multiplayer_peer(peer)
print("Waiting for players!")
pass # Replace with function body.
func _on_start_button_down() -> void:
start_game.rpc()
pass # Replace with function body.
GameManager.gd <- autoload script to keep player data global
extends Node
var players = {}
func _ready() -> void:
pass
func _process(_delta: float) -> void:
pass
Tutorials that helped: