Version License Discord QQ

# Auto Injections for [DragonECS](https://github.com/DCFApixels/DragonECS)
Readme Languages:

Русский

English(WIP)

The extension is designed to reduce the amount of code by simplifying dependency injection by doing injections automatically. > [!WARNING] > The project is a work in progress, API may change. > > While the English version of the README is incomplete, you can view the [Russian version](https://github.com/DCFApixels/DragonECS-AutoInjections/blob/main/README-RU.md). # Оглавление - [Installation](#Installation) - [Integration](#Integration) - [Dependency Injection](#Dependency-Injection) - [Auto Builder for aspects](#Auto-Builder-for-aspects) - [Auto Runners](#Auto-Runners) - [Code Example](#Code-Example) - [Non-null injections](#Non-null-injections)
# Installation Versioning semantics - [Open](https://gist.github.com/DCFApixels/af79284955bf40e9476cdcac79d7b098#file-dcfapixels_versioning-md) ## Environment Requirements: + Dependency: [DragonECS](https://github.com/DCFApixels/DragonECS) + Minimum version of C# 7.3; Optional: * Game engines with C#: Unity, Godot, MonoGame, etc. Tested with: * **Unity:** Minimum version 2021.2.0. ## Unity Installation * ### Unity Package The package supports installation as a Unity package by adding the Git URL [in the PackageManager](https://docs.unity3d.com/2023.2/Documentation/Manual/upm-ui-giturl.html): ``` https://github.com/DCFApixels/DragonECS-AutoInjections.git ``` Or add the package entry to `Packages/manifest.json`: ``` "com.dcfa_pixels.dragonecs-auto_injections": "https://github.com/DCFApixels/DragonECS-AutoInjections.git", ``` * ### Source Code The package source code can also be copied directly into the project.
# Integration Add the AutoInject() call to the pipeline Builder. Example: ```c# _pipeline = EcsPipeline.New() .Inject(world) .Inject(_timeService) .Add(new TestSystem()) .Add(new VelocitySystem()) .Add(new ViewSystem()) .AutoInject() // Done — automatic injections enabled .BuildAndInit(); ``` > [!IMPORTANT] > Ensure AutoInject() is called during initialization; otherwise nothing will work. # Dependency Injection The `[DI]` attribute replaces the `IEcsInject` interface. Fields marked with this attribute automatically receive dependencies injected into the pipeline. Example: ```c# [DI] EcsDefaultWorld _world; ``` Injection can also be done via a property or method: ```c# EcsDefaultWorld _world; // A set accessor is required. [DI] EcsDefaultWorld World { set => _world = value; } // Methods must have exactly one argument. [DI] void InjectWorld(EcsDefaultWorld world) => _world = world; ``` > Aggressive injection (without the `[DI]` attribute) is enabled by calling `.AutoInject(true)`.
# Auto Builder for aspects AutoInjections also simplifies building aspects. The following attributes are available: Attributes for initializing pool fields: * `[Inc]` - caches the pool and adds the component type to the include constraint of the aspect (equivalent to `Inc()`); * `[Exc]` - caches the pool and adds the component type to the exclude constraint (equivalent to `Exc()`); * `[Opt]` - only caches the pool (equivalent to `Opt`); * Attribute for combining aspects: * `[Combine(order)]` - caches the aspect and merges constraints from aspects (equivalent to `Combine(int)`); order sets combine order (default 0); Additional attributes for specifying aspect constraints. They can be applied to the aspect itself or any field inside: * `[IncImplicit(type)]` - adds Type from the constructor to the include constraint (equivalent to `Inc()`); * `[ExcImplicit(type)]` - adds Type from the constructor to the exclude constraint (equivalent to `Exc()`); To initialize an aspect, it is not necessary to inherit from EcsAspect. Example: ```c# class Aspect { [ExcImplicit(typeof(FreezedTag))] [Inc] public EcsPool poses; [Inc] public EcsPool velocities; } ```
# Auto Runners To obtain runners without adding them manually, use `[BindWithRunner(type)]` and `GetRunnerAuto()`. ```c# [BindWithRunner(typeof(DoSomethingProcessRunner))] interface IDoSomethingProcess : IEcsProcess { void Do(); } // Runner implementation. See built-in processes for example sealed class DoSomethingProcessRunner : EcsRunner, IDoSomethingProcess { public void Do() { foreach (var item in Process) item.Do(); } } //... // If the runner wasn't added to the pipeline, GetRunnerAuto will automatically add an instance of DoSomethingProcessRunner. _pipeline.GetRunnerAuto().Do(); ```
# Code Example ```c# class VelocitySystemDI : IEcsRun { class Aspect { [ExcImplicit(typeof(FreezedTag))] [Inc] public EcsPool poses; [Inc] public EcsPool velocities; } [DI] EcsDefaultWorld _world; [DI] TimeService _time; public void Run() { foreach (var e in _world.Where(out Aspect a)) { a.poses.Get(e).position += a.velocities.Read(e).value * _time.DeltaTime; } } } ```
Same code but without AutoInjections ```c# class VelocitySystem : IEcsRun, IEcsInject, IEcsInject { class Aspect : EcsAspect { public EcsPool poses; public EcsPool velocities; public Aspect(Builder b) { b.Exc(); poses = b.Inc(); velocities = b.Inc(); } } EcsDefaultWorld _world; TimeService _time; public void Inject(EcsDefaultWorld obj) => _world = obj; public void Inject(TimeService obj) => _time = obj; public void Run() { foreach (var e in _world.Where(out Aspect a)) { a.poses.Get(e).position += a.velocities.Read(e).value * _time.DeltaTime; } } } ```

# Non-null injections To ensure a field marked with `[DI]` is initialized even if injection does not occur, pass a fallback type to the attribute constructor. In the example below the field `Foo` will receive the injected `Foo` instance or an instance of `FooDummy : Foo` if injection was not performed. > The provided type must have a parameterless constructor and be either the same type as the field or derived from it. The extension will also report if any `[DI]`-marked fields remain uninitialized after the pre-injection phase.
# License The MIT License: [Open](LICENSE.md)