Dependency Injection (DI) ================================= Starting with version 2.0, RTS Starter Kit moved away from singletons (like the old ``GameController.Instance``) towards **Dependency Injection**. Instead of a script reaching out to a global instance to get what it needs, its dependencies (other services, settings, etc.) are given to it automatically by the framework — you just declare a field, and it gets filled in for you. This is why you see code like this everywhere in the API Reference and tutorials: .. code-block:: csharp class SomeSystem : IInitSystem { [Inject] GameplayService gameplay; [Inject] SelectionService selection; public void Init() { selection.SelectionStarted += OnSelectionStarted; } void OnSelectionStarted() { // gameplay and selection are already filled in here, no manual lookup needed. } } The DI mechanism itself is not part of RTS Starter Kit — it comes from a small, separate open-source library, `InsaneOne.Core.Injection `_, which RTS Starter Kit uses under the hood. You don't need to reference or install anything extra, it's already included with the asset — but if you want to see exactly how it works internally, that repository is the source of truth. How it works **************************************** **The** ``[Inject]`` **attribute** marks a field (or a method) that should receive a dependency: .. code-block:: csharp [Inject] GameplayService gameplay; At some point during initialization, the framework's ``Container`` goes through all registered objects (called *targets*) and, for every ``[Inject]``-marked field, finds a registered dependency whose type matches the field type and assigns it. You don't call anything to make this happen in your own gameplay scripts — as long as your class is picked up by the game's bootstrap as a system/service, its ``[Inject]`` fields will already be set by the time your ``Init()``/``Start()`` runs. Under the hood, roughly: .. code-block:: csharp var container = new Container(InjectionType.All); container.AddDependency(gameplayServiceInstance); // registers a dependency by its runtime type container.AddDependencyAs(someSystemInstance); // ...or registers it under a specific interface/base type instead container.AddTarget(someSystemInstance); // marks an object as needing injection container.Resolve(); // fills in [Inject] fields/methods on all added targets This part is handled by the asset's own bootstrap code (see the ``Boot`` partition) — as an asset user, you normally only write the consumer side (the ``[Inject]`` fields), not the registration/bootstrap side. Named bindings **************************************** If you ever need more than one dependency of the same type, you can bind a dependency to a specific id, and request that exact one with ``[Inject("id")]``: .. code-block:: csharp container.AddDependency(firstInstance).Bind("first"); container.AddDependency(secondInstance).Bind("second"); .. code-block:: csharp [Inject("first")] SomeType first; [Inject("second")] SomeType second; Unity integration **************************************** For ``MonoBehaviour``-based objects, there is ``UnityContainer`` (derived from ``Container``), which knows how to inject freshly spawned/instantiated objects. A ``MonoBehaviour`` opts in to this by implementing the marker interface ``IRequireInject``: .. code-block:: csharp public class SomeUnitComponent : MonoBehaviour, IRequireInject { [Inject] GameplayService gameplay; } .. code-block:: csharp // somewhere in spawning code var instance = unityContainer.Spawn(prefab, position, rotation); // instantiates the prefab and injects all IRequireInject components on it If your ``IRequireInject`` component was created some other way (not through ``Spawn``), you can inject it directly: .. code-block:: csharp unityContainer.ApplyTo(gameObject); // finds all IRequireInject components on the GameObject and injects them .. note:: If a class should **not** receive field injection at all (for example, it only uses method injection), it can implement ``INoFieldsInject`` to opt out of field scanning. .. toctree::