📇 速览卡片(3 分钟复习)
| 概念 |
一句话 |
| ENet |
基于 UDP 的可靠传输库,Godot 4 内置封装 |
| ENetMultiplayerPeer |
端对端(P2P)联机的核心实例,替代底层 Socket 管理 |
| call_remote |
RPC 只发远端,本地不执行 |
| call_local |
RPC 本地与远端同时执行 |
| authority |
仅该节点的网络权限所有者(通常是房主/服务器)可调用 |
| any_peer |
任意对等体均可调用 |
| reliable |
保证送达,适合状态变更、扣血 |
| unreliable |
不保证送达,适合高频位置同步 |
| unreliable_ordered |
不保证送达,但保证顺序,适合连续动画帧 |
| 通道 (Channel) |
0~9 共 10 条,类似端口分流,可分别设带宽上限 |
📝 详细笔记
1. Host 与 ENetMultiplayerPeer
Godot 4 的 P2P 联机基于 ENet 库,底层走 UDP 协议。开发者不需要手动处理 Socket、NAT Punch-through 或数据包序列化,直接通过 ENetMultiplayerPeer 一个高层实例完成端对端管理。
1 2 3 4 5 6 7
| var peer = ENetMultiplayerPeer.new() # 创建主机(监听) peer.create_server(port, max_clients) # 或加入房间(客户端) peer.create_client(address, port)
multiplayer.multiplayer_peer = peer
|
ENetMultiplayerPeer.new() 即为一个完整的端对端实例,封装了连接管理、心跳、丢包重传等细节。
2. @rpc 注解四参数
Godot 4 使用 @rpc(...) 标记远程调用函数,四个位置参数决定了网络行为的全部策略:
1 2 3
| @rpc(A, B, C, D) func my_remote_func(): pass
|
A —— 执行范围(本地 vs 远端)
| 取值 |
语义 |
call_remote |
仅远端执行,本地不执行。适合”通知别人” |
call_local |
本地 + 远端全部执行。适合确定性逻辑,确保所有端状态一致 |
B —— 调用权限(谁能发起)
| 取值 |
语义 |
authority |
仅该节点的网络权限所有者(默认是创建它的端,通常是 Host)可以调用。防作弊首选 |
any_peer |
任意对等体均可调用。适合玩家输入、自由交互 |
C —— 传输可靠性
| 取值 |
语义 |
适用场景 |
reliable |
保证送达,丢包会重传,可能延迟 |
状态变更、扣血、拾取道具、游戏开始/结束 |
unreliable |
不保证送达,不重传,最低延迟 |
高频位置同步、旋转、速度 |
unreliable_ordered |
不保证送达,但保证顺序 |
连续动画帧、弹道轨迹,允许丢帧但不允许乱序 |
D —— 通道编号 (Channel)
- 取值范围:0 ~ 9,共 10 条独立通道
- 作用类似端口分流:不同通道的数据互不阻塞,可分别设置带宽上限
- 典型用法:
- 通道 0:
reliable 状态同步(低带宽、高优先级)
- 通道 1:
unreliable 位置同步(高频、可丢包)
- 通道 2:聊天/语音(独立带宽限制)
💻 代码示例
主机创建与 RPC 调用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| extends Node
@rpc("call_remote", "authority", "reliable", 0) func start_game(): # 只有房主能调用,通知所有客户端游戏开始 get_tree().change_scene_to_file("res://Game.tscn")
@rpc("call_local", "any_peer", "unreliable", 1) func update_position(pos: Vector2): # 任意玩家都能发,本地也立即执行,高频位置同步 $Character.position = pos
func _ready(): var peer = ENetMultiplayerPeer.new() peer.create_server(9999, 4) multiplayer.multiplayer_peer = peer multiplayer.peer_connected.connect(_on_peer_connected)
func _on_peer_connected(id: int): print("玩家 ", id, " 已连接") # 房主调用,所有客户端(包括本地)执行 start_game start_game.rpc()
|
客户端加入
1 2 3 4 5 6 7 8
| func join_game(ip: String, port: int): var peer = ENetMultiplayerPeer.new() peer.create_client(ip, port) multiplayer.multiplayer_peer = peer multiplayer.connected_to_server.connect(_on_connected)
func _on_connected(): print("已连接到服务器,本机 ID: ", multiplayer.get_unique_id())
|