◆ 持续开发中 · 当前为非稳定版本,如需抢先体验请通过 GitHub Issues 联系作者

示例 · 行为树 BT

一棵有真实行为数据的敌人 AI 树(不是占位 NoOp):Root → Selector[ Sequence( IsTargetInRange → Attack ), Patrol ],三个叶子是 GameCustom 节点,进 Play 后控制台真的逐帧打印执行日志。

文件:HYCSimpleBTDemoSystem.cs(运行时)/ HYCSimpleEnemyBTNodes.cs(自定义节点)/ Editor/HYCSimpleBTTreeBuilder.cs(编辑器建树+导出)

心智模型

  • 编辑器:在 BTTreeAsset 里画节点 / 连线,或用代码 EnsureRootNode() 程序化建树。
  • 导出:BTBlobExporter.ExportAll()(菜单 HYC Framework/BT/Export Blob (For Build))→ Assets/StreamingAssets/BTBlob/*.blob + manifest.txt。
  • 运行期:BTBlobLoader.LoadAll() 把 blob 读回并 BTManager.Register(treeId, blob)。
  • 驱动:给实体挂 RunningBT { TreeId },框架自带 BTInterpreterSystem : ISystem 每帧 Tick,结果写回 BTLastResult。
  • 游戏层自定义行为:GameCustom 节点用 LongParams[0] 当子类型 subType,BTNodeRuntimeRegistry 运行期反射扫描程序集、把 BTCustomNode 子类按 (TreeKind, subType) 自动注册并分发执行。

示例树结构

Root
 └─ Selector(EnemyAI)            优先战斗,否则巡逻
     ├─ Sequence(Combat)
     │    ├─ GameCustom(1) IsTargetInRange  (条件)
     │    └─ GameCustom(2) Attack            (动作)
     └─ GameCustom(3) Patrol                 (动作, 持续 Running)

跑菜单 HYC Framework/Samples/创建全部示例资产(或单独 BT/创建示例树并导出)会程序化造这棵树并直接导出 Blob。

运行期加载 + 挂组件

BTBlobLoader.LoadAll();                       // 读 StreamingAssets/BTBlob/*.blob 注册
entityManager.AddComponentData(e, new RunningBT { TreeId = 900001, RunState = default, TickInterval = 0f });
// BTInterpreterSystem 已 RequireForUpdate<RunningBT>(),会自动驱动

编辑器自动注册 vs 真机导出 Blob(最关键)

✕
编辑器能跑 ≠ 真机能跑。编辑器 Play 时 BTAutoRegisterOnPlay 会自动把工程内 BTTreeAsset 注册进 BTManager,所以不导出也能跑。但 BTTreeAsset / BTBlobBuilder 在 Editor 程序集,打包后不存在,BTAutoRegisterOnPlay 也不执行 ⇒ 真机一棵树都拿不到。必须走「导出 Blob → BTBlobLoader.LoadAll() 读回」。

⚠️ 改完树一定要重新导出,否则真机跑的是旧版本(编辑器走自动注册,看不出来)。

给 AI 的雷区

  • BTTreeAsset / BTBlobBuilder 是 Editor 程序集:引用它们(建树/导出)的代码必须放 Editor 汇编或 #if UNITY_EDITOR。运行期只用 BTBlobLoader / BTManager / RunningBT / BTInterpreterSystem(均 Runtime)。
  • 连接即 children:BTConnectionData.PortIndex 是源节点输出端口序号,导出时据此构造 children 顺序;漏连或 PortIndex 重复会导出失败。
  • RunningBT.RunState 是 BTRunState(跨帧状态),用 default 即可;TickInterval=0 每帧,>0 按秒节流。
  • BTManager.Register 重复覆盖:同 TreeId 再次注册会释放旧 blob;编辑器自动注册 + 运行期 LoadAll 都注册同 Id,无害。
  • BTBlobLoader 已跨端,游戏层零平台代码:内部按 StreamingAssets.NeedsPreload 分流,你只管照常 LoadAll()。唯一硬性要求:引导入口用 await FrameworkBootstrap.InitializeAsync("World"),否则缓存是空的,加载会全部 miss。
  • GameCustom 节点 必须放在运行时程序集(不能只在 Editor),否则反射扫不到、节点变空操作。
  • ⚠️ 示例树 Id 必须避开业务常用 Id:导出会写 StreamingAssets/BTBlob/<treeId>.blob,用 1001 这类 Id 极易覆盖宿主自己的树。本例固定 900001。

验证清单

  • 跑菜单后 Assets/HYCSampleTrees/HYCSimpleDemoTree.asset + StreamingAssets/BTBlob/900001.blob + manifest.txt 出现。
  • 进 Play,日志显示「行为树加载:成功 1 棵」「已为实体挂载 RunningBT」。
  • 控制台持续打印 [HYCSimple] BT条件[目标在范围?] = 是/否、BT动作[攻击]…、BT动作[巡逻]…(说明 GameCustom 叶子被真正执行)。
  • 改动树节点后重新导出,确认真机链路拿到新版。