1

I have an array of prefabs and I want to be able to Instantiate randomly picked prefabs thru Zenject Factory and perform their bindings in their sub-containers.

What I want to do is the same as in this code sample from Zenject documentation, but for randomly selected prefabs. https://github.com/modesttree/Zenject/blob/master/Documentation/SubContainers.md#using-game-object-contexts-no-monobehaviours

using UnityEngine;
using Zenject;

public class GameInstaller : MonoInstaller
{
    [SerializeField]
    GameObject ShipPrefab;

    public override void InstallBindings()
    {
        Container.BindInterfacesTo<GameRunner>().AsSingle();

        Container.BindFactory<float, ShipFacade, ShipFacade.Factory>()
            .FromSubContainerResolve().ByNewPrefabInstaller<ShipInstaller>(ShipPrefab);
    }
}

I was able to partially make it work with

[SerializeField] private GameObject[] ships;

...

Container.BindFactory<float, ShipFacade, ShipFacade.Factory>()
            .FromSubContainerResolve().ByNewGameObjectMethod(SpawnShip);

...

private void SpawnShip(DiContainer container, float speed)
{
    container.Bind<ShipFacade>().AsSingle();
    container.Bind<Transform>().FromComponentOnRoot();
    var shipPrefab = ships[Random.Range(0, ships.Length)];
    var ship = container.InstantiatePrefab(shipPrefab);
    container.Bind<ShipHealthHandler>().FromNewComponentOn(ship).WhenInjectedInto<ShipFacade>();
    container.BindInstance(speed).WhenInjectedInto<ShipInputHandler>();
}

But it's awkward and in this case I guess I'm not using an advantage of sub-container. And also prefabs spawns in an empty GameObject. What I want to achieve is to be able to use ShipInstaller for sub-container binding.

earthQuake
  • 106
  • 3
  • 11
  • Btw it is bad practice to execute any of the Instantiate methods during the install. It would be better to put that inside a bind method like `Container.Bind().FromComponentInNewPrefab(shipPrefab).AsSingle()` – Steve Vermeulen Nov 10 '19 at 19:08

1 Answers1

1

You're right, there wasn't really a very elegant way to choose the sub-container prefab dynamically.

I took some time to make this better today with this commit. If you use the latest version of Extenject then you can now do things like this:

public class QuxInstaller : Installer {
    float _speed;

    public QuxInstaller(float speed) {
        _speed = speed;
    }

    public override void InstallBindings() {
        Container.BindInstance(_speed);
        Container.Bind<QuxFacade>().AsSingle();
    }
}

public class CubeInstaller : MonoInstaller
{
    public List<GameObject> QuxPrefabs;

    public override void InstallBindings()
    {
        Container.BindFactory<float, QuxFacade, QuxFacade.Factory>()
            .FromSubContainerResolve().ByNewPrefabInstaller<QuxInstaller>(ChooseQuxPrefab);
    }

    UnityEngine.Object ChooseQuxPrefab(InjectContext _) {
        return QuxPrefabs[Random.Range(0, QuxPrefabs.Count)];
    }
}
Steve Vermeulen
  • 1,406
  • 1
  • 19
  • 25
  • 3
    What if you want to be able to pass a parameter to the prefab chooser method or use a prefab factory you already have? I have a custom factory for creating prefabs based on a parameter that is passed into the Create method, but now I want to create a facade class with a subcontainer like the shipfacade example but I still need to create prefab based on the parameter passed in. Do you have any suggestions? – Ben Feb 11 '20 at 15:11
  • I ended up getting this to work by binding my parameter to the subcontainer and then injecting that value into the prefab factory. That way I have a factory that can only make prefabs based on that bound parameter. It works really well, and I end up not needing a custom factory for the facade. – Ben Feb 13 '20 at 14:59
  • Could you post an example of your code which you solved that with? – earthQuake Mar 16 '20 at 08:08
  • I got stuck with the same problem, and couldn't find a good solution. I would love an example of this as well – novalain Jun 09 '21 at 07:13