[Entities 1.0.0-pre.15] ECS & Cinemachine

From 1.0, as I understand, all entities must reside in a sub-scene.
How would you plug a virtual cinemachine camera (which requires a reference to a gameobject to follow) to an entity residing in the subscene?

I’m thinking about making a ScriptaleObject containing a RigidTransform, referenced by a ReadTransform MonoBehaviour on the non-ECS side, and a WriteTransform managed component on the ECS side.

Do you have a better solution? Looks too complicated for me.

The simplest solution I have found is to keep the Player GameObject out of the subscene so it’s not converted to an Entity. If you have a need for the Player GameObject in the entity world, then you can just create a Player Entity and create a System (that runs on the main thread) that copies the Transform data from the Player GameObject to the Player Entity (or vice versa depending on your use case).

Spawn GameObject and link it with your player entity using CompanionLink.

  1. Create prefab with simple monobehaviour to find CmCamera (FindCmCamera)
  2. Add GameObjectPrefab ComponentData to store GameObject reference in your Player entity.
    3.Spawn GameObjectPrefab and link it with your Playerentity using CompanionLink.

FindCmCamera.cs

public class FindCMCamera : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {
        CmCamera camera = (CmCamera)FindObjectOfType(typeof(CmCamera));
        camera.Target = new CameraTarget
        {
            TrackingTarget = this.transform,
            LookAtTarget = this.transform
        };
    }
}

System to spawn and link gameobject to your Player entity

public partial struct GameObjectInitSystem : ISystem
{
    [BurstCompile]
    public void OnCreate(ref SystemState state)
    {
        var query = SystemAPI.QueryBuilder().WithAll<GameObjectPrefab>().Build();
        state.RequireForUpdate(query);
    }

    [BurstCompile]
    public void OnDestroy(ref SystemState state)
    {
    }

    public void OnUpdate(ref SystemState state)
    {
        var ecbSingleton = SystemAPI.GetSingleton<BeginSimulationEntityCommandBufferSystem.Singleton>();
        var ecb = ecbSingleton.CreateCommandBuffer(state.WorldUnmanaged);

        // Instantiate the associated GameObject from the prefab.
        foreach (var (goPrefab, entity) in SystemAPI.Query<GameObjectPrefab>().WithEntityAccess())
        {
            var go = GameObject.Instantiate(goPrefab.Prefab);
            ecb.AddComponent(entity, new CompanionLink{ Companion = go });
            ecb.RemoveComponent<GameObjectPrefab>(entity);
        }
    }
}
public class GameObjectPrefabAuthoring : MonoBehaviour
{
    public GameObject Prefab;

    public class Baker : Baker<GameObjectPrefabAuthoring>
    {
        public override void Bake(GameObjectPrefabAuthoring authoring)
        {
            AddComponentObject(new GameObjectPrefab { Prefab = authoring.Prefab });
        }
    }
}

// Stores a GameObject prefab that will be instantiated and associated with the entity.
public class GameObjectPrefab : IComponentData
{
    public GameObject Prefab;
}
3 Likes

Nice, Thanks a lot!

1 Like

Hmm, from what I have gathered, “CompanionLink” is not meant for public use and its functionality is limited to certain component types: Entities 0.50 AddComponentObject Not Creating CompanionLink

Here is a recent thread of devs discussing different hybrid workflows for 1.0: Clarification of Hybrid changes in 1.0?

And here is a thread on proposals for a more official hybrid workflow: Proposal for hybrid interoperability

2 Likes

Thanks for the info! Looks like it’s still complicated to do hybrid :s
For now, I did something like you suggested :

public class PlayerTracker : MonoBehaviour
{
    private void Start()
    {
        var em = World.DefaultGameObjectInjectionWorld.EntityManager;
        var entity = em.CreateEntity();
        em.SetName(entity, name);
        em.AddComponentObject(entity, this);
    }
}

[UpdateInGroup(typeof(TransformSystemGroup), OrderLast = true)]
public partial class PlayerTrackerSystem : SystemBase
{
    protected override void OnUpdate()
    {
        if (!SystemAPI.TryGetSingletonEntity<PlayerTag>(out var playerEntity))
            return;

        var localToWorld = SystemAPI.GetComponent<LocalToWorld>(playerEntity);

        Entities.ForEach((PlayerTracker tracker) =>
        {
            tracker.transform.SetPositionAndRotation(localToWorld.Position, localToWorld.Rotation);
        }).WithoutBurst().Run();
    }
}

Cinemachine points to the PlayerTracker Game Object residing in the same scene.

1 Like

error CS0122: ‘CompanionLink’ is inaccessible due to its protection level entities 1.0.0