Injecting data across scenes - mariaheine/Zenject-But-Wiki GitHub Wiki

In some cases it's useful to pass arguments from one scene to another. The way Unity allows us to do this by default is fairly awkward. Your options are to create a persistent GameObject and call DontDestroyOnLoad() to keep it alive when changing scenes, or use global static classes to temporarily store the data.

Let's pretend you want to specify a 'level' string to the next scene. You have the following class that requires the input:

public class LevelHandler : IInitializable
{
    readonly string _startLevel;

    public LevelHandler(
        [InjectOptional]
        string startLevel)
    {
        if (startLevel == null)
        {
            _startLevel = "default_level";
        }
        else
        {
            _startLevel = startLevel;
        }
    }

    public void Initialize()
    {
        ...
        [Load level]
        ...
    }
}

You can load the scene containing LevelHandler and specify a particular level by using the following syntax:


public class Foo
{
    readonly ZenjectSceneLoader _sceneLoader;

    public Foo(ZenjectSceneLoader sceneLoader)
    {
        _sceneLoader = sceneLoader;
    }

    public void AdvanceScene()
    {
        _sceneLoader.LoadScene("NameOfSceneToLoad", LoadSceneMode.Single, (container) =>
            {
                container.BindInstance("custom_level").WhenInjectedInto<LevelHandler>();
            });
    }
}

The bindings that we add here inside the lambda will be added to the container as if they were inside an installer in the new scene.

Note that you can still run the scene directly, in which case it will default to using "default_level". This is possible because we are using the InjectOptional flag.

An alternative and arguably cleaner way to do this would be to customize the installer itself rather than the LevelHandler class. In this case we can write our LevelHandler class like this (without the [InjectOptional] flag).

public class LevelHandler : IInitializable
{
    readonly string _startLevel;

    public LevelHandler(string startLevel)
    {
        _startLevel = startLevel;
    }

    public void Initialize()
    {
        ...
        [Load level]
        ...
    }
}

Then, in the installer for our scene we can include the following:

public class GameInstaller : Installer
{
    [InjectOptional]
    public string LevelName = "default_level";

    ...

    public override void InstallBindings()
    {
        ...
        Container.BindInstance(LevelName).WhenInjectedInto<LevelHandler>();
        ...
    }
}

Then, instead of injecting directly into the LevelHandler we can inject into the installer instead.


public class Foo
{
    readonly ZenjectSceneLoader _sceneLoader;

    public Foo(ZenjectSceneLoader sceneLoader)
    {
        _sceneLoader = sceneLoader;
    }

    public void AdvanceScene()
    {
        _sceneLoader.LoadScene("NameOfSceneToLoad", (container) =>
            {
                container.BindInstance("custom_level").WhenInjectedInto<GameInstaller>();
            });
    }
}

The ZenjectSceneLoader class also allows for more complex scenarios, such as loading a scene as a "child" of the current scene, which would cause the new scene to inherit all the dependencies in the current scene. However, it is often better to use Scene Contract Names for this instead.