fix: create local player on client so WASD movement works
client_main.gd: when server spawns our player, create a player node with authority - Camera follows local player from 3/4 overhead view - player.gd WASD input sends position to server via RPC - Server validates and broadcasts to all peers
This commit is contained in:
@@ -22,6 +22,10 @@ extends Node3D
|
|||||||
var remote_players: Dictionary = {}
|
var remote_players: Dictionary = {}
|
||||||
var connected: bool = false
|
var connected: bool = false
|
||||||
|
|
||||||
|
# Our local player node (created when server spawns us)
|
||||||
|
var _local_player: Node = null
|
||||||
|
var _camera: Camera3D = null
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# Lifecycle
|
# Lifecycle
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
@@ -45,8 +49,17 @@ func _connect_to_server() -> void:
|
|||||||
_connect_to_server()
|
_connect_to_server()
|
||||||
return
|
return
|
||||||
|
|
||||||
# Setup camera and lights
|
# Setup camera (fixed overhead until a player is spawned)
|
||||||
_setup_scene()
|
_camera = _make_camera()
|
||||||
|
add_child(_camera)
|
||||||
|
|
||||||
|
# Ambient light so we can see the box meshes
|
||||||
|
var light := DirectionalLight3D.new()
|
||||||
|
light.light_energy = 1.0
|
||||||
|
light.shadow_enabled = true
|
||||||
|
light.position = Vector3(10, 20, 10)
|
||||||
|
add_child(light)
|
||||||
|
light.look_at(Vector3.ZERO)
|
||||||
|
|
||||||
connected = true
|
connected = true
|
||||||
|
|
||||||
@@ -57,31 +70,41 @@ func _connect_to_server() -> void:
|
|||||||
|
|
||||||
print("[ClientMain] Connected to server. Peer ID: %d" % multiplayer.get_unique_id())
|
print("[ClientMain] Connected to server. Peer ID: %d" % multiplayer.get_unique_id())
|
||||||
|
|
||||||
func _setup_scene() -> void:
|
func _make_camera() -> Camera3D:
|
||||||
# Simple 3/4 top-down camera for Phase 0 testing
|
var cam := Camera3D.new()
|
||||||
var camera := Camera3D.new()
|
cam.current = true
|
||||||
camera.current = true
|
cam.position = Vector3(0, 16, 12)
|
||||||
camera.position = Vector3(0, 16, 12)
|
cam.rotation_degrees.x = -55
|
||||||
camera.rotation_degrees.x = -55
|
return cam
|
||||||
add_child(camera)
|
|
||||||
|
func _process(_delta: float) -> void:
|
||||||
# Ambient light so we can see the box meshes
|
# Make camera follow local player if we have one
|
||||||
var light := DirectionalLight3D.new()
|
if _local_player and _camera:
|
||||||
light.light_energy = 1.0
|
_camera.position = _local_player.position + Vector3(0, 16, 12)
|
||||||
light.shadow_enabled = true
|
# Always look down at the player
|
||||||
light.position = Vector3(10, 20, 10)
|
var look_target := _local_player.position
|
||||||
add_child(light)
|
look_target.y = _local_player.position.y + 2.0
|
||||||
light.look_at(Vector3.ZERO)
|
_camera.look_at(look_target)
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# Player replication handlers (called when server broadcasts via RPC)
|
# Player replication handlers (called when server broadcasts via RPC)
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
func _on_remote_player_spawned(peer_id: int, pos: Vector3) -> void:
|
func _on_remote_player_spawned(peer_id: int, pos: Vector3) -> void:
|
||||||
if peer_id == multiplayer.get_unique_id():
|
if peer_id == multiplayer.get_unique_id():
|
||||||
# This is OUR player — the server has authority over it,
|
# THIS IS OUR PLAYER — create a local player node and attach camera.
|
||||||
# but we don't need to create a duplicate. Our local player
|
# The player.gd script handles WASD input and sends position to server.
|
||||||
# node is managed by the server's authority system.
|
if _local_player:
|
||||||
# The player.gd script will handle input on this peer.
|
push_warning("[ClientMain] Local player already exists, respawning")
|
||||||
|
_local_player.queue_free()
|
||||||
|
|
||||||
|
_local_player = player_scene.instantiate()
|
||||||
|
_local_player.name = "LocalPlayer"
|
||||||
|
_local_player.position = pos
|
||||||
|
# Set authority so player.gd detects is_local = true and enables input
|
||||||
|
_local_player.set_multiplayer_authority(peer_id)
|
||||||
|
add_child(_local_player, true)
|
||||||
|
|
||||||
|
print("[ClientMain] Spawned LOCAL player at (%.1f, %.1f)" % [pos.x, pos.z])
|
||||||
return
|
return
|
||||||
|
|
||||||
if peer_id in remote_players:
|
if peer_id in remote_players:
|
||||||
@@ -107,6 +130,11 @@ func _on_remote_player_spawned(peer_id: int, pos: Vector3) -> void:
|
|||||||
|
|
||||||
func _on_remote_player_despawned(peer_id: int) -> void:
|
func _on_remote_player_despawned(peer_id: int) -> void:
|
||||||
if peer_id == multiplayer.get_unique_id():
|
if peer_id == multiplayer.get_unique_id():
|
||||||
|
# Our player was despawned
|
||||||
|
if _local_player:
|
||||||
|
_local_player.queue_free()
|
||||||
|
_local_player = null
|
||||||
|
print("[ClientMain] Local player despawned")
|
||||||
return
|
return
|
||||||
|
|
||||||
if peer_id in remote_players:
|
if peer_id in remote_players:
|
||||||
@@ -118,7 +146,9 @@ func _exit_tree() -> void:
|
|||||||
if connected:
|
if connected:
|
||||||
NetworkManager.stop()
|
NetworkManager.stop()
|
||||||
|
|
||||||
# Clean up any remaining remote players
|
# Clean up any remaining players
|
||||||
|
if _local_player:
|
||||||
|
_local_player.queue_free()
|
||||||
for p in remote_players.values():
|
for p in remote_players.values():
|
||||||
p.queue_free()
|
p.queue_free()
|
||||||
remote_players.clear()
|
remote_players.clear()
|
||||||
|
|||||||
Reference in New Issue
Block a user