Init(args) has built-in support for the Entity Component System (ECS).
Systems
You can register a system as a global service by adding the [Service] attribute to its type. This makes it possible for clients – including all other services that have the [Service] attribute – to receive the system automatically during their initialization.
Containing World
You can specify the world that should contain the system by assigning the name of the world into the World property of the [Service] attribute.
[Service(World = "Gameplay")]
partial class PlayerSystem : SystemBase
{
protected override void OnUpdate() { }
}
In the system’s world has not been specified, then it will be created in / located from the default game object injection world.
Initialization
If an existing instance of the system is found in the world that should contain it, the existing instance will be registered as the global service; otherwise, a new instance will be created automatically by Init(args) during initialization and added into the World.
By default, Unity will automatically create instances of all system types found in the project. You can disable this behaviour, and allow Init(args) to take over the responsibility for the creation of a system, by adding the [DisableAutoCreation] attribute to a system’s type. You can also disable the behaviour for all systems inside an assembly by adding the attribute to the assembly, or disable the behaviour for your entire project by adding the scripting define UNITY_DISABLE_AUTOMATIC_SYSTEM_BOOTSTRAP_RUNTIME_WORLD in your Project Settings.
Dependencies
Systems that are global services can receive other global services that they depend on as constructor arguments:
[Service, DisableAutoCreation]
partial class EnemySystem : SystemBase
{
PlayerSystem playerSystem;
public EnemySystem(PlayerSystem playerSystem)
=> this.playerSystem = playerSystem;
protected override void OnUpdate() { }
}
Alternatively, they can implement IInitializable<T…> and receive the service that they depend on as Init method arguments:
[Service, DisableAutoCreation]
partial class EnemySystem : SystemBase, IInitializable<PlayerSystem>
{
PlayerSystem playerSystem;
public void Init(PlayerSystem playerSystem)
=> this.playerSystem = playerSystem;
protected override void OnUpdate() { }
}
Note that if auto-creation has not been disabled for a system, then its OnCreate method will be executed before the system has received the services that it depends on:
[Service]
partial class EnemySystem : SystemBase, IInitializable<PlayerSystem>
{
PlayerSystem playerSystem;
protected override void OnCreate() => Debug.Log("1");
public void Init(PlayerSystem playerSystem) => Debug.Log("2");
protected override void OnStartRunning() => Debug.Log("3");
protected override void OnUpdate() => Debug.Log("4");
}
Unmanaged Systems
It is possible to create global services from unmanaged systems (structs that implement ISystem) in addition to managed systems (classes that derive from SystemBase). However, due to the nature of structs, there is a major caveat with creating services from unmanaged systems: if a client receives an unmanaged system as a constructor or Init method argument, they will actually receive a new copy of the system. This means that any changes done to the state of the system will not affect other copies of the system – including the one that is attached to a world and being updated internally by Unity. As such, it’s generally best to only create global services from managed systems.
Worlds
Worlds can also be registered as global services.
You can register a class that derives from World as a global service by adding the [Service] attribute to it.
World Name
You can optionally specify the name of the world by assigning it into the World property of the [Service] attribute. If you also define a constructor with a string-type name parameter, then Init(args) can execute the constructor with the world’s name passed as the argument:
[Service(World = "Gameplay")]
class GameplayWorld : World, IInitializable<GameSettings>
{
public GameplayWorld(string name) : base(name) { }
}
If you define a constructor with a string-type name parameter, but don’t assign any name into the World property of the [Service] attribute, then Init(args) will provide the type’s name as the constructor argument automatically.
[Service]
class GameplayWorld : World, IInitializable<GameSettings>
{
public GameplayWorld(string name) : base(name) { } // <- name: "GameplayWorld"
}
Dependencies
Worlds that are global services can receive other global services that they depend on as constructor arguments:
[Service]
class GameplayWorld : World
{
GameSettings settings;
public GameplayWorld(GameSettings settings) : base("Gameplay", WorldFlags.Game)
=> this.settings = settings;
}
Alternatively, they can implement IInitializable<T…> and receive the service that they depend on as Init method arguments:
[Service(World = "Gameplay")]
class GameplayWorld : World, IInitializable<GameSettings>
{
GameSettings settings;
public GameplayWorld(string name) : base(name) { }
public void Init(GameSettings settings)
=> this.settings = settings;
}
All world services are created before any system services by default.