unity-game-enginedependency-injectionzenject

I can’t bind standard interface Zenject ITickable to class as AsTransient(). Tick() not called


I have a Timer class that implements the standard Zenject ITickable interface:

Timer: ITickable {
    public void Tick() {}
}

I want to create a new instance of Timer for every class that asks for it.

I do this (I want bind ITickable to Timer):

Container.BindInterfacesAndSelfTo<Timer>().AsTransient();

I get dependency, but method Tick() is not called.

And if I change the registration to AsSingle:

Container.BindInterfacesAndSelfTo<Timer>().AsSingle();

the method Tick() is called normally.

How can I get a new instance of Timer with ITickable AsTransient()?


Solution

  • TickableManager is a manager that is responsable for IxTickable interfaces. It gets a list of instances by injection. So when Zenject binder injecting dependencies in classes, it looks for certain provider that implements method to get exact object. So when you use .AsTransient it calls transient provider. And its instruction is to return a new instance, when someone is asking for it. So as TickableManager._tickables is a List<ITickables> that injected as well, the instance created by transient provider for this list is also brand new.

    So you have 3 options here:

    1. You can force add instances of Timer in TickableManager.
    Container.Bind<Timer>().AsTransient()
    .OnInstantiated<Timer>((_context,_object)=> 
             _context.Container.Resolve<TickableManager>()
             .Add(_object)
       );
    
    
    1. You can bind Timer with different id's. But this can only be justified in rare cases. In common it is a bad practice.
       Container.Bind(typeof(ITickable),typeof(Timer)).WithId(1).To<Timer>().AsSingle();
       Container.Bind(typeof(ITickable),typeof(Timer)).WithId(2).To<Timer>().AsSingle();
    
    1. You can use GameObjectContext for each object. And if you are trying to create some sort of collection of game entities: enemies, weapons ,NPC, etc, this approach is the best. You just have to add GameObjectContext to the root of an entity prefab. And then create installer for this context, where Timer should be binded .AsSingle.

    That was really tough question. It took several hours to find out the solution. I've already regretted taking on this question.

    P.S. Don't be affraid to press F12 and Ctrl+F12 in your IDE to seek something in external libraries. You gain important experience by acting like this.