帧同步 Sync
联机方案的 ECS 底层:帧同步(P1 已落地)与状态同步(P3 路线,共用同一底座)。框架负责传输抽象、输入汇流、tick 推进、确定性纪律工具;游戏层只做两件事:继承基类写逻辑、照常用 SystemAPI 取数。
命名空间 HYC.Framework.Sync,Host / Client 两端同码。
实现状态(如实)
| 阶段 | 状态 | 内容 |
|---|---|---|
| P1 | ✅ 已落地 | 共享底座 + 帧同步(Driver / InputSystem / LogicSystem / Loopback 双端) |
| P2 | ❌ 未做 | 帧同步快照([SyncSnapshot] 全量二进制)、断线快照重连追赶、观战中途加入 |
| P3 | ❌ 未做(路线) | 状态同步:脏检广播 / 应用+实体映射 / B9 前插值 / spawn·despawn 原型 ID / SyncIdService 发号 |
| P4 | ❌ 未做 | 确定性校验和(周期 hash 比对)+ Samples 示例 |
◈
P1 断链后世界默认永久等齐(
MaxStallFrames=0);「重链快照追赶」是 P2 范围。核心类
| 类 | 职责 |
|---|---|
ITransport | 传输抽象:Send / event MessageReceived / Pump();框架不绑定任何网络库 |
LoopbackTransport | 回环传输,单机双端联调与确定性回归(CreatePair / CreateSolo) |
BlitIO | blittable 类型顺序 memcpy,零分配零反射 |
SyncRandom | 确定性随机:splitmix64 + 纯整数位运算 |
SyncBootstrap | 装配入口:Install(World, ITransport) |
LockstepDriver | 齐帧推进(写 SyncTickSingleton) |
LockstepInputSystem<T> | 输入汇流基类(SampleLocal / Submit) |
LockstepLogicSystem | 逻辑系统基类(Step(tick) / TryGetInput<T>) |
上层接入范式
// ① 输入结构:blittable(unmanaged IComponentData)
public struct MyInput : IComponentData
{
public float2 Move;
public uint Buttons; // bit0=跳 bit1=攻击 …
}
// ② 输入系统:两组特性必须自带(ILPP 不继承基类特性,见坑 1)
[UpdateInGroup(typeof(UpdateGroup_A0))]
[UpdateBefore(typeof(LockstepDriver))]
public partial class MyInputSystem : LockstepInputSystem<MyInput>
{
protected override void SampleLocal(int localPlayerId, ref MyInput input)
{
input.Move = new float2(Input.GetAxisRaw("Horizontal"), Input.GetAxisRaw("Vertical"));
input.Buttons = (Input.GetKey(KeyCode.Space) ? 1u : 0u);
}
}
// ③ 逻辑系统:组特性自带;OnUpdate 被封闭,只写 Step
[UpdateInGroup(typeof(UpdateGroup_A3))]
public partial class MySimSystem : LockstepLogicSystem
{
protected override void Step(int tick)
{
float dt = 1f / 60f; // 固定步长,禁读 Time
var rng = SyncRandom.ForTick(tick, 20261019); // 确定性随机
if (TryGetInput<MyInput>(0, out var local)) { /* 本地玩家 */ }
if (TryGetInput<MyInput>(1, out var remote)) { /* 对端 */ }
foreach (var (hp, vel) in SystemAPI.Query<RefRW<Health>, RefRW<Velocity>>())
{
// 与单机完全同形的确定性逻辑
}
}
}
// ④ bootstrap 装配(FrameworkBootstrapBase 子类 / installer 内)
protected override void RegisterSystems(World world)
{
HYC.Framework.Sync.SyncBootstrap.Install(world, new MyEnetTransport(...));
}
坑
- 组特性必须游戏侧具体类自带:Entities ILPP 用 Cecil 直读类型特性、不继承泛型基类特性。缺失症状:输入系统脱离 A0(本地输入总在 Driver 检查后才提交,推进降半速)、逻辑系统脱离固定步长。
SyncBootstrap.Install会在装配期对缺失类 LogError。系统类还必须是partial。 - 一个玩法一个输入系统:多个
LockstepInputSystem<T>会各自解码同一份远端字节;多路输入合并进同一个TInput结构。 playerId < 32(齐帧用位掩码)。P1 传输模型 = 一条链路两个端点。MaxStallFrames默认 0 = 永久等齐。- 追帧期间同渲染帧复用输入快照:这是正确行为,不要试图在
Step内重新采样。 - 边界铁律:同步层 = 传输 / 帧同步 / 状态同步,与输入采集严格分离。