[1.0.0-pre.15] Unable to create ECS Transform hierarchy in code

Hi all,

I have been trying for a few days to get an entity hierarchy happening just using code (no GameObjects). I have something like the following:

            var topEntity = ecb.CreateEntity();
            ecb.SetName(topEntity, "Parent");
            ecb.AddComponent<LocalTransform>(topEntity);
            ecb.SetComponent(topEntity, new LocalTransform { Position = new float3(1, 1, 1) });
            ecb.AddComponent<LocalToWorld>(topEntity);
            ecb.AddComponent<WorldTransform>(topEntity);

            var childEntity = ecb.CreateEntity();
            ecb.SetName(topEntity, "Child");
            ecb.AddComponent<LocalTransform>(childEntity);
            ecb.SetComponent(childEntity, new LocalTransform { Position = new float3(2, 2, 2) });
            ecb.AddComponent<LocalToWorld>(childEntity);
            ecb.AddComponent<WorldTransform>(childEntity);
            ecb.AddComponent<ParentTransform>(childEntity);
            ecb.AddComponent(childEntity, new Parent { Value = topEntity });

The entities appear in the hierarchy correctly. My understanding is that that LocalToWorld xyz show the actual location of the entity in the world. Parent above shows (1,1,1) as expected. Child I would expect to show (3,3,3) as it takes into account it’s own LocalTransform and the parent’s too. However when I run the code the Child has (1,1,1) as it’s LocalToWorld.

When I set up exactly the same as above using GameObjects it works as expected and the child has (3,3,3). Does anyone know why this is happening? There is very little documentation or example code regarding this setup which seems like it should be fairly straightforward.

Thanks!

Stu

Just bumping this … does anyone have an example of working code using 1.0.0 that creates a working transform hierarchy from code please? I must be doing something wrong but I can’t find any sample code for creating a transform hierarchy using the latest version.

Also having issues with LocalToWorld not working, what is the correct way to replace this? It would be nice if breaking changes like this were made clearer to users

https://docs.unity3d.com/Packages/com.unity.entities@1.0/manual/transforms-using.html

I have problem with transforms on children of rigidbodies (all setup done manually via code)
Rigid Body Parent
Mesh Child

The children are always at 0,0,0
Things dont work as expected with transforms in 1.0, there are hidden components or something i dont know

You need to setup LinkedEntityGroup, LocalTransform and Parent components when creating entities. Others like Child, ParentTransform are calculated automatic by Unity internal systems.

When the parent becomes a rigid body though the children stop getting updated.

Maybe they become unparented.
8676753--1169526--upload_2022-12-21_2-0-59.png

baking works fine, the problems rise when doing things via code. Simply adding physics velocity and the other components to make an entity a dynamic body, makes the children stop updating

you need to add this on the root object for children to work

entityManager.AddComponentData(entity, new PropagateLocalToWorld() { });

Thanks for the help above but I still haven’t been able to solve this. I really love the idea of ECS and DOTS but I don’t think I’m alone in saying that finding good examples and clear documentation is very hard and frustrating.

I would have thought that a pure ECS approach to constructing a transform hierarchy using 1.0.0 would be a core concept that needs to be documented, working and have clear examples. Too often the documents are outdated, moved or don’t match the actual behaviour … or as in my case only cover using a hybrid approach and not an approach that doesn’t use GameObjects.

Can anyone show me some code that achieves what is in the above code sample and actually works … please? =)

2 Likes

Can’t see anything wrong with your parenting.
Are those entities static?
In that case, maybe the LocalToWorldSystem filters out their update thinking they didn’t changed.
Try to remove the WorldTransform & ParentTransform Components on both entities. The LocalToWorldSystem adds them anyway, and it should trigger an update.

Oh and don’t forget to setup scale & rotation in your LocalTransform, or the scale will be 0 and the entity won’t be visible.

new LocalTransform { Position = new float3(1, 1, 1), Scale = 1, Rotation = quaternion.identity }

Actually, this might be the problem…

I have the same issue. Parent component just disappears from the child entity (or is not created in the first place).

@stu_g did you manage to make it?

I end up making my own primitive parenting system for now.

I wrote a unit(y) test just to check my own sanity. This is what I ended up with that seems to be working

    [Test]
    public void TestParenting()
    {
        var parent = EntityManager.CreateEntity();
        var child = EntityManager.CreateEntity();

        EntityManager.AddComponentData(parent, new LocalTransform() { Position = float3(0, -1, 0), Rotation = Unity.Mathematics.quaternion.identity, Scale = 1 });
        EntityManager.AddComponentData(child, new LocalTransform() { Position = float3(0, 2, 0), Rotation = Unity.Mathematics.quaternion.identity, Scale = 1 });
        EntityManager.AddComponent<LocalToWorld>(parent);
        EntityManager.AddComponent<LocalToWorld>(child);
        World.Update();
        EntityManager.AddComponentData<Parent>(child, new Parent { Value = parent });
        World.Update();

        Assert.IsTrue(EntityManager.HasComponent<Parent>(child), "child should have the parent component");
        Assert.IsTrue(EntityManager.HasBuffer<Child>(parent), "parent should have the child buffer");

      
        var childWorld = EntityManager.GetComponentData<WorldTransform>(child);
        Assert.AreEqual(float3(0, 1, 0), childWorld.Position, "child should be at 0, 1, 0");


        EntityManager.SetComponentData(parent, new LocalTransform
        {
            Position = float3(1, 0, 1),
            Rotation = Unity.Mathematics.quaternion.identity,
            Scale = 1
        });

        World.Update();
     
        childWorld = EntityManager.GetComponentData<WorldTransform>(child);
        Assert.AreEqual(float3(1, 2, 1), childWorld.Position, "child should be at 1, 2, 1 after moving the parent");
    }

What strikes me as odd is that the WorldTransform gets added automatically by the LocalToWorldSystem:

            if (!_entityWithoutWorldTransformQuery.IsEmptyIgnoreFilter)
                state.EntityManager.AddComponent(_entityWithoutWorldTransformQuery, ComponentType.ReadWrite<WorldTransform>());
            if (!_parentWithoutParentTransformQuery.IsEmptyIgnoreFilter)
                state.EntityManager.AddComponent(_parentWithoutParentTransformQuery, ComponentType.ReadWrite<ParentTransform>());
            if (!_parentTransformWithoutParentQuery.IsEmptyIgnoreFilter)
                state.EntityManager.RemoveComponent(_parentTransformWithoutParentQuery, ComponentType.ReadWrite<ParentTransform>());

But the LocalToWorld component doesn’t get added, so if you add that manually, then the system’s main query picks up your entity:

 var builder = new EntityQueryBuilder(Allocator.Temp)
                .WithAll<LocalTransform>()
                .WithAllRW<WorldTransform, LocalToWorld>()
                .WithNone<Parent>()
                .WithOptions(EntityQueryOptions.FilterWriteGroup);
            _rootsQuery = state.GetEntityQuery(builder);

and then the hierarchy starts working if you add a Parent component, where you can move the parent, and the child moves with it

1 Like

Thanks! It helped a lot.
Also: it’s enough to set LocalToWorld only for Parent entity.