Search code examples
c#unity-game-enginezenject

Unity Zenject, how to invoke method Construct in a class derived from MonoBehaviour from GameObjectContext?


So I have two scripts, Character and PlayerInstaller

using UnityEngine;
using Zenject;

public class Character : MonoBehaviour
{
    [SerializeField] private Vector3 VelocityWhenPressed = Vector3.up;

    private Rigidbody m_Rigidbody;

    [Inject]
    public void Construct(Rigidbody rigidbody)
    {
        m_Rigidbody = rigidbody;
    }

    private void Update()
    {
        if(Input.GetMouseButtonDown(0))
        {
            m_Rigidbody.velocity += VelocityWhenPressed;
        }
        if(Input.GetMouseButtonUp(0))
        {
            m_Rigidbody.velocity -= VelocityWhenPressed;
        }
    }
}
using UnityEngine;
using Zenject;

public class PlayerInstaller : MonoInstaller
{
    [SerializeField] private Rigidbody m_PlayerRigidbody;
    [SerializeField] private Character m_TheCharacter;

    public override void InstallBindings()
    {
        this.Container.Bind<Rigidbody>().FromInstance(m_PlayerRigidbody).AsSingle();
        this.Container.Bind<Character>().FromInstance(m_TheCharacter).AsSingle();
    }
}

If I create a game object with Rigidbody, add there Character component, create another game object, add there components GameObjectContext and PlayerInstaller and add PlayerInstaller to MonoInstallers of GameObjectContext, Construct method will not be invoked. But if I do the same thing with SceneContext instead of GameObjectContext, it will work. How to do that with GameObjectContext?

If I do the same thing with SceneContext instead of GameObjectContext, it works well.


Solution

  • The difference between SceneContext and GameObjectContext is that they handle a Scene and a GameObject respectively.

    When you use your installer in SceneContext it works because everything is part of the scene.

    But when you use it in GameObjectContext, it doesn't work because your Rigidbody/Character components belong to a separate GameObject hiearchy and thus injection doesn't happen.

    In the installer itself, you only bind your components as references, so no injection into components happens either. This way you only make the context aware of these classes for future injections that the context could handle.

    Plus, there are more issues with your setup:

    • If you have SceneContext in your scene, SceneContext will try to automatically inject into Character because Character belongs to the scene and has [Inject] attribute. And it will fail unless it knows about some Rigidbody (which is probably a wrong one anyway).

    • And if you don't have SceneContext in your scene, then GameObjectContext and your installer will not run at all.

    So, to correctly use the GameObjectContext here, make your Rigidbody/Character GameObject a child of it. With this setup, your GameObjectContext will properly inject Rigidbody into Character and your SceneContext will not interfere.