ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Unity EasyECS 五分钟快速上手:只加一个 [ECS]

Unity EasyECS 五分钟快速上手:只加一个 [ECS] 前几篇一直在讲 EasyECS 为什么存在、AoS、SoA 和 CPU Cache。从这一篇开始不再只讲原理。我们直接把 EasyECS 跑起来。如果已经有一个普通的struct最基本的接入实际上只需要[ECS]然后剩下的大量代码都由 Source Generator 自动生成。项目地址EasyECS 本身就是MyFramework仓库中的一个独立 Unity Package。MyFramework 主仓库https://github.com/ZHOURUIH/MyFrameworkEasyECS 的 Unity Package Manager 地址https://github.com/ZHOURUIH/MyFramework.git?path/Packages/com.zhourui.easyecs当前版本EasyECS 1.1.0第一步安装 EasyECS打开 UnityWindow → Package Manager → → Add package from git URL填入https://github.com/ZHOURUIH/MyFramework.git?path/Packages/com.zhourui.easyecs安装完成以后就可以using EasyECS;第二步定义一个普通 Struct先写一个最普通的角色数据public struct RoleData { public int mHP; public float mSpeed; public float mPositionX; public float mPositionY; public int mID; public int mModelID; public int mCamp; }如果按照传统方式使用通常就是ListRoleData roles new();现在我们只做一个修改using EasyECS; [ECS] public struct RoleData { public int mHP; public float mSpeed; public float mPositionX; public float mPositionY; public int mID; public int mModelID; public int mCamp; }增加[ECS]以后这个 Struct 就会被 EasyECS 的 Source Generator 处理。第三步创建 ECSList编译完成以后EasyECS 会根据RoleData自动生成RoleDataECSList所以可以直接写RoleDataECSList roles new RoleDataECSList();也可以提前指定 CapacityRoleDataECSList roles new RoleDataECSList(1024);如果大概知道数据量我一般更建议提前给 Capacity。原因和ListT类似避免不断 Resize。第四步Add 数据添加数据仍然是非常熟悉的写法roles.Add(new RoleData { mHP 100, mSpeed 5.0f, mPositionX 10.0f, mPositionY 20.0f, mID 1001, mModelID 2001, mCamp 1, });还可以继续roles.Add(new RoleData { mHP 200, mSpeed 6.0f, mID 1002, });使用体验和ListRoleData.Add()非常接近。但底层存储已经不再是简单的RoleData[]第五步访问数据普通Liststruct有一个经常让人觉得麻烦的地方。例如RoleData role roles[index]; role.mHP - 10; roles[index] role;因为普通 struct 是值类型取出来以后得到的是副本。EasyECS 生成的 ECSList Indexer 返回的则是RoleDataRef所以可以直接RoleDataRef role roles[0]; role.mHP - 10; role.mPositionX role.mSpeed;修改会直接作用到底层 Storage。甚至可以写roles[0].mHP - 10;不需要RoleData temp ... ... roles[0] temp;这也是 EasyECS 希望保留的使用体验之一。[ECS]背后发生了什么代码中只写了[ECS] public struct RoleData但 Source Generator 实际会生成一整套相关类型。其中比较重要的包括RoleDataStorage RoleDataRef RoleDataECSList RoleDataECSDictionaryTKey Direct Column例如RoleDataECSList负责Add Insert RemoveAt RemoveAtSwapBack Clear Indexer Capacity Dispose所以开发者不需要自己去维护int[] hp; float[] speed; float[] positionX; float[] positionY;也不需要自己处理这些数组之间的同步。默认情况下字段会进入 SoA如果 Struct 使用[ECS]那么字段默认按照 ECS 字段处理。例如[ECS] public struct RoleData { public int mHP; public float mSpeed; public float mPositionX; public float mPositionY; }从数据布局上可以简单理解成mHP[] mSpeed[] mPositionX[] mPositionY[]也就是前面几篇一直讲的 SoA。不想拆开的字段怎么办实际项目中并不是所有字段都需要 SoA。例如HP Speed Position可能每帧都要处理。但ID ModelID Camp可能很少参与批量计算。那么可以写成[ECS] public struct RoleData { public int mHP; public float mSpeed; public float mPositionX; public float mPositionY; [NotECS] public int mID; [NotECS] public int mModelID; [NotECS] public int mCamp; }这样HP / Speed / Position走 SoA。而ID / ModelID / Camp继续保留 AoS。这就是 EasyECS 的SoA AoS Hybrid Storage。具体规则下一篇会专门讲。用完记得 DisposeECSList实现了IDisposable所以生命周期结束以后需要roles.Dispose();例如组件自己持有private RoleDataECSList mRoleList; private void Awake() { mRoleList new RoleDataECSList(1024); } private void OnDestroy() { mRoleList?.Dispose(); }这是因为 EasyECS 在合适的 Backend 下会使用 Native Storage。它和普通ListT只依赖 GC 回收不是一回事。一个最小完整例子把前面的内容放在一起using EasyECS; using UnityEngine; [ECS] public struct RoleData { public int mHP; public float mSpeed; public float mPositionX; public float mPositionY; [NotECS] public int mID; } public class EasyECSTest : MonoBehaviour { private RoleDataECSList mRoles; private void Start() { mRoles new RoleDataECSList(1024); mRoles.Add(new RoleData { mHP 100, mSpeed 5.0f, mID 1001, }); RoleDataRef role mRoles[0]; role.mHP - 10; role.mPositionX role.mSpeed; Debug.Log($HP:{role.mHP},X:{role.mPositionX}); } private void OnDestroy() { mRoles?.Dispose(); } }到这里其实已经完成 EasyECS 最基本的接入了。写在最后EasyECS 最基础的使用流程可以压缩成四步安装 Package ↓ struct 添加 [ECS] ↓ 使用生成的 xxxECSList ↓ 结束时 Dispose最重要的是开发者仍然维护public struct RoleData而不是手工维护一堆int[] float[] float[] float[]EasyECS 把这些重复又容易出错的工作交给 Source Generator。不过这里马上会出现一个问题是不是加了[ECS]以后所有字段都必须变成 SoA当然不是。下一篇[ECS]和[NotECS]不只是两个标签EasyECS 的数据布局规则详解我们会正式进入 EasyECS 的 Hybrid Storage看看一个 Struct 里怎么同时存在 AoS 和 SoA。
返回列表