Reputation: 23
I tried following this Unity ECS Entity Manager Tutorial from Turbo Makes Games. I'm using Unity 2020.3.20f1.1086.
The Code works initially but I noticed, that Line 20 in SpawnEntitiesSystem.cs EntityManager.SetComponentData(newEntity, newPosition);
stops working when I change or remove the namespace. After tinkering a bit with the code I can reproduce that the specific namespace is not important but the namespace has to be at least 18 characters long for the code to work as expected. If I change the namespace to 17 characters all entities are spawned at the same place. Other code after the faulty line 20 was executed but for readability I removed it from the code example. (Originally user input was also handled and in OnStartRunning a component was added to the new entitiy)
I only heard of similar problems in e.g. C++ when using pointer maths but I thought something like this shouldn't happen in C#. Does anyone have an idea what the core problem could be?
What I already tried without succes
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
using Unity.Entities;
using Unity.Mathematics;
[GenerateAuthoringComponent]
public struct EntitySpawnData : IComponentData {
public Entity EntityPrefab;
public int2 SpawnGrid;
public int2 EntitySpacing;
}
Edit: I tried around a bit more and found out that it seems that the correct position will be set correctly at first. But it will instantly be overwritten by the prefabs Transform component. So maybe it has to do with converting the Prefab to an entity (I use the default Convert to Entity component) or a problem with some internal multithreading occurs.
Upvotes: 1
Views: 181
Reputation: 20249
Well, we finally got a response from Unity. Here is what they said:
This is intended behavior and will therefore be set to "won't fix".
The reasons are this:
- Changing the name (and namespace) of a system changes its default position in the frame, this is expected behavior.
- The SpawnEntitiesSystem from the provided repro project is not using any explicit system ordering attribute like UpdateAfter/UpdateBefore/UpdateInGroup, so it follows the default.
- Because it is expected behavior but nonetheless surprising, we have a task in the backlog about making the default order not depend on the name in the future.
- The prefab being instantiated has a Translation component and a Rotation component in addition to the LocalToWorld component.
- The transform system from entities updates LocalToWorld based on the values of Translation and Rotation.
- A direct modification of LocalToWorld conflicts with the transform system, and that modification will be overwritten.
- Because Translation and Rotation are still at their default values, the expected outcome is that the transform system will compute an identity LocalToWorld and put the instances back to the origin.
- Unfortunately there is a bug in that particular setup (where the system doing the modification does it in OnStartRunning and executes immediately after the transform system, if the namespace is long enough to put it after the transform system) that causes the newly spawned instances to never be noticed by the transform system. And it is because of that bug that the cubes would show in the intended grid layout by accident.
So, apparently, this whole situation happens because the LocalToWorld
is sometimes being overwritten due to operation ordering that the length of the namespace is able to change. But this won't be the case in a future version.
Upvotes: 1
Reputation: 23
A workaround is to set the Translation instead of the LocalToWorld Data.
Working code:
EntityManager.SetComponentData(newEntity, new Translation {
Value = new float3(
10.0f,
1.0f,
10.0f
)
});
Upvotes: 1