You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Godot 4.2.1多人游戏:客户端玩家无法在指定位置生成

客户端玩家固定在Vector3.Zero生成,无法在指定位置Spawn(Godot多人FPS)

问题描述

我正在开发一款多人FPS游戏,现在实现生成系统时遇到问题:复制的客户端玩家始终无法在指定位置生成,固定在世界坐标Vector3.Zero处。试过各种方法都解决不了,谷歌搜过有用户碰到相同问题,但没找到解决方案。

相关代码

Network全局脚本

extends Node

var server : ENetMultiplayerPeer = null
var client : ENetMultiplayerPeer = null
var port : int = 9898
var ip_address : String = "localhost"
var max_clients : int = 20
var unique_identifier : int = -1
var game_manager : Node = null

var players : Dictionary = {}

func _ready():
    setup_network()

func setup_network() -> 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)

func host_server() -> void:
    server = ENetMultiplayerPeer.new()
    var error = server.create_server(port,max_clients)
    if error != OK:
        print("Failed to create server. Error: " + error)
        return
    server.get_host().compress(ENetConnection.COMPRESS_RANGE_CODER)
    multiplayer.multiplayer_peer = server
    unique_identifier = server.get_unique_id()
    send_player_info("Host",unique_identifier)
    game_manager.create_player(unique_identifier)

func join_server() -> void:
    client = ENetMultiplayerPeer.new()
    client.create_client(ip_address,port)
    client.get_host().compress(ENetConnection.COMPRESS_RANGE_CODER)
    multiplayer.multiplayer_peer = client
    unique_identifier = client.get_unique_id()

func peer_connected(identifier : int) -> void:
    print("player has connected, ID: " + str(identifier))
    if identifier != 1:
        game_manager.create_player(identifier)

func peer_disconnected(identifier : int) -> void:
    print("player has disconnected, ID: " + str(identifier))
    var player = game_manager.get_node_or_null(str(identifier))
    if player:
        player.queue_free()

func connected_to_server() -> void:
    print("Connected to the server")
    send_player_info.rpc_id(1,"None",multiplayer.get_unique_id())

@rpc("any_peer")
func send_player_info(username : String, identifier : int) -> void:
    if not players.has(identifier):
        players[identifier] = {
            "identifier" : identifier,
            "username" : username
        }
    if multiplayer.is_server():
        for index in players:
            send_player_info.rpc(str(players[index]),index)
func connection_failed() -> void:
    print("Connection failed")

GameZone脚本(调用Network函数、生成玩家)

class_name GameZone extends Node

@export_subgroup("Game Exports")
@export var menu : Control
@export var spawn_points : Node3D
const TestDummyPlayer : PackedScene = preload("res://Game/Objects/TestDummyGame/TestDummyPlayer/TestDummyPlayer.tscn")
var index : int = 0

func _ready():
    Network.game_manager = self

func _on_single_player_pressed():
    Network.host_server()
    menu.hide()

func _on_host_pressed():
    Network.host_server()
    menu.hide()


func _on_join_pressed():
    Network.join_server()
    menu.hide()

func _on_exit_pressed():
    get_tree().quit()


func _on_line_edit_text_changed(ip):
    Network.ip_address = ip


func create_player(identifier : int) -> void:
    if not multiplayer.is_server(): return
    var instance = TestDummyPlayer.instantiate()
    instance.name = str(identifier)
    add_child(instance)
    instance.global_position = spawn_points.global_position
    

玩家场景代码

extends CharacterBody3D

@export var synchronizer : MultiplayerSynchronizer
@export var camera : Camera3D
@export var horizontal_n : Node3D
@export var vertical_n : Node3D
@export var sensitivity : float = 0.001
var spawn_point : Vector3

var mouse_motion : Vector2 = Vector2.ZERO

const SPEED = 7.0
const JUMP_VELOCITY = 4.5

# Get the gravity from the project settings to be synced with RigidBody nodes.
var gravity = ProjectSettings.get_setting("physics/3d/default_gravity")

func _ready():
    if not is_multiplayer_authority(): return
    camera.current = true
    Input.mouse_mode = Input.MOUSE_MODE_CAPTURED

