Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Bug ConvertTransformToGameobject desyncing data?

Discussion in 'Entity Component System' started by Soaryn, Sep 1, 2020.

  1. Soaryn

    Soaryn

    Joined:
    Apr 17, 2015
    Posts:
    328
    So I've noticed an oddity when using ConvertTransformToGameobject and multiple entities.
    Scenario:
    Two entities are moving back and forth on a set path. When "shot" they will take some damage. However, using ConvertTransformToGameobject breaks down when both entities "die" at different times. One dies, then the other. When this happens, the gameobjects swap Transform values, but the entity data is still correct.

    Dying consists of tagging the entities with a "DeadState" ComponentData tag.

    Below is an example of two entities dying at different times. What I suspect is happening, is the transform indices are desynced from the entities. This seemingly only happens when they are die near each other.
    doomTransforms.gif
     
    Last edited: Sep 1, 2020
    Tony_Max likes this.
  2. Soaryn

    Soaryn

    Joined:
    Apr 17, 2015
    Posts:
    328
    UPDATE: It would seem the
    CopyTransformToGameObjectSystem:CopyTransforms job has a small issue of a race condition. Some where the indices for the transform arrays are seemingly swapped.

    In the end, I wound up using this as a temporary replacement. And while notably it is not near as performant as everything is on main thread, no indices have a chance to swap.
    Code (CSharp):
    1. [UpdateInGroup(typeof(TransformSystemGroup))]
    2. [UpdateAfter(typeof(EndFrameLocalToParentSystem))]
    3. public class CopyTransformBack : SystemBase {
    4.     protected override void OnUpdate() {
    5.         Entities.WithAll<CopyableTransform>().WithoutBurst().ForEach((in Transform transform, in LocalToWorld local) => {
    6.             transform.position = local.Position;
    7.             transform.rotation = local.Rotation;
    8.         }).Run();
    9.     }
    10.  
    11.     public struct CopyableTransform : IComponentData { }
    12. }
     
    Last edited: Sep 1, 2020
  3. Soaryn

    Soaryn

    Joined:
    Apr 17, 2015
    Posts:
    328
    Looking into it more, I managed to capture an example of the indices mixing up with some test code below, this time with three entities:
    Before death:
    0 - Enemy Variant - Trooper (1) - float3(2.35f, 0f, 0f)
    1 - Enemy Variant - Cacodemon - float3(0f, 0f, 0f)
    2 - Enemy Variant - Trooper - float3(-2.6f, 0f, 0f)
    After death:
    0 - Enemy Variant - Trooper - float3(1.063419f, 0f, 0f)
    1 - Enemy Variant - Trooper (1) - float3(0f, 0f, 0f)
    2 - Enemy Variant - Cacodemon - float3(-0.1049773f, 0f, 0f)

    Code for Debugging:
    Code (CSharp):
    1.  
    2. protected override void OnCreate() {
    3.     _transformGroup = GetEntityQuery(
    4.         ComponentType.ReadOnly(typeof(CopyableTransform)),
    5.         ComponentType.ReadOnly<LocalToWorld>(),
    6.         typeof(UnityEngine.Transform)
    7.     );
    8. }
    9.  
    10. protected void OnUpdate(){
    11.     var transforms = _transformGroup.GetTransformAccessArray();
    12.     var entities = _transformGroup.ToEntityArray(Allocator.TempJob);
    13.     var localsData = GetComponentDataFromEntity<LocalToWorld>();
    14.     for (var i = 0; i < transforms.length; i++) {
    15.         Debug.Log($"{i} - {transforms[i].gameObject.name} - {localsData[entities[i]].Position}");
    16.     }
    17.  
    18.     entities.Dispose();
    19.  
    20.     //Notably this is what is working, (albeit less performantly) and the transforms are linked to the right localPosition. So where does transformGroup become desynced? :thinking:
    21.     Entities.WithAll<CopyableTransform>().WithoutBurst().ForEach((in Transform transform, in LocalToWorld local) => {
    22.        Debug.Log($"{transform.gameObject.name} - {local.Position}");
    23.         transform.position = local.Position;
    24.        transform.rotation = local.Rotation;
    25.     }).Run();
    26. }
    27.  
    28. public struct CopyableTransform : IComponentData { }
    Supposition:
    Somehow the GetTransformAccessArray order is becoming dirtied, but not updated.
     
    Last edited: Sep 2, 2020
  4. Soaryn

    Soaryn

    Joined:
    Apr 17, 2015
    Posts:
    328
    May have found a more performant workaround for now. Not ideal, but it maintains multi threading as well as bursting the copy over. (Based on the companion link)
    Code (CSharp):
    1. protected override void OnUpdate() {
    2.     var entities = _transformGroup.ToEntityArray(Allocator.TempJob);
    3.     var transformObjects = new Transform[entities.Length];
    4.     var localsData = GetComponentDataFromEntity<LocalToWorld>();
    5.     for (var i = 0; i < entities.Length; i++) {
    6.         var entity = entities[i];
    7.         transformObjects[i] = EntityManager.GetComponentObject<Transform>(entity);
    8.     }
    9.  
    10.     _transformAccessArray.SetTransforms(transformObjects);
    11.  
    12.  
    13.     Dependency = new CopyTransformJob {
    14.         localToWorld = localsData,
    15.         entities = entities
    16.     }.Schedule(_transformAccessArray, Dependency);
    17.     entities.Dispose(Dependency);
    18. }
    19.  
    20. [BurstCompile]
    21. private struct CopyTransformJob : IJobParallelForTransform {
    22.     [NativeDisableParallelForRestriction]
    23.     public ComponentDataFromEntity<LocalToWorld> localToWorld;
    24.  
    25.     [ReadOnly]
    26.     public NativeArray<Entity> entities;
    27.  
    28.     public void Execute(int index, TransformAccess transform) {
    29.         var ltw = localToWorld[entities[index]];
    30.         // var mat = *(UnityEngine.Matrix4x4*) & ltw;
    31.         transform.localPosition = ltw.Position;
    32.         transform.localRotation = ltw.Rotation;
    33.         transform.localScale = new float3(ltw.Value.c0.x, ltw.Value.c1.y, ltw.Value.c2.z);
    34.     }
    35. }
    36.  
     
    Last edited: Sep 3, 2020