ECS Hybrid: Copying Animation data back into ECS

One of the strict requirements of the project I’m experimenting with porting to ECS is deriving object positions based on animation-driven Transforms. Before this was simple as I could directly read the Transform positions. However, in ECS Hybrid, Entities drive the positions of Transforms, not the other way around. From here I’ve found several ideas on how to do this:

  • Use DOTS Animation to sample animation, copy data into Translation/Rotation/Scale on Entities, use hybrid links to write the results to the Transform components which drives the SkinnedMeshRenderer. (I still need cloth physics, which doesn’t seem to be supported by both DOTS Animation and DOTS Physics do not support right now).

  • Use a custom job to sample a Playable on the main thread then use a separate job with a TransformAccessArray to copy this data back into their linked Entities. Compute the locations for the simulation objects, then use the hybrid link to write the same data back into Transforms.

  • Bake either at startup or at edit time the transform data for into a BlobAsset to skip runtime data sampling entirely and just read the per-game tick transform data from the BlobAsset. The associated GameObject only follows the animation sample rate as a view for the underlying simulation data.

Until DOTS Animation is in a more usable state, what’s the suggested way to approach this problem? Related to two of the ideas I’ve listed here, is are the TransformStream computations done in Animation Jobs or Playables computed with FloatMode.Strict or FloatMode.Fast? Determinism is one of the big reasons we chose to start experimenting with DOTS, and if sampling Playables or using Animation Jobs breaks determinism, they’re no longer viable solutions for us.

  1. Create an entity for each animation driven transform.

  2. Use EntityManager.AddComponentObject() to attach the transform to the entity.

  3. Use a job like this to sync transform to translation rotation:

        [BurstCompile]
        struct CopyFromTransforms : IJobParallelForTransform
        {
            [NativeDisableParallelForRestriction]
            public ComponentDataFromEntity<Translation> translationLookup;
         
            [NativeDisableParallelForRestriction]
            public ComponentDataFromEntity<Rotation> rotationLookup;

            [ReadOnly]
            public NativeArray<Entity> entities;

            public void Execute(int index, TransformAccess transform)
            {
                var entity = entities[index];

                translationLookup[entity] = new Translation
                {
                    Value = transform.position
                };

                rotationLookup[entity] = new Rotation
                {
                    Value = transform.rotation
                };
            }
        }

Note: this job assumes entities that follow transforms are in world space and ignores animation scaling.

I don’t know if any data from mecanim or playables can be considered deterministic.

Why not just use CopyTransformFromGameObject ? This is a standard component from Unity.Transforms.Hybrid

Thanks for the suggestions!

Re CopyTransformFromGameObject: It’s not documented too well. I have some questions:

  • Does it only apply to a given GameObject - Entity pair? or does it affect the entire hierarchy underneath it?

  • Does this require a call AddComponentObject on the entity with the Transform?

  • Does it copy the full world TRS or just the local TRS?

  • Can I have a root entity use CopyTransformToGameObject on the root and CopyTransformFromGameObject on it’s children?

  • Is it reasonable to subclass the controlling systems to move them to another location in the system execution order?