我正在尝试学习 Unity Dots / ECS 系统,并且遇到了一个非常难以理解的问题,让我完全陷入困境。我正在尝试构建一个 life 模拟,其中有一组细胞产生并四处移动。但我还没到那一步。
我的项目的完整代码可以在这里找到,但是不起作用的代码片段如下:
using Unity.Burst;
using Unity.Entities;
using Unity.Mathematics;
[BurstCompile]
[UpdateInGroup(typeof(SimulationSystemGroup))]
public partial struct UpdateSystem : ISystem
{
[BurstCompile]
void OnUpdate(ref SystemState state)
{
float3 position;
foreach(var cell in SystemAPI.Query<CellAspect>())
{
// The following operation causes all the objects to be moved to the exact same position!
position = cell.position;
cell.position = position;
}
}
}
这看起来很简单。我遍历所有“单元格”并存储位置,然后直接将其分配回去。当我运行它时,所有单元格最终都处于相同位置。我尝试输出位置 (
Debug.Log(cell.position)
) 并且输出符合预期,每个位置都是它们随机生成的位置。
我的印象是有一个基本概念我没有讲到这里,但是随着ECS api如何不断变化,确实必须在网上找到可靠的教程。
CellAspect的定义如下:
public readonly partial struct CellAspect : IAspect
{
private readonly TransformAspect _transformAspect;
private readonly RefRW<CellProperties> _cellProperties;
public TransformAspect transform => _transformAspect;
public float3 velocity
{
get => _cellProperties.ValueRO.velocity;
set => _cellProperties.ValueRW.velocity = value;
}
public float3 position
{
get => _transformAspect.WorldPosition;
set => _transformAspect.WorldPosition = value;
}
public float detectionRadius => _cellProperties.ValueRO.detectionRadius;
}
IAspect
是在 1.0 中引入的,我已经不喜欢它了。这是一个为什么的例子。因为,正如您将在这里了解到的那样,它混淆了本应非常明确和明显的事情。
这是您的(部分)
CellAspect
:
public readonly partial struct CellAspect : IAspect
{
private readonly TransformAspect _transformAspect;
public float3 position
{
get => _transformAspect.WorldPosition;
set => _transformAspect.WorldPosition = value;
}
}
请注意,您创建了一个名为
position
的属性,它究竟改变了什么? WorldPosition
当然。这有什么重要的呢?好吧,这可能很重要,例如,您初始化了一个实体的本地位置 (LocalTransform
),并且在用这个隐藏的 _transformAspect.WorldPosition
覆盖该本地位置之前从未给它传播成为世界位置的时间框架回到它的原始预制件的价值。
我建议你要么删除这个模糊的
position
属性,要么在它的位置引入worldPosition
和localPosition
:
public readonly partial struct CellAspect : IAspect
{
private readonly TransformAspect _transformAspect;
public float3 worldPosition
{
get => _transformAspect.WorldPosition;
set => _transformAspect.WorldPosition = value;// surprise: writes to local pos too!
}
public float3 localPosition
{
get => _transformAspect.LocalPosition;
set => _transformAspect.LocalPosition = value;// haven't checked but probably writes to world pos too
}
}
void OnUpdate(ref SystemState state)
{
foreach(var cell in SystemAPI.Query<CellAspect>())
{
float3 pos = cell.localPosition;
cell.localPosition = pos;
}
}