| src | ||
| .gitattributes | ||
| .gitignore | ||
| DragonECS-AutoInjections.asmdef | ||
| DragonECS-AutoInjections.asmdef.meta | ||
| LICENSE.md | ||
| LICENSE.md.meta | ||
| package.json | ||
| package.json.meta | ||
| README-RU.md | ||
| README-RU.md.meta | ||
| README.md | ||
| README.md.meta | ||
| src.meta | ||
Auto Injections for 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.
Оглавление
- Installation
- Integration
- Dependency Injection
- Auto Builder for aspects
- Auto Runners
- Code Example
- Non-null injections
Installation
Versioning semantics - Open
Environment
Requirements:
- Dependency: 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://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:
_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<T> interface. Fields marked with this attribute automatically receive dependencies injected into the pipeline. Example:
[DI] EcsDefaultWorld _world;
Injection can also be done via a property or method:
EcsDefaultWorld _world;
//Обязательно наличие set блока.
[DI] EcsDefaultWorld World { set => _world = value; }
//Количество аргументов должно быть равно 1.
[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 toInclude<T>());[Exc]- caches the pool and adds the component type to the exclude constraint (equivalent toExclude<T>());[Opt]- only caches the pool (equivalent toOptional<T>);
Attribute for combining aspects:
[Combine(order)]- caches the aspect and merges constraints from aspects (equivalent toCombine<TOtherAspect>(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 toInclude<T>());[ExcImplicit(type)]- adds Type from the constructor to the exclude constraint (equivalent toExclude<T>());
Auto Runners
To obtain runners without adding them manually, use [BindWithRunner(type)] and GetRunnerAuto<T>().
[BindWithRunner(typeof(DoSomethingProcessRunner))]
interface IDoSomethingProcess : IEcsProcess
{
void Do();
}
//Реализация раннера. Пример реализации можно так же посмотреть в встроенных процессах
sealed class DoSomethingProcessRunner : EcsRunner<IDoSomethingProcess>, IDoSomethingProcess
{
public void Do()
{
foreach (var item in Process) item.Do();
}
}
//...
// Если в пайплайн не был добавлен раннер, то GetRunnerAuto автоматически добавит экземпляр DoSomethingProcessRunner.
_pipeline.GetRunnerAuto<IDoSomethingProcess>().Do();
Code Example
class VelocitySystemDI : IEcsRun
{
class Aspect : EcsAspectAuto
{
[ExcImplicit(typeof(FreezedTag))]
[Inc] public EcsPool<Pose> poses;
[Inc] public EcsPool<Velocity> 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
class VelocitySystem : IEcsRun, IEcsInject<EcsDefaultWorld>, IEcsInject<TimeService>
{
class Aspect : EcsAspect
{
public EcsPool<Pose> poses;
public EcsPool<Velocity> velocities;
public Aspect(Builder b)
{
b.Exclude<FreezedTag>();
poses = b.Include<Pose>();
velocities = b.Include<Velocity>();
}
}
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.