@rpc 远程过程调用
2026/4/14大约 5 分钟
最后同步日期:2026-04-15 | Godot 官方原文 — @rpc 远程过程调用
@rpc 远程过程调用
定义
@rpc 是 Godot 中的一个装饰器(也叫注解/特性),用来标记一个函数为"远程过程调用"——也就是说,这个函数不仅能在本地执行,还能被网络上的其他玩家调用。就像打电话让远方的朋友帮你做一件事一样。
打个比方:你和好友各有一台对讲机。你在你的对讲机上按了一个按钮(调用函数),好友的对讲机上对应的功能就自动执行了。@rpc 就是告诉 Godot:"这个函数可以通过对讲机(网络)远程触发。"
在实际游戏开发中,RPC 用于多人游戏的同步:比如玩家 A 移动了,通过 RPC 告诉玩家 B 的游戏也要更新玩家 A 的位置;玩家 A 发射子弹,通过 RPC 让所有其他玩家也看到这颗子弹。
函数签名
C#
// C# 中使用 [Rpc] 特性
[Rpc(MultiplayerApi.RpcMode.AnyPeer, CallLocal = false, TransferMode = MultiplayerPeer.TransferModeEnum.Reliable)]
public void MyRpcFunction(string message)
{
// 函数体
}GDScript
# GDScript 中使用 @rpc 注解
@rpc("any_peer", "call_remote", "reliable")
func my_rpc_function(message: String) -> void:
# 函数体
pass参数说明
| 参数 | 可选值 | 说明 |
|---|---|---|
| 调用权限 | ||
"any_peer" / AnyPeer | 任何客户端都可以调用 | 适合聊天、投票等 |
"authority" / Authority | 只有服务器(权威端)可以调用 | 适合权威服务器同步 |
| 调用范围 | ||
"call_remote" / CallRemote | 只在远端执行,不在本地执行 | 默认值 |
"call_local" / CallLocal | 本地和远端都执行 | 适合服务器也需要执行逻辑的情况 |
| 传输模式 | ||
"reliable" / Reliable | 可靠传输,保证到达但可能延迟 | 适合重要数据 |
"unreliable" / Unreliable | 不可靠但快速 | 适合位置同步等实时数据 |
"unreliable_ordered" / UnreliableOrdered | 不可靠但保持顺序 | 折中方案 |
返回值
RPC 函数本身的返回值不会被传回调用者。RPC 是"单向"的——你调用远端函数,但无法获取返回值。如果需要双向通信,对方需要用另一个 RPC 回调你。
代码示例
基础用法:简单的 RPC 聊天
C#
using Godot;
public partial class ChatManager : Node
{
[Rpc(MultiplayerApi.RpcMode.AnyPeer, CallLocal = true)]
public void SendChatMessage(string playerName, string message)
{
GD.Print($"[聊天] {playerName}: {message}");
// 运行结果: 所有连接的玩家都会看到聊天消息
}
public void OnChatInputSubmitted(string text)
{
// 调用 RPC,让所有玩家执行 SendChatMessage
Rpc(nameof(SendChatMessage), "玩家1", text);
// 运行结果: 所有客户端打印聊天消息
}
}GDScript
extends Node
@rpc("any_peer", "call_local")
func send_chat_message(player_name: String, message: String):
print("[聊天] %s: %s" % [player_name, message])
# 运行结果: 所有连接的玩家都会看到聊天消息
func _on_chat_input_submitted(text):
# 调用 RPC,让所有玩家执行 send_chat_message
rpc("send_chat_message", "玩家1", text)
# 运行结果: 所有客户端打印聊天消息实际场景:服务器同步玩家位置
C#
using Godot;
public partial class Player : CharacterBody3D
{
[Export] public float ExMoveSpeed = 5.0f;
public override void _PhysicsProcess(double delta)
{
if (!IsMultiplayerAuthority()) return;
// 只有自己的客户端控制移动
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();
// 通过 RPC 同步位置给其他玩家
Rpc(nameof(SyncPosition), GlobalPosition);
}
[Rpc(MultiplayerApi.RpcMode.AnyPeer, TransferMode = MultiplayerPeer.TransferModeEnum.Unreliable)]
private void SyncPosition(Vector3 position)
{
GlobalPosition = position;
// 运行结果: 其他客户端看到此玩家平滑移动
}
}GDScript
extends CharacterBody3D
@export var move_speed: float = 5.0
func _physics_process(delta):
if not is_multiplayer_authority():
return
# 只有自己的客户端控制移动
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()
# 通过 RPC 同步位置给其他玩家
rpc("sync_position", global_position)
@rpc("any_peer", "unreliable")
func sync_position(position: Vector3):
global_position = position
# 运行结果: 其他客户端看到此玩家平滑移动进阶用法:权威服务器处理伤害计算
C#
using Godot;
public partial class CombatManager : Node
{
// 客户端调用此 RPC 请求服务器处理伤害
[Rpc(MultiplayerApi.RpcMode.AnyPeer)]
public void RequestDamage(long targetId, float damage)
{
// 只有服务器执行伤害计算逻辑(权威服务器模式)
if (!Multiplayer.IsServer()) return;
long attackerId = Multiplayer.GetRemoteSenderId();
GD.Print($"玩家 {attackerId} 对玩家 {targetId} 造成 {damage} 伤害");
// 服务器验证后广播伤害结果给所有客户端
Rpc(nameof(ApplyDamage), targetId, damage);
// 运行结果: 服务器验证并广播伤害,所有客户端执行 ApplyDamage
}
// 服务器调用此 RPC 广播伤害结果
[Rpc(MultiplayerApi.RpcMode.Authority, CallLocal = true)]
public void ApplyDamage(long targetId, float damage)
{
GD.Print($"玩家 {targetId} 受到 {damage} 伤害");
// 运行结果: 所有客户端(包括服务器)执行受伤逻辑
}
}GDScript
extends Node
# 客户端调用此 RPC 请求服务器处理伤害
@rpc("any_peer")
func request_damage(target_id: int, damage: float):
# 只有服务器执行伤害计算逻辑(权威服务器模式)
if not multiplayer.is_server():
return
var attacker_id = multiplayer.get_remote_sender_id()
print("玩家 %d 对玩家 %d 造成 %.1f 伤害" % [attacker_id, target_id, damage])
# 服务器验证后广播伤害结果给所有客户端
rpc("apply_damage", target_id, damage)
# 运行结果: 服务器验证并广播伤害
# 服务器调用此 RPC 广播伤害结果
@rpc("authority", "call_local")
func apply_damage(target_id: int, damage: float):
print("玩家 %d 受到 %.1f 伤害" % [target_id, damage])
# 运行结果: 所有客户端(包括服务器)执行受伤逻辑注意事项
- 函数名必须在所有客户端一致:RPC 通过函数名来调用远端函数,所以所有客户端上该函数的名称必须完全相同。
- 参数类型有限制:RPC 只能传递可序列化的类型(基本类型、Vector2/3、颜色等)。不能传递自定义对象或节点引用。
MultiplayerAuthority很重要:每个节点都有一个"多人权威"属性,决定谁有权控制这个节点。通常玩家的角色由该玩家自己控制(设置 authority = 该玩家的 ID)。- C# 中使用
Rpc(nameof(...))而不是直接调用:在 C# 中触发 RPC 要用Rpc("MethodName", args)而不是直接调用方法。GDScript 中用rpc("method_name", args)。 - 安全考虑:
any_peer模式下任何客户端都能调用,存在作弊风险。关键逻辑(如伤害计算)应该使用authority模式在服务器端执行。
