客户端 Logic:状态、规则与系统调度 private

客户端 Logic:状态、规则与系统调度

本篇只讲运行时“游戏应该发生什么”。GameObject、Animator、Renderer、VFX 和 UI 如何表现见 24;菜单、Inspector、导入和生成工具见 25。

返回技术文档目录

1. Logic 在本项目中的含义

Logic 不是某一个文件夹,而是一条责任边界:

  • 保存可序列化、可同步、可复现的状态。
  • 根据输入、配置、地图事件和网络消息推进规则。
  • 产生“创建实体、移动、扣血、切状态、刷怪、结算”等结果。
  • 不把材质、粒子、动画片段或 Inspector 操作当成规则真相。

当前工程的物理目录并未严格分层:

目录内容
Assets/Code/Game/OGame/CoreCore 组件和生命值等 ECS 系统
Assets/Code/Game/OGame/Map地图实体、触发器、Spawner、导航资源事件
Assets/Code/Game/OGame/Network网络上下文、快照和远端上下文
Assets/Code/Game/OLogic/ActorHero、Enemy、Boss、Bullet、Buff、Skill、Move
Assets/Code/Game/World世界和 Gameplay 生命周期
Assets/Code/Game/Temp仍在使用的 Battle/StateSync/Spawn 过渡系统
Assets/Code/Game/OGame/GeneratedJenny 生成物,不手改

OLogic 中也有 Graphic、Effect、UI 等表现代码,不能仅凭目录名判断边界。

2. 四个 Entitas Context

Context保存什么不应该保存什么
CoreHero、Monster、Bullet、GPE 的逻辑数据和事件材质、Animator Clip
MapSpawner、Trigger、Portal、导航描述和地图事件玩家长期数据
Network玩家连接、远端实体和同步状态具体角色 Prefab
GameObjectCore 与 Unity 对象之间的桥接句柄战斗规则的唯一真相
flowchart LR
    Input[输入/AI] --> Rule[Actor / Move / Skill 规则]
    Packet[网络消息] --> Sync[StateSync / BattleDataSync]
    MapData[地图数据] --> MapEntity[Map Context]
    MapEntity --> Spawn[Spawner / Trigger System]
    Spawn --> Core[CoreEntity]
    Rule --> Core
    Sync --> Core
    Core --> Event[组件变化/事件组件]
    Event --> Bridge[GameObjectContext 桥接]

Logic 的输出应尽量先落到 Entity/Actor 状态,再由桥接层驱动表现。

3. Zenject 装配,Entitas 调度

Assets/Code/Game/OGame/GameInstaller.cs 绑定四个 Context、Core/Map/Network/Object 系统、Factory、Optimizer 和 PacketCache。

Zenject 解决“对象从哪里来、依赖是什么”;Entitas System 解决“每帧或组件变化时执行什么”。Installer 不应该承载业务规则,Generated 目录也不应该手改。

var contexts = Contexts.sharedInstance;
Container.BindInstance(contexts.core);
Container.BindInstance(contexts.map);
Container.BindInstance(contexts.network);
Container.BindInstance(contexts.gameObject);

Container.Bind<StateSyncSystem>().AsTransient();
Container.Bind<RollbackSystem>().AsTransient();
Container.Bind<ObjectAssetSystem>().AsTransient();

4. System 生命周期

接口调用时机适合做什么
IInitializeSystemFeature 启动时一次建组、订阅、初始化
IExecuteSystem每个逻辑 Tick持续模拟和推进
ReactiveSystem匹配组件变化时出生、扣血、触发器等事件
ICleanupSystem一帧末尾清理一次性事件、批量发送
ITearDownSystemWorld/Feature 销毁取消订阅、释放缓存

工程大量使用 UniRx 把 Entitas Group 变化转为 Observable。Subscribe 必须绑定 CompositeDisposable 并在 TearDown 释放,否则切地图后旧 World 仍会响应。

5. 主要逻辑链路

5.1 创建实体

地图 Spawner、服务端 SpawnActor 或本地玩法创建请求,最终建立带 EntityID、ObjectType、ActorType 的 CoreEntity。CoreEntity 是资产创建、网络索引和销毁的共同锚点。

创建阶段不能假设 View 已存在。资产加载是异步的,CoreEntity 会先于 GameObject 出现。

5.2 移动

  1. 输入或 AI 选择目标。
  2. MoveController 选择 MoveStrategy。
  3. Strategy 推进逻辑位置、方向和结束条件。
  4. 联网实体发布 SyncMoveData。
  5. 表现端同步 Transform、NavMeshAgent 和 Animator。

NavMesh 与策略见 20 客户端寻路,跨端消息见 22 联机同步。

5.3 生命值

合理顺序是:

Damage rule
  -> HpChanged event
  -> CoreEntity.Hp
  -> Network/Replay
  -> IViewObject.ApplyHpChangedToView

动画事件可以决定受击特效的时机,但不应成为最终 HP 数据源。

5.4 刷怪与地图事件

Map Context 保存 Spawner 和 Trigger。触发器产生 SpawnEvent,SpawnSystem 创建 CoreEntity,ObjectAssetSystem 再创建表现对象。制作配置见 13/15,Battle 权威见服务端分区。

6. 本地、远端和私有实体

类型Logic 来源是否发 Battle表现特点
本地 Hero本地输入 + Battle 结果是立即响应,失败回滚
远端 HeroBattle 广播否PacketCache 后重放
Enemy/BossAI 或主机/Battle 消息取决于模式需要明确控制权
PrivateEntity本地临时逻辑否不进入共享战斗状态
Dummy HeroEditor 调试专用路径只在 UNITY_EDITOR 使用

新增实体类型时必须明确:谁创建 ID、谁控制、是否同步、何时销毁。

7. Logic 与 Network 的边界

flowchart LR
    Packet[Protobuf Packet] --> Decode[解压/校验/索引]
    Decode --> LogicEvent[逻辑消息/组件变化]
    LogicEvent --> Rule[状态推进]
    Rule --> SyncEvent[待发送意图]
    SyncEvent --> Encode[量化/压缩]
    Encode --> Battle[BattleClient]

网络包不是渲染命令。例如 SC_MoveBegin 先进入 PacketCache,再发布 ApplyMoveData,最后由移动控制器驱动远端表现。

8. Logic 与 Rendering 的桥接规则

  • Logic 修改 Entity/Actor 状态或发布领域事件。
  • Rendering 订阅组件变化或实现 IViewObject。
  • 立即修正通过接口调用,不依赖具体 Prefab 子节点。
  • View 可以读取展示状态,但 Transform/Animator 不是唯一逻辑真相。

当前 Actor 类仍同时持有 Transform、NavMeshAgent 和战斗状态,这是历史混合。新代码应减少规则层直接 transform.Find 和硬编码资源路径。

9. 新增玩法逻辑的落点

  1. 可同步状态:优先放 Core/Actor 数据。
  2. 组件变化触发:使用 ReactiveSystem。
  3. 持续逐帧推进:使用 ExecuteSystem 或明确控制器 Tick。
  4. 纯视觉反馈:放 Rendering。
  5. 只在制作期使用:放 Editor 扩展。
  6. 需要跨端:先定义权威端、协议、失败结果和兼容策略。

10. 常见错误

现象Logic 优先检查
切地图后事件执行两次旧 Feature/UniRx 未 TearDown
CoreEntity 存在但无角色转到 ObjectAssetSystem 和资源层
动画结束但状态没切动画被当成规则真相,或事件未回写
远端 HP 对但飘字错Logic 正确,View 参数消费错误
本地移动但远端不动SyncMoveData 后转查 22
怪物触发两次Map 事件清理或重复订阅

11. 代码审查清单

  • 业务真相是否在 Entity/Actor,而非 UI 文本、Animator 或 Transform。
  • 一次性事件是否在 Cleanup 清理。
  • Subscribe 是否在 TearDown Dispose。
  • 异步创建返回时是否重新检查 Entity 和 ID。
  • 本地、远端、Private、Dummy 分支是否明确。
  • 网络失败是否有恢复状态。
  • 新组件是否重新运行 Jenny,Generated 是否未手改。

12. 关联阅读


下一篇:客户端 Rendering