I’m trying to learn unity DOTS and every tutorial showed me a particular workflow when it comes to spawning entities. Yet, when I try to replicate it, my the previous spawned entities teleport back when instantiating new ones. Can someone please tell me why it happens? This is the code:
public void OnUpdate(ref SystemState state)
{
if (!SystemAPI.TryGetSingletonEntity<BoidSpawnerComponent>(out Entity spawnerEntity))
return;
RefRW<BoidSpawnerComponent> spawner = SystemAPI.GetComponentRW<BoidSpawnerComponent>(spawnerEntity);
spawner.ValueRW.ElapsedTime += Time.deltaTime;
if (spawner.ValueRO.ElapsedTime < spawner.ValueRO.Interval) return;
EntityCommandBuffer ecb = new(Allocator.Temp);
BoidSpawnerComponent spawnerValuesRO = spawner.ValueRO;
spawner.ValueRW.ElapsedTime = 0;
for (int i = 0; i <= spawnerValuesRO.BoidsPerInterval; i++)
{
if (spawnerValuesRO.TotalSpawnedBoids >= spawnerValuesRO.BoidsToSpawn)
break;
Debug.Log("Spawning entity");
Entity e = ecb.Instantiate(spawner.ValueRO.BoidPrefab);
ecb.AddComponent(e, new BoidComponent
{
velocity = math.normalize(UnityEngine.Random.insideUnitSphere) * spawnerValuesRO.MaxSpeed,
PersceptionDistance = spawnerValuesRO.PersceptionDistance,
Speed = spawnerValuesRO.MaxSpeed,
cellSize = spawnerValuesRO.CellSize,
alignmentBias = spawnerValuesRO.AlignmentBias,
separationBias = spawnerValuesRO.SeparationBias,
cohesionBias = spawnerValuesRO.CohesionBias,
Step = spawnerValuesRO.Step,
});
spawner.ValueRW.TotalSpawnedBoids++;
}
ecb.Playback(state.EntityManager);
}
Considering that you move things with LocalToWorld and you see issues when new objects are added, I suspect that you also have LocalTransform on the prefab (as is expected), and your instantiations add entities to existing chunks, causing change filters to trigger and have jobs do things like update the entire chunk’s LocalToWorlds based on the LocalTransforms, because change filters only work on a chunk level granularity. Since you’d not modified LocalTransform, entities in the chunk end up snapping back to their starting point.
LocalToWorld on its own does represent an object’s transform, but if LocalTransform is also present (and / or PostTransformMatrix), LocalToWorld is generated based on that.
You’re more or less supposed to use LocalTransform to perform main transform modifications if it’s present, and optionally apply non-uniform scale etc. with PostTransformMatrix. Try changing your code to only modify LocalTransform.
I don’t really understand what you’re asking cuz I think I already am only modifying LocalTransform. This is the only spot where I am doing that:
// in BoidMovementJob of BoidSystem line 124
trans.Value = float4x4.TRS(
math.lerp(trans.Position, (trans.Position + boid.velocity), deltaTime * boid.Step),
quaternion.identity,
new float3(1f)
);
Additionally, I tried setting an initial value when Instantiating, but it doesn’t solve the teleportation problem:
//I added this in the BoidSpawnSystem right after adding the BoidComponent (line 34)
ecb.SetComponent(e, new LocalTransform
{
Position = new float3(randomX, randomY, 0),
Rotation = quaternion.identity,
Scale = 1f
});
You’re writing to LocalToWorld there. Not LocalTransform. Your execute parameter was a LocalToWorld. float4x4.TRS returns a float4x4.
Given that you were writing to LocalToWorld, my previous post applies. Whatever value LocalTransform has, whether it be from the prefab’s stored value or from a SetComponent when it’s created, will be applied to LocalToWorld again when a new entity is added to the same chunk because a change filter (used for performance reasons, to skip updating chunks where components haven’t had write access to them or adds/removes) is triggered via component changes in a chunk. The behaviour is completely expected because LocalToWorld is derived from LocalTransform (and PostTransformMatrix) when present, and thus should not be modified directly on its own. The other components should be written to, and you can optionally compute the LocalToWorld yourself if you need an up-to-date value immediately.