func _enter_tree():
    set_multiplayer_authority(str(name).to_int())
    synchronizer.set_multiplayer_authority(str(name).to_int())

func _unhandled_input(event):
    if not is_multiplayer_authority(): return
    if event is InputEventMouseMotion && Input.mouse_mode == Input.MOUSE_MODE_CAPTURED:
        mouse_motion = -event.relative * sensitivity
        rotate_y(mouse_motion.x)
        horizontal_n.rotate_x(mouse_motion.y)
        horizontal_n.rotation.x = clamp(horizontal_n.rotation.x,-PI / 2, PI / 2)

func _physics_process(delta):
    if not is_multiplayer_authority(): return
    if Input.is_action_just_pressed("ui_cancel"):
        get_tree().quit()
        
    # Add the gravity.
    if not is_on_floor():
        velocity.y -= gravity * delta

    # Handle jump.
    if Input.is_action_just_pressed("Jump") and is_on_floor():
        velocity.y = JUMP_VELOCITY

    # Get the input direction and handle the movement/deceleration.
    # As good practice, you should replace UI actions with custom gameplay actions.
    var input_dir = Input.get_vector("Left", "Right", "Forward", "Backward")
    var direction = (global_transform.basis * Vector3(input_dir.x, 0, input_dir.y)).normalized()
    if direction:
        velocity.x = direction.x * SPEED
        velocity.z = direction.z * SPEED
    else:
        velocity.x = move_toward(velocity.x, 0, SPEED)
        velocity.z = move_toward(velocity.z, 0, SPEED)

    move_and_slide()

问题截图

客户端玩家在Vector3.Zero位置生成
指定的Spawn点位置


解决方案

问题核心是MultiplayerSynchronizer的同步优先级和节点权限设置时机导致服务器设置的位置被场景默认值覆盖。具体修改如下:

1. 调整玩家权限设置与位置同步时机

修改GameZone.gd的create_player函数,在添加节点到树之前先设置权限,并且手动RPC同步初始位置:

func create_player(identifier : int) -> void:
    if not multiplayer.is_server(): return
    var instance = TestDummyPlayer.instantiate()
    instance.name = str(identifier)
    
    # 先设置权限,避免节点进入树时的默认同步冲突
    instance.set_multiplayer_authority(identifier)
    instance.synchronizer.set_multiplayer_authority(identifier)
    
    add_child(instance)
    
    # 设置Spawn位置
    instance.global_position = spawn_points.global_position
    
    # 手动RPC通知所有客户端同步该玩家的初始位置
    instance.rpc("sync_initial_position", instance.global_position)
    
    # 打印Spawn点位置,确认是否为有效坐标(非Zero)
    print("Spawn point position: ", spawn_points.global_position)

2. 修改玩家场景代码

移除_enter_tree中的权限设置,添加同步位置的RPC函数:

extends CharacterBody3D

@export var synchronizer : MultiplayerSynchronizer
@export var camera : Camera3D
@export var horizontal_n : Node3D
@export var vertical_n : Node3D
@export var sensitivity : float = 0.001
var spawn_point : Vector3

var mouse_motion : Vector2 = Vector2.ZERO

const SPEED = 7.0
const JUMP_VELOCITY = 4.5

var gravity = ProjectSettings.get_setting("physics/3d/default_gravity")

func _ready():
    if not is_multiplayer_authority(): return
    camera.current = true
    Input.mouse_mode = Input.MOUSE_MODE_CAPTURED

# 移除原来的权限设置,改由服务器在实例化时处理
func _enter_tree():
    pass

# 新增RPC函数,同步初始位置
@rpc("any_peer")
func sync_initial_position(pos : Vector3):
    global_position = pos

# 其他函数保持不变...

3. 检查MultiplayerSynchronizer配置

确保玩家场景中的MultiplayerSynchronizer组件已添加global_position到同步属性列表,并且禁用"Initial Sync"(可选,避免默认Zero位置覆盖)。

4. 验证Spawn点有效性

确认编辑器中GameZone节点的spawn_points已正确赋值,且其global_position不是Vector3.Zero(通过create_player中的打印信息验证)。


内容的提问来源于stack exchange,提问作者Dragon20C

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 09:37:03