Godot游戏练习01-第4.5节-多人控制与玩家位置同步问题修复

0 阅读2分钟

昨天碰到了多人控制与位置同步问题, 刚开始提示不能在ready中设置authority, 于是我按照报错提示调整为enter_tree中动态创建输入组件并设置authority, 但又碰到Player同步生成的问题

今天调试了好一会, 一直各种问题, 核对源码没发现任何逻辑上的问题

于是我从头实现了整个项目, 结构, 源码都没怎么动, 它好了...

甚至也没提示在ready中不能设置authority的报错了, 这让人非常不爽, 我不知道问题是怎么好的, 也不知道是怎么出现的, 意味着我没有弄清昨天的情形, 我感到非常困扰

昨天有问题就没提交git, 现在觉得出问题的代码也要加入源码管理, 方便复现当时的场景

如果有细心的朋友从我昨天的文章里面发现蛛丝马迹的错误, 希望立即指出来, 感激不尽!

目前仅涉及到4个简单的场景和脚本

  1. main_menu (默认场景)

    核心逻辑: 点击Host后创建server并跳转到main场景; 点击Join后创建client, 并在连接server成功后跳转main场景

    class_name MainMenu
    extends Control
    
    const PORT : int = 34560
    const MAIN = preload("uid://yubvfldj7w73")
    
    @onready var host_button: Button = %HostButton
    @onready var join_button: Button = %JoinButton
    
    
    func _ready() -> void:
    	host_button.pressed.connect(_on_host_pressed)
    	join_button.pressed.connect(_on_join_pressed)
    	multiplayer.connected_to_server.connect(_on_connected_to_server)
    
    
    func _on_host_pressed() -> void:
    	var server_peer := ENetMultiplayerPeer.new()
    	server_peer.create_server(PORT)
    	multiplayer.multiplayer_peer = server_peer
    	get_tree().change_scene_to_packed(MAIN)
    
    
    func _on_join_pressed() -> void:
    	var client_peer := ENetMultiplayerPeer.new()
    	client_peer.create_client("127.0.0.1", PORT)
    	multiplayer.multiplayer_peer = client_peer
    
    
    func _on_connected_to_server() -> void:
    	get_tree().change_scene_to_packed(MAIN)
    
    
  2. main (游戏场景)

    在ready中定义spawn_function, 并调用rpc通知server同步生成player

    extends Node
    
    const PLAYER = preload("uid://dgstmloeo60yy")
    
    @onready var level: Node2D = $Level
    @onready var player_spawner: MultiplayerSpawner = %PlayerSpawner
    
    func _ready() -> void:
    	player_spawner.spawn_function = func(data):
    		print("[peer %s] Spawn player: %s" % [multiplayer.get_unique_id(), data.peer_id])
    		var player = PLAYER.instantiate()
    		player.name = "Player%s" % [data.peer_id]
    		player.input_peer_id = data.peer_id
    		return player
    	_create_player.rpc_id(1)
    
    
    @rpc("any_peer", "call_local", "reliable")
    func _create_player() -> void:
    	var sender_id := multiplayer.get_remote_sender_id()
    	player_spawner.spawn({ "peer_id": sender_id })
    
  3. player

    并在ready中设置input组件的authority, 区分控制权限

    extends CharacterBody2D
    
    var input_peer_id : int
    var move_vector: Vector2 = Vector2.ZERO
    var move_speed: float = 100.0
    
    @onready var player_input_multiplayer_synchronizer_component: PlayerInputMultiplayerSynchronizerComponent = $PlayerInputMultiplayerSynchronizerComponent
    
    func _ready() -> void:
    	print("[peer %s] Set player(%s) input authroity %s" % [multiplayer.get_unique_id(), name, input_peer_id])
    	player_input_multiplayer_synchronizer_component.set_multiplayer_authority(input_peer_id)
    
    
    func _process(_delta: float) -> void:
    	var input := player_input_multiplayer_synchronizer_component.move_vector
    	velocity = input * move_speed
    	move_and_slide()
    
  4. input_component (player_input_multiplayer_synchronizer_component)

    获取输入, 在属性同步列表中同步move_vector

    class_name PlayerInputMultiplayerSynchronizerComponent
    extends MultiplayerSynchronizer
    
    var move_vector : Vector2 = Vector2.ZERO
    
    func _process(_delta: float) -> void:
    	if is_multiplayer_authority():
    		move_vector = Input.get_vector("move_left", "move_right", "move_up", "move_down")