Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Bug WorldTransform doesn't change on child entity

Discussion in 'Entity Component System' started by Quit, Mar 7, 2023.

  1. Quit

    Quit

    Joined:
    Mar 5, 2013
    Posts:
    63
    EDIT: Apparently when using PhysicsVelocity.Linear and moving the object, its WorldTransform doesn't update even on the root object itself.

    OLD DISCUSSION BELOW.

    Hello,

    I may be misunderstanding how the WorldTransform is supposed to work on entities, but I have noticed that asking TransformAspect for WorldPosition on a child entity doesn't change even if the parent entity moves.

    Here is the cube on the left with the child world position the same as the cube moved to the right.

    Left:
    cube left.png
    Right:
    cube right.png

    TransformAspect:
    transform.png

    Since LocalToWorld matrix changes, I'd expect WorldTransform to reflect that. Currently whenever I use WorldPosition of a child entity, it's returning me the default position of when the entity got baked.

    Thanks,
    A.
     
    Last edited: Mar 9, 2023
  2. tertle

    tertle

    Joined:
    Jan 25, 2011
    Posts:
    3,626
    You're not using rival or something are you?
     
  3. Quit

    Quit

    Joined:
    Mar 5, 2013
    Posts:
    63
    No. It's a barebone PhysicsVelocity.Linear. Unless using physics messes up the hierarchy handling?
     
  4. Occuros

    Occuros

    Joined:
    Sep 4, 2018
    Posts:
    284
    I have similar issues, but I expected it is related to netcode.

    no netcode in your project?
     
  5. TheOtherMonarch

    TheOtherMonarch

    Joined:
    Jul 28, 2012
    Posts:
    791
    I don't use TransformAspect. I use LocalTransform directly. What TransformAspect does is keep local and world position synchronized. It does nothing to synchronize children until the next update.

    I greatly prefer setting LocalTransform in a ending commandBuffer.
     
    Last edited: Mar 8, 2023
  6. Quit

    Quit

    Joined:
    Mar 5, 2013
    Posts:
    63
    No netcode in my project.

    It should update later in the frame, that's right, but it doesn't update it at all right now. The LocalToWorld matrix gets updated but not the WorldTransform, though it should be updated as far as I understand. Kinda makes it useless otherwise, which in turn makes TransformAspect useless.

    Would be great to find out from Unity if this is expected behaviour, or is this a bug?

    I'd be surprised if it was expected...kinda basic parent/child relationship and position change.
     
    Last edited: Mar 8, 2023
  7. TheOtherMonarch

    TheOtherMonarch

    Joined:
    Jul 28, 2012
    Posts:
    791
    The thread title is "WorldTransform doesn't change on child entity." You are saying that the child transform does not change after LocalToWorldSystem is run even though the LocalTransform is set?
     
  8. Quit

    Quit

    Joined:
    Mar 5, 2013
    Posts:
    63
    LocalTransform should not change on a child when the parent is moved, cuz the child is not moved. Only the world position should change when the parent gets moved. However, currently the WorldTransform (hence world position) of the child doesn't change when the parent gets moved.

    When I move the parent I'm expecting the child to move with it as well. The parent is a root object so Local and World positions are the same. The child has a local position which doesn't change, which is correct (cuz it moves together with a parent). However, the world position of the child should change (cuz it has changed it's world position), which it doesn't at the moment.

    Example

    I have a ship game object as a parent, and as a child I have a weapon transform game object. When I move the ship, it moves the weapon transform together with it. However, when I ask TransformAspect.WorldPosition for weapon transform - it is not updated ever (see the screenshots above).

    I can work around it cuz the LocalToWorld matrix gets updated, but I want to figure out if the WorldTransform is suppose to be updated or not, and if it's a bug.
     
    Last edited: Mar 8, 2023
  9. OUTTAHERE

    OUTTAHERE

    Joined:
    Sep 23, 2013
    Posts:
    656
    This is by "design", you need to set a special component on the entity for that to occur by default (it shows just how badly the Transform Aspect is thought out, because it silently conceals these pitfalls without as little as a warning).

    The component is PropagateLocalToWorld https://docs.unity3d.com/Packages/c...i/Unity.Transforms.PropagateLocalToWorld.html

    If this is not on the parent entity, all the child entities share the same world transform (that of the parent).

    This is actually quite desirable in many scenarios (e.g. child entity is responsible for UI rendering based on the parent's world position), but of course it's not how you would expect it to be if you come from vanilla Unity "Transform.position" semantics.
     
  10. Quit

    Quit

    Joined:
    Mar 5, 2013
    Posts:
    63
    Oddly LocalToWorld gets updated, but not the WorldTransform...

    EDIT: although it is slower to update all the hierarchy properly, it seems counter-intuitive to allow hierarchy, but not update it. If I wanted for a child not to update, I wouldn't make that entity a child.

    I based my conclusions on this. Is the documentation wrong then?
    upload_2023-3-8_12-40-35.png

    Strangely, it also says that if there is WorldTransform component, the LocalToWorld matrix will get updated. Which it does at the moment, and I could use the LocalToWorld for what I need.

    However, WorldTransform not being updated still stands. TransformAspect and WorldTransform are useless at the moment by returning the wrong (non-updated) values.

    Also:
    upload_2023-3-8_12-55-25.png
     
    Last edited: Mar 8, 2023
  11. Quit

    Quit

    Joined:
    Mar 5, 2013
    Posts:
    63
    FYI - just noticed that the root object WorldTransform doesn't change as well when using PhysicsVelocity.Linear. Only LocalTransform and LocalToWorld matrix react to the changes applied through PhysicsVelocity.Linear.
     
  12. scottjdaley

    scottjdaley

    Joined:
    Aug 1, 2013
    Posts:
    152
    I encountered this bug with TransformAspect a while back. I think it was discussed in the discord but should have been made into a forum thread or bug report. Thanks for creating the thread!

    My workaround right now is too create a custom TransformAspect that simply replaces the WorldTransform property with this:

    Code (CSharp):
    1.     public WorldTransform WorldTransform
    2.     {
    3.         get => HasParent() ? m_WorldTransform.ValueRO : (WorldTransform)m_LocalTransform.ValueRO;
    4.         set
    5.         {
    6.             m_WorldTransform.ValueRW = value;
    7.             if (HasParent())
    8.             {
    9.                 m_LocalTransform.ValueRW = (LocalTransform)ParentTransform.InverseTransformTransform(value);
    10.             }
    11.             else
    12.             {
    13.                 m_LocalTransform.ValueRW = (LocalTransform)value;
    14.             }
    15.         }
    16.     }
    The only difference is that the getter checks if there is a parent or not. If not, it returns the LocalTransform as the world transform instead.

    Another solution would be to add a system to the TransformSystemGroup that propagates the LocalTransform to the WorldTransform on entities with no parent. That would make the existing TransformAspect work as expected (unless you manually modify the WorldTransform component directly without using the aspect). In fact, this is probably the better solution and close to what an official fix would look like. I'll share the code for this once I've written it.
     
    Occuros likes this.
  13. scottjdaley

    scottjdaley

    Joined:
    Aug 1, 2013
    Posts:
    152
    Here is the code for a system that updates WorldTransform on entities with no parent at the end of the TransformSystemGroup:

    Code (CSharp):
    1. [UpdateInGroup(typeof(TransformSystemGroup), OrderLast = true)]
    2. public partial struct PropagateWorldTransformSystem : ISystem
    3. {
    4.     [BurstCompile]
    5.     public void OnUpdate(ref SystemState state)
    6.     {
    7.         new CopyTransformJob().ScheduleParallel();
    8.     }
    9.  
    10.     [BurstCompile]
    11.     [WithNone(typeof(ParentTransform))]
    12.     private partial struct CopyTransformJob : IJobEntity
    13.     {
    14.         [BurstCompile]
    15.         private void Execute(ref WorldTransform world, in LocalTransform local)
    16.         {
    17.             world.Position = local.Position;
    18.             world.Rotation = local.Rotation;
    19.             world.Scale = local.Scale;
    20.         }
    21.     }
    22. }
     
    Occuros likes this.
  14. Quit

    Quit

    Joined:
    Mar 5, 2013
    Posts:
    63
    Thank you for your help here.

    I have used LocalToWorld from the children which contains the world position and it gets updated, so it's not a problem getting the world position. The main thing was making sure that WorldTransform itself gets updated with the child's world position. It seems now that Local and World Transforms are the same, which means they do not change at all. Hence TransformAspect return values are wrong.

    I guess they use LocalToWorld to render objects, hence this bug slipped through. Otherwise children on screen positions would not change at all. Or they could completely remove WorldTransform and just use LocalToWorld.
     
  15. scottjdaley

    scottjdaley

    Joined:
    Aug 1, 2013
    Posts:
    152
    Yeah, you can also use LocalToWorld, but I personally like using TransformAspect because of all the other utility methods it provides and the fact that it keeps WorldTransform and LocalTransform in sync. Also, you can write to TransformAspect.WorldTransform but you cannot write to LocalToWorld because it will just be overwritten by the transform systems.

    Not sure I'm following. Child entities should have a correct WorldTransform without any of the fixes above. The problem is that the WorldTransform of entities without a parent is not updated when the LocalTransform changes (unless that change was done through TransformAspect.LocalTransform). With the example system I shared, it fixes this so that by the end of the TransformSystemGroup, the WorldTransform will be correct on entities without a parent.

    An important thing to note is that WorldTransform should never be directly written to. As the documentation says, it is derived from LocalTransform (and ParentTransform if there is a Parent). However, you can write to TransformAspect.WorldTransform because this correctly updates the LocalTransform at the same time.

    The other time that you can run into problems is if you write to LocalTransform directly (not using TransformAspect). If you then read from WorldTransform or TransformAspect.WorldTransform, both will return the incorrect value because the TransformSystem (including the system I wrote) haven't run yet this frame to keep the WorldTransform in sync.

    That is why it is usually best to always read and write transform data using TransformAspect as it will update all related data immediately.

    However, this still isn't perfect. If you use TransformAspect to update an entity's world or local transform, the children of that entity are not going to have their transform components updated until the TransformSystemGroup runs. This means that directly reading transform components or using TransformAspect on child entities may not return the correct results depending on when in the frame you read vs. when you updated the parent.

    The only way to make everything stay in sync all the time would be to recursively update all child entity WorldTransform components every time you updated the parent entity's world or local transform. This is expensive which is why there is no out of the box solution for this.

    Yeah exactly, LocalToWorld is really all the matters in the end. I think the reason that LocalToWorld and WorldTransform both exist is that LocalToWorld supports non-uniform scaling (and I think shearing?).
     
  16. Quit

    Quit

    Joined:
    Mar 5, 2013
    Posts:
    63
    I shall try to explain what I'm doing and maybe we will find the problem in my thinking.

    I have a gameobject Parent->Child. That's Ship (gameobject)->Bullet_spawn_location (child gameobject).

    I use PhysicsVelocity.Linear to move the Ship, which in turn moves Bullet_spawn_location together with it, cuz Bullet_spawn_location is a child of Ship (LocalToWorld matrix updates right).

    However, Bullet_spawn_location WorldTransform never updates. That means if I get TransformAspect from the Bullet_spawn_location and ask it for TransformAspect.WorldPosition, I get the wrong position, cuz it never updates.
     
  17. TheOtherMonarch

    TheOtherMonarch

    Joined:
    Jul 28, 2012
    Posts:
    791
    You are probably having some kind of race condition. Make a small test project and report a bug with the title including ECS if you cannot solve it.
     
    Quit likes this.
  18. scottjdaley

    scottjdaley

    Joined:
    Aug 1, 2013
    Posts:
    152
    Hmm yeah I don't have the same problem. I just tested this in my project. I have a PhysicsBody entity and I added a child entity to it (the cube). When I move the physics body around using physics, it correctly moves the cube and updates the WorldTransform component. Granted, I'm not directly writing to PhysicsVelocity.Linear, but I feel like that wouldn't make a difference.

    Here is a video of my test: https://imgur.com/EldjfY4
     
    Quit likes this.
  19. Quit

    Quit

    Joined:
    Mar 5, 2013
    Posts:
    63
    Here is the video with me moving a cube with PhysicsVelocity.Linear, and WorldTransform not changing at all.

    https://www.kapwing.com/videos/64125aa06b2e30001f6ea0f6

    Removed everything else, just left the player input and ship control systems.

    Here is the test project - https://github.com/killeriukas/World_Transform_Unity

    Unity used: 2022.2.8.f1

    P.S. To move a cube in a sample scene - A or D -> left or right. Look at the Ship and its children WorldTransforms.

    Appreciated if anybody finds time for the test. Maybe it's just me going insane. Thanks.