Hello
We’ve run into an issue which we believe to be a bug.
We have a bunch of object which still rely to some part on standard systems, hence we’re still very much working in a hybrid way and utilizing CopyTransformFromGameObject on most such objects. These are instantiated with IConvertGameObjectToEntity, along with ConvertToEntity set to ConvertAndInjectOriginal
In certain situations it appears as if the LocalToWorld component data can get incorrect values from the system handling the syncing. We noticed it when we had enemes targeting the player, and for one frame targeting something completely different position.
It became very apparent when we had a component we added to an entity, removed it and replaced it with another component, and then removed that one in the same frame. (Code examples below)
The problem went away when we used the same components and methods, but delayed the replacement and removal one frame. I.e., instead of Add → Remove/Add another component, it works if we do Add → Remove → Next frame → Add
Are we doing something horriby wrong, or is this a bug?
Wild guess, but I’m assuming that there occurs some mismatch in the copy transform system due to the entity changing archetypes in an unorthodox manner.
Our authoring scripts
public class TestUnitProxy : MonoBehaviour, IConvertGameObjectToEntity
{
public Entity entity { get; private set; }
public EntityManager entityManager { get; private set; }
void Update()
{
if(Input.GetKeyDown(KeyCode.Return))
{
if(!entityManager.HasComponent<TestUnitComponentData>(entity))
{
entityManager.AddComponentData<TestUnitComponentData>(entity, new TestUnitComponentData());
}
}
}
public void Convert(Entity entity, EntityManager dstManager, GameObjectConversionSystem conversionSystem)
{
this.entity = entity;
entityManager = dstManager;
entityManager.AddComponentData<CopyTransformFromGameObject>(this.entity, new CopyTransformFromGameObject());
dstManager.SetName(entity, name);
}
}
public class StationaryProxy : MonoBehaviour, IConvertGameObjectToEntity
{
public Entity entity { get; private set; }
public EntityManager entityManager { get; private set; }
public void Convert(Entity entity, EntityManager dstManager, GameObjectConversionSystem conversionSystem)
{
this.entity = entity;
entityManager = dstManager;
dstManager.SetComponentData<LocalToWorld>(entity, new LocalToWorld
{
Value = new float4x4(transform.rotation, transform.position)
});
dstManager.AddComponentData<CopyTransformFromGameObject>(entity, new CopyTransformFromGameObject());
#if UNITY_EDITOR
dstManager.SetName(entity, name);
#endif
}
}
TestUnitProxy adds an arbitrary component on hitting enter. The other type is just as a counterpoint, I tihnk there needs to be at least two archetypes for it to happen
The components. Does not seem related to whether they have data or are just tags, I’ve reproduced it with both cases
public struct TestUnitComponentData : IComponentData
{
}
public struct TestUnitSecondComponentData : IComponentData
{
}
The systems
public sealed class TestUnitSystem : ComponentSystem
{
#region Fields
EntityQuery group;
#endregion // Fields
#region Methods
#region ComponentSystem
protected override void OnCreate()
{
base.OnCreate();
group = GetEntityQuery(ComponentType.ReadWrite<TestUnitComponentData>());
}
protected override void OnUpdate()
{
NativeArray<Entity> entities = group.ToEntityArray(Allocator.TempJob);
for(int i = 0; i < entities.Length; ++i)
{
Entity entity = entities[i];
PostUpdateCommands.AddComponent<TestUnitSecondComponentData>(entity, new TestUnitSecondComponentData());
PostUpdateCommands.RemoveComponent<TestUnitComponentData>(entity);
}
entities.Dispose();
}
#endregion // ComponentSystem
#endregion // Methods
}
public sealed class SecondTestUnitSystem : ComponentSystem
{
#region Fields
EntityQuery group;
#endregion // Fields
#region Methods
#region ComponentSystem
protected override void OnCreate()
{
base.OnCreate();
group = GetEntityQuery(ComponentType.ReadOnly<TestUnitSecondComponentData>());
}
protected override void OnUpdate()
{
EntityManager.RemoveComponent(group, typeof(TestUnitSecondComponentData));
}
#endregion // ComponentSystem
#endregion // Methods
}
I’ve also uploaded a package with the project I used to reproduce the issue.