0

I am new in Zenject world. I am modifying a sample code SampleGame1 (Beginner) (you can check the full example here) on Zenject in ShipState by implementing the Iinitializable, the existing one is IDisposable, so there are two implemented Interfaces right now.

public abstract class ShipState : IDisposable, IInitializable
{
    public abstract void Update();

    public virtual void Start()
    {
        // optionally overridden
    }

    public virtual void Dispose()
    {
        // optionally overridden
    }

    public virtual void OnTriggerEnter(Collider other)
    {
        // optionally overridden
    }

    public virtual void Initialize()
    {
        // optinally overridden
    }
}

The purpose is I want to use the Initialize() method in one of the states, so I can Subscribe to a signal inside the state.

public override void Initialize()
{
    Debug.Log("Initializing ShipStateMoving");
    signalBus.Subscribe("ExampleSignal");
}

public override void Dispose()
{
    Debug.Log("Disposing ShipStateMoving");
    _ship.ParticleEmitter.gameObject.SetActive(false);
}

But when I try to implement the method, Initialize() is not called, but Dispose() is successfully called... Why is that?

If I look at the InstallShip() in GameInstaller.cs I don't have any clue about how Idisposable is bound, but why the implementation of Idisposable in the existing example is successfully called while IInitializable isn't? I have no idea.

// I have no clue in this function why Idisposable is bound, there are no BindInterfaces in it.
void InstallShip()
{
    Container.Bind<ShipStateFactory>().AsSingle();

    // Note that the ship itself is bound using a ZenjectBinding component (see Ship
    // game object in scene hierarchy)

    Container.BindFactory<ShipStateWaitingToStart, ShipStateWaitingToStart.Factory>().WhenInjectedInto<ShipStateFactory>();
    Container.BindFactory<ShipStateDead, ShipStateDead.Factory>().WhenInjectedInto<ShipStateFactory>();
    Container.BindFactory<ShipStateMoving, ShipStateMoving.Factory>().WhenInjectedInto<ShipStateFactory>();
}
gazoon007
  • 56
  • 7

2 Answers2

0

I'm not really a master of Zenject but I think I can enlighten you a little for this case:

  • IDisposable is not a part of Zenject but part of System. As you can see in the documentation, it's needed to call explicitly the Dispose() method. In fact if the interface is bound with Zenject, when OnDestroy() is called on the context, it will call the Dispose() method on all bound IDisposable.
  • As you can see in the installer that you posted, there is only factories binded, not interfaces, so, if Dispose is called, it's called somewhere else ! And there it is.
  • The easiest way is to write what you wanted to do in Initialize in the already existing Start method provided by ShipState as it is called right after the state creation, to do initializations.
Adrian Mole
  • 49,934
  • 160
  • 51
  • 83
0

Happy New Year!
In conclusion, I recommend modifying the Factory method.
The code would look something like this

public abstract class ShipState : IDisposable
{
    public abstract void Update();

    public virtual void Start()
    {
        // optionally overridden
    }

    public virtual void Dispose()
    {
        // optionally overridden
    }

    public virtual void OnTriggerEnter(Collider other)
    {
        // optionally overridden
    }
    
    public class ShipStateFactory: PlaceholderFactory<ShipState>
    {
        
    }


}


public class CustomShipStateFactory : IFactory<  ShipState  >
{
    private DiContainer _container;
        
    [Inject]
    public CustomShipStateFactory(DiContainer container)
    {
        _container = container;
    }     
        
    public ShipState Create()
    {
            
        ShipState shipstate = _container.Instantiate<ShipState>();
        // todo Initialize    
        return shipstate;

    }
        
}
public class SampleInstaller : MonoInstaller
{
        public override void InstallBindings()
        {
            Container
                .BindFactory<ShipState , ShipState.Factory>()
                .FromFactory<CustomShipStateFactory>()
        }
}

=Why it's hard to use Initilized=
Zenject's Initialize method will only fire when you are bound with IInitializable.
In the Installer you wrote, the ShipState class and its Factory are Bind.
I believe that the ShipState class is only Bind as a ShipState class, so Initilized is not being called.
So, I would like to write .BindInterfacesTo() in Installer, but I cannot connect .From() because there is no Instance until it is generated.
This time, we need to Bind that Instance as IInitializable each time Create is called.

In order to bind,You need to create a GameObject that is created at the same time, attach a GameObjectContext to it, and bind it with that GameObjectContext separately.
(I recommend this way too, If ShipState is created with some gameobject)

Or you may be able to do it by using .FromSubContainerResolve(). But I do'nt know this way detailed.