Unity ECS实体生成代码仅在命名空间长度≥18字符时正常运行问题求助
问题描述
参考Turbo Makes Games发布的Unity ECS Entity Manager教程开发,所用引擎版本为Unity 2020.3.20f1.1086。
初始代码可正常运行,但修改或删除命名空间后,SpawnEntitiesSystem.cs第20行的EntityManager.SetComponentData(newEntity, newPosition);逻辑会失效。经过多次测试复现:命名空间的具体名称不影响运行结果,仅当命名空间长度≥18字符时代码可正常运行,若命名空间长度为17字符,所有生成的实体会被创建在同一位置。第20行之后的其他代码可正常执行,为提升可读性示例代码已移除这部分内容(原始代码还处理了用户输入,且会在OnStartRunning方法中为新实体添加组件)。
此前仅在C++的指针运算场景见过类似问题,本以为C#不会出现此类问题,想了解该问题的核心成因是什么?
已尝试且无效果的操作
- 重启Unity
- 删除Library、Obj和Logs文件夹
- 在新项目中从零重写全部代码
- 重启电脑
相关代码
SpawnEntitiesSystem.cs
using Unity.Entities; using Unity.Mathematics; using Unity.Transforms; namespace ALongNamespaceABCD { public class SpawnEntitiesSystem : SystemBase { private int2 _entitySpacing; protected override void OnStartRunning() { var entitySpawnData = GetSingleton<EntitySpawnData>(); var gridSize = entitySpawnData.SpawnGrid; _entitySpacing = entitySpawnData.EntitySpacing; for (int x = 0; x < gridSize.x; x++) { for (int y = 0; y < gridSize.y; y++) { var newEntity = EntityManager.Instantiate(entitySpawnData.EntityPrefab); var newPosition = new LocalToWorld { Value = CalculateTransform(x, y) }; EntityManager.SetComponentData(newEntity, newPosition); } } } private float4x4 CalculateTransform(int x, int y) { return float4x4.Translate(new float3 { x = x * _entitySpacing.x, y = 1f, z = y * _entitySpacing.y }); } protected override void OnUpdate() {} } }
EntitySpawnData.cs
using Unity.Entities; using Unity.Mathematics; [GenerateAuthoringComponent] public struct EntitySpawnData : IComponentData { public Entity EntityPrefab; public int2 SpawnGrid; public int2 EntitySpacing; }
补充说明
后续测试发现,正确位置初始会被成功赋值,但会立刻被预制体的Transform组件覆盖,推测问题可能和预制体转实体的流程有关(使用了默认的Convert to Entity组件),或是引擎内部多线程逻辑存在问题。
问题解答
该问题的核心是Unity 2020版本配套的低版本Entities包存在的系统执行顺序隐式排序Bug,和C#语言本身无关。
- 没有显式指定
[UpdateBefore]/[UpdateAfter]执行顺序属性的ECS系统,Unity会默认按照系统所在命名空间、类型名称的字符串哈希值决定执行优先级,哈希值的计算规则会受字符串长度直接影响。 - 当命名空间长度为17字符时,
SpawnEntitiesSystem的隐式排序优先级刚好低于Unity自带的LocalToWorldSystem,你手动设置的LocalToWorld值会被后续运行的LocalToWorldSystem用实体上的Translation、Rotation等Transform组件值覆盖,就出现了赋值后立刻失效的现象。 - 当命名空间长度≥18字符时,
SpawnEntitiesSystem的隐式排序优先级刚好高于LocalToWorldSystem,你设置的LocalToWorld值不会被覆盖,逻辑就表现为正常。
修复方案
- 给
SpawnEntitiesSystem类加上显式执行顺序属性[UpdateBefore(typeof(TransformSystemGroup))],明确系统执行时机,就不会再受命名空间这类随机因素影响。 - 更规范的写法是不要直接修改
LocalToWorld组件,改为修改Translation组件的值,LocalToWorld会由Unity的Transform系统自动计算,也能规避该问题。
内容的提问来源于stack exchange,提问作者Milas_B
相关产品推荐
相关产品推荐

