总结一下使用MultiPlayerSpawner/Synchronizer
(简称Spawner/Synchronizer,下同)实现多人游戏过程的一些踩坑/经验,实现过程参考了官方的:高级多人游戏,multiplayer-in-godot-4-0-scene-replication,强烈建议提前阅读一下。
多人游戏通常涉及到游戏服务器进程与多个游戏客户端进程,因此初学者(比如我)最好提前思考一下游戏中每个实体对象的相对于 服务器与客户端 的关系。以多人游戏里面的玩家为例:
multiplayer.get_unique_id()
和set_multiplayer_authority
可以用来区分当前的执行进程。用来将授权端(默认是服务器)创建的对象自动复制到其它的客户端。使用时,通过add_spawnable_scene()
指定要自动复制的对象;通过检查器中的Spawn Path
指定对象的生成根路径。
例如,例如使用add_spawnable_scenen(player)
设置要复制的对象Player,使用game节点作为Spawn Path
,实现 服务端创建player对象到场景中时,自动在客户端中的game节点下创建player对象并添加到场景中 的效果。
用来同步属性到每个客户端上,可以在下方的复制面板中选择需要同步的属性以及同步的频率。例如可以通过选择Player对象的position/rotation等属性同步位置/旋转到服务器与其它客户端,也可以同步导出的变量。
同步是1对多的方式,通过set_multiplayer_authority
设置同步的授权方,然后需要同步到属性就会从 授权方 同步到其它所有的peer。
下面以一个简单的场景为例介绍具体如何使用Spawner/Synchronizer实现多人游戏:
场景描述:
平台 + 几个障碍物 + 摄像机 + 光照 + MultiPlayerSpawner
方块 + 标签 + 摄像机 + InputSync(MultiPlayerSynchronizer) + PosSync(MultiPlayerSynchronizer)
以下从进程的角度描述服务器或者一个客户端。server, client_1, client_2, ...
分别代表服务器、玩家1的客户端进程、玩家2的客户端进程等:
multiplayer.get_unique_id()
区分。InputSync
捕获输入并同步到其它peer,根据捕获的输入处理运动
InputSync.set_multiplayer_authority(my_peer_id)
区分执行实体
Player.InputSync
,只有它的Player身份是当前玩家时才需要处理输入;如果其Player的身份是其它玩家,那么应该跳过对当前玩家输入的捕获,因为这个InputSync
的属性会从他所隶属的客户端中同步过来。 所以,需要在Player对象初始化时,通过set_multiplayer_authority
将Player的authority
设置为所属客户端的peerid,从而在不同的客户端中区分身份。PosSync
将服务端的执行同步到客户端
resource_local_to_scene
保证mesh
是新创建,而不是引用同一个实例
text_mesh
修改其中一个实例的text
值影响所有其他实例setter
函数更新multiplayer_authority
MultiPlayerSpawner
复制过来的,仅在peer_id
变量设置之后,客户端才能够设置multiplayer_authority
MultiPlayerSpawner
复制实例的时候,变量的setter发生在_enter_tree
和_ready
之后_process
保证是在变量setter执行完后的,也可以在其中进行一次multiplayer_authority
能够节省许多对象/属性复制的逻辑,但是文档较为匮乏,导致上手难度略大。