Load/unload scenes with ECS Hybrid

Hey all,

I use an entity as a Bridge that gives some information to Camera and Spawner gameobjects. To add the Camera as a follower of this Bridge entity, I use this pattern:

  • Camera has a monobehavior with a public Entity field;
  • the Bridge entity is a converting gameobject that adds itself to the above Entity field during conversion:
public class FollowGameObject : MonoBehaviour, IConvertGameObjectToEntity
{
    public GameObject Follower;

    public void Convert(Entity entity, EntityManager dstManager, GameObjectConversionSystem conversionSystem)
    {
        FollowEntity followEntity = Follower.GetComponent<FollowEntity>();
        followEntity.EntityToFollow = entity;
    }
}

This entity Bridge tells me when to load a new scene, to do some level design. For example, when I reach a certain task in scene A, in one of my systems I set a flag in the Bridge. Then, the follower gameobject should notice this in LateUpdate() and try to load a new scene say B:

public class FollowEntity : MonoBehaviour
{
    public Entity EntityToFollow;
    public float3 offset;

    private EntityManager _entityManager;

    void Start()
    {
        _entityManager = World.DefaultGameObjectInjectionWorld.EntityManager;
    }

    void LateUpdate()
        if (EntityToFollow == null)
            return;
        Translation entityPosition = _entityManager.GetComponentData<Translation>(EntityToFollow);
        transform.position = entityPosition.Value + offset;

        if(_entityManager.GetComponentData<ChangeScene>(EntityToFollow).Value)
        {
            _entityManager.DestroyEntity(_entityManager.UniversalQuery);
            SceneManager.LoadScene("Level2Development");

        }
    }
}

I’m testing this just reloading an identical scene. The main problem is that when the new scene is loaded, the Camera gameobject can’t find the Bridge in its EntityToFollow field and I get ā€œThe entity does not existā€ error… My guess is that the conversion happens too late, so the above code in the Convert function, in which the entity adds itself to GameObject, happens after the GameObject throws an error because because its field is null.

Any workaround to this? I hope my architecture is not too bad… but any architectural suggestion would be appreciated. also I prefer to stay with legacy Unity scene loading/unloading from monobehaviors because, y’ know…
Thank you in advance :slight_smile:

I think you might be missing the IDeclareReferencedPrefabs on your conversion.

 public GameObject Follower;

public void DeclareReferencedPrefabs(List<GameObject> referencedPrefabs)
    {
        referencedPrefabs.Add(Follower);
    }

And maybe
followerEntity = conversionSystem.GetPrimaryEntity(Follower);

Other scene data transfer methods I use (because it works not because I would recommend them):

To move data between scenes I use a normal game object singleton with some static fields and a ā€˜don’t destroy on load’. This object is in every scene and when it is loaded the singleton is replaced with the new object and the data of the old object is copied to the new object. This allows to get correct game object references from normal objects in the scene (like the camera).

Thank you for your response! What I’m missing reading your solution is that I need a follower GameObject that never converts to entity. My Camera never converts, still needs to reference for an entity to follow its translation. So, nothing wrong with my above workflow for the first scene, but when I destroy all entities and reload the scene, Camera loses its reference for just one frame, throws an error, then gets the reference right (because conversion happened) and starts to follow the entity. But I wished to know how to avoid this first frame error and get the reference immediately (maybe with GetPrimaryEntity, but idk where to put it in the Camera Monobehavior since it can’t implement IConvertGameObjectToEntity).

Oke, what I do for this (if i understand your issue correctly) is have a ā€˜don’t destroy on load’ Singleton ā€œGameManagerā€ within every scene that holds the references all mono objects of the scene like the camera.

public class GameManager : MonoBehaviour
{
        public static GameManager main;
        public GameObject VrPlayer;
        public bool pauseToggelReleased = true;

        private void Awake()
        {
            var old = main;
            main = this;
            DontDestroyOnLoad(gameObject);

            if (old != null)
            {
               // if you want to keep data from the previous instance
                pauseToggelReleased = old.pauseToggelReleased;
               

                Destroy(old.gameObject);
             }
     }
}

VRplayer will now always reference the GameObject you have in the loaded scene. (you could add a isNewGamemanagerLoaded bool that you set to false before loading and to true in the awake() function. So you will have something to check before trying to get the data, should the old GameManager still be the one in the singleton)

In the OnUpdate() of a system, in the non-burst main thread part, you can now access the data and put it in a variable which you can use in bursted/scheduled code.

float3 VrPlayerPosition = GameManager.main.VrPlayer.transform.position;

1 Like

Such an elegant solution :slight_smile: that perfectly worked. Thank you!