MultiplayerSynchronizer
2026/4/14大约 5 分钟
最后同步日期:2026-04-15 | Godot 官方原文 — MultiplayerSynchronizer
MultiplayerSynchronizer
定义
MultiplayerSynchronizer 是 Godot 提供的自动属性同步节点——它能帮你把一个节点上的某些属性值自动同步给网络上的其他玩家。你不需要手动写 RPC 来同步每个属性,只要告诉它"同步哪些属性"就行了。
打个比方:想象你和朋友各有一面镜子。你在自己这边摆弄镜子(修改属性),朋友的镜子里会自动显示同样的画面。MultiplayerSynchronizer 就是这面"魔镜",你设置好要同步的属性,剩下的它自动搞定。
在实际游戏开发中,MultiplayerSynchronizer 通常用来同步角色的位置、旋转、动画状态、血量等数据。比手写 RPC 方便很多,特别是属性多的时候。
常用方法
| 方法 | 返回值 | 说明 |
|---|---|---|
SetVisibilityParent(path) | void | 设置可见性父节点,控制哪些玩家能看到这个同步器 |
UpdateVisibility() | void | 手动更新可见性 |
GetVisibilityMode() | VisibilityMode | 获取可见性模式 |
常用属性
| 属性 | 类型 | 说明 |
|---|---|---|
RootPath | NodePath | 同步的根节点路径 |
ReplicationConfig | 资源 | 配置哪些属性需要同步(在编辑器中设置) |
VisibilityUpdateMode | 枚举 | 可见性更新模式(总是/手动/从不) |
DeltaInterval | float | 增量同步间隔(秒),0 表示每帧同步 |
常用信号
| 信号 | 说明 |
|---|---|
delta_synchronized | 增量同步完成时触发 |
synchronized | 完整同步完成时触发 |
visibility_changed(who) | 可见性变化时触发 |
代码示例
基础用法:同步玩家位置和旋转
C#
using Godot;
public partial class PlayerSync : Node3D
{
private MultiplayerSynchronizer _synchronizer;
public override void _Ready()
{
_synchronizer = GetNode<MultiplayerSynchronizer>("MultiplayerSynchronizer");
_synchronizer.RootPath = _synchronizer.GetPathTo(this);
// 运行结果: 同步器开始自动同步此节点的属性
GD.Print("玩家同步器已配置");
}
}GDScript
extends Node3D
@onready var _synchronizer: MultiplayerSynchronizer = $MultiplayerSynchronizer
func _ready():
_synchronizer.root_path = _synchronizer.get_path_to(self)
# 运行结果: 同步器开始自动同步此节点的属性
print("玩家同步器已配置")实际场景:完整的玩家同步方案
C#
using Godot;
public partial class NetworkPlayer : CharacterBody3D
{
[Export] public float ExMoveSpeed = 5.0f;
[Export] public float ExJumpForce = 4.5f;
private MultiplayerSynchronizer _synchronizer;
private bool _isLocalPlayer = false;
// 这些属性通过 MultiplayerSynchronizer 自动同步
// 在编辑器的 Replication Config 中勾选这些属性
private Vector3 _syncPosition;
private Vector3 _syncRotation;
private float _syncHealth = 100.0f;
public override void _Ready()
{
_synchronizer = GetNode<MultiplayerSynchronizer>("MultiplayerSynchronizer");
// 设置根路径
_synchronizer.RootPath = _synchronizer.GetPathTo(this);
// 设置多人权威(谁控制这个角色)
if (IsMultiplayerAuthority())
{
_isLocalPlayer = true;
GD.Print("这是本地玩家,控制权在此");
}
}
public override void _PhysicsProcess(double delta)
{
if (_isLocalPlayer)
{
ProcessLocalInput();
}
else
{
// 非本地玩家:使用同步过来的位置进行插值
GlobalPosition = GlobalPosition.Lerp(_syncPosition, 0.2f);
}
}
private void ProcessLocalInput()
{
var input = Vector3.Zero;
if (Input.IsKeyPressed(Key.W)) input.Z -= 1;
if (Input.IsKeyPressed(Key.S)) input.Z += 1;
if (Input.IsKeyPressed(Key.A)) input.X -= 1;
if (Input.IsKeyPressed(Key.D)) input.X += 1;
Velocity = input.Normalized() * ExMoveSpeed;
MoveAndSlide();
// 位置会通过 MultiplayerSynchronizer 自动同步
// 运行结果: 本地玩家控制移动,位置自动同步给其他客户端
}
}GDScript
extends CharacterBody3D
@export var move_speed: float = 5.0
@export var jump_force: float = 4.5
@onready var _synchronizer: MultiplayerSynchronizer = $MultiplayerSynchronizer
var _is_local_player: bool = false
# 这些属性通过 MultiplayerSynchronizer 自动同步
var _sync_position: Vector3
var _sync_rotation: Vector3
var _sync_health: float = 100.0
func _ready():
# 设置根路径
_synchronizer.root_path = _synchronizer.get_path_to(self)
# 设置多人权威
if is_multiplayer_authority():
_is_local_player = true
print("这是本地玩家,控制权在此")
func _physics_process(delta):
if _is_local_player:
process_local_input()
else:
# 非本地玩家:使用同步过来的位置进行插值
global_position = global_position.lerp(_sync_position, 0.2)
func process_local_input():
var input = Vector3.ZERO
if Input.is_key_pressed(KEY_W): input.z -= 1
if Input.is_key_pressed(KEY_S): input.z += 1
if Input.is_key_pressed(KEY_A): input.x -= 1
if Input.is_key_pressed(KEY_D): input.x += 1
velocity = input.normalized() * move_speed
move_and_slide()
# 位置会通过 MultiplayerSynchronizer 自动同步
# 运行结果: 本地玩家控制移动,位置自动同步给其他客户端进阶用法:带可见性控制的同步
C#
using Godot;
public partial class VisibilitySyncManager : Node3D
{
private MultiplayerSynchronizer _synchronizer;
public override void _Ready()
{
_synchronizer = GetNode<MultiplayerSynchronizer>("MultiplayerSynchronizer");
// 只同步给可见范围内的玩家
_synchronizer.VisibilityUpdateMode = MultiplayerSynchronizer.VisibilityUpdateModeEnum.Always;
// 运行结果: 自动管理哪些玩家能看到此对象的同步数据
}
public override void _Process(double delta)
{
// 根据距离控制可见性
foreach (var peerId in Multiplayer.GetPeers())
{
var peerNode = GetNode<Node3D>($"/root/Players/{peerId}");
if (peerNode != null)
{
float distance = GlobalPosition.DistanceTo(peerNode.GlobalPosition);
// 距离超过 50 米时不同步
_synchronizer.SetVisibilityFor(peerId, distance < 50.0f);
}
}
// 运行结果: 超过 50 米的玩家不会收到此对象的更新
}
}GDScript
extends Node3D
@onready var _synchronizer: MultiplayerSynchronizer = $MultiplayerSynchronizer
func _ready():
# 只同步给可见范围内的玩家
_synchronizer.visibility_update_mode = MultiplayerSynchronizer.VISIBILITY_PROCESS_ALWAYS
# 运行结果: 自动管理哪些玩家能看到此对象的同步数据
func _process(delta):
# 根据距离控制可见性
for peer_id in multiplayer.get_peers():
var peer_node = get_node_or_null("/root/Players/%d" % peer_id) as Node3D
if peer_node:
var distance = global_position.distance_to(peer_node.global_position)
# 距离超过 50 米时不同步
_synchronizer.set_visibility_for(peer_id, distance < 50.0)
# 运行结果: 超过 50 米的玩家不会收到此对象的更新注意事项
- 在编辑器中配置同步属性:大多数情况下,你在编辑器中选中 MultiplayerSynchronizer 节点,然后在 "Replication" 配置面板中勾选需要同步的属性。代码中也可以动态配置。
- 权威端概念:每个 MultiplayerSynchronizer 需要设置"谁有权威"(谁说了算)。通常由拥有该角色的玩家控制(
SetMultiplayerAuthority(playerId))。 - 性能考虑:不要同步太多属性,每个同步的属性都会占用网络带宽。位置同步用不可靠模式(Unreliable),重要状态用可靠模式(Reliable)。
- 插值平滑:网络同步的数据有延迟,直接使用同步位置会导致角色"瞬移"。建议在接收端使用插值(
Lerp)来平滑过渡。 - 与 Spawner 配合:通常
MultiplayerSpawner负责在所有客户端生成角色实例,MultiplayerSynchronizer负责同步角色属性。两者配合使用。
