2023-03-29 00:21:25 +08:00
< p align = "center" >
2024-03-11 01:50:50 +08:00
< img width = "660" src = "https://github.com/DCFApixels/DragonECS/assets/99481254/c09e385e-08c1-4c04-904a-36ad7e25e45b" >
2023-03-29 00:21:25 +08:00
< / p >
2024-06-16 01:22:16 +08:00
<!-- <a href="https://discord.gg/kqmJjExuCf"><img alt="Discord" src="https://img.shields.io/discord/1111696966208999525?color=%2300b269&label=Discord&logo=Discord&logoColor=%23ffffff&style=for - the - badge"></a> -->
2023-05-29 03:26:53 +08:00
< p align = "center" >
2023-05-29 03:38:37 +08:00
< img alt = "Version" src = "https://img.shields.io/github/package-json/v/DCFApixels/DragonECS?color=%23ff4e85&style=for-the-badge" >
2023-05-31 04:52:18 +08:00
< img alt = "License" src = "https://img.shields.io/github/license/DCFApixels/DragonECS?color=ff4e85&style=for-the-badge" >
2024-06-16 01:08:25 +08:00
< a href = "https://discord.gg/kqmJjExuCf" > < img alt = "Discord" src = "https://img.shields.io/badge/Discord-JOIN-00b269?logo=discord&logoColor=%23ffffff&style=for-the-badge" > < / a >
< a href = "http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=IbDcH43vhfArb30luGMP1TMXB3GCHzxm&authKey=s%2FJfqvv46PswFq68irnGhkLrMR6y9tf%2FUn2mogYizSOGiS%2BmB%2B8Ar9I%2Fnr%2Bs4oS%2B&noverify=0&group_code=949562781" > < img alt = "QQ" src = "https://img.shields.io/badge/QQ-JOIN-00b269?logo=tencentqq&logoColor=%23ffffff&style=for-the-badge" > < / a >
2023-05-29 03:26:53 +08:00
< / p >
2023-03-29 00:21:25 +08:00
# DragonECS - C# Entity Component System Framework
2024-08-01 10:55:29 +08:00
2024-08-01 09:58:11 +08:00
< table >
2024-08-01 10:33:37 +08:00
< tr > < / tr >
2024-08-01 09:58:11 +08:00
< tr >
2024-08-01 10:33:37 +08:00
< td colspan = "3" > Readme Languages:< / td >
2024-08-01 09:58:11 +08:00
< / tr >
2024-08-01 10:33:37 +08:00
< tr > < / tr >
2024-08-01 09:58:11 +08:00
< tr >
2024-08-01 10:33:37 +08:00
< td nowrap width = "100" >
2026-03-13 20:44:02 +08:00
< a href = "README-RU.md" >
2026-03-18 13:25:34 +08:00
< img src = "https://github.com/user-attachments/assets/7bc29394-46d6-44a3-bace-0a3bae65d755" > < br / >
2024-08-01 10:33:37 +08:00
< span > Русский< / span >
2024-08-01 09:58:11 +08:00
< / a >
< / td >
2024-08-01 10:33:37 +08:00
< td nowrap width = "100" >
2024-08-01 09:58:11 +08:00
< a href = "https://github.com/DCFApixels/DragonECS" >
2026-03-18 13:25:34 +08:00
< img src = "https://github.com/user-attachments/assets/3c699094-f8e6-471d-a7c1-6d2e9530e721" > < br / >
2024-08-08 10:11:39 +08:00
< span > English< / span >
2024-08-01 09:58:11 +08:00
< / a >
< / td >
2024-08-01 10:33:37 +08:00
< td nowrap width = "100" >
2026-03-13 20:44:02 +08:00
< a href = "README-ZH.md" >
2026-03-18 13:25:34 +08:00
< img src = "https://github.com/user-attachments/assets/8e598a9a-826c-4a1f-b842-0c56301d2927" > < br / >
2024-08-01 10:33:37 +08:00
< span > 中文< / span >
2024-08-01 09:58:11 +08:00
< / a >
< / td >
< / tr >
< / table >
2023-05-30 18:20:16 +08:00
2026-03-29 17:11:17 +08:00
The [ECS ](https://en.wikipedia.org/wiki/Entity_component_system ) Framework aims to maximize usability, modularity, extensibility and performance for dynamic entity changes, without code generation or external dependencies. Inspired by [LeoEcs Lite ](https://github.com/Leopotam/ecslite ).
2023-12-31 23:56:08 +08:00
2023-12-22 17:31:52 +08:00
> [!WARNING]
2026-03-29 17:11:17 +08:00
> The project is a work in progress; the API may change.
>
2024-10-19 16:23:01 +08:00
> The most current version of the README is in [Russian version](https://github.com/DCFApixels/DragonECS/blob/main/README-RU.md).
>
> If there are unclear points, you can ask questions here [Feedback](#Feedback)
2023-03-13 05:43:27 +08:00
2024-08-24 21:43:19 +08:00
## Table of Contents
2024-11-11 14:26:47 +08:00
- [Installation ](#installation )
2026-03-18 13:25:34 +08:00
- [Extensions ](#extensions )
2024-11-11 14:26:47 +08:00
- [Basic Concepts ](#basic-concepts )
2024-08-04 00:03:30 +08:00
- [Entity ](#entity )
- [Component ](#component )
- [System ](#system )
2024-11-11 14:26:47 +08:00
- [Framework Concepts ](#framework-concepts )
- [Pipeline ](#pipeline )
- [Building ](#building )
- [Dependency Injection ](#dependency-injection )
- [Modules ](#modules )
- [Sorting ](#sorting )
2024-08-04 00:03:30 +08:00
- [Processes ](#Processes )
- [World ](#World )
- [Pool ](#Pool )
2024-11-11 14:26:47 +08:00
- [Mask ](#mask )
- [Aspect ](#aspect )
- [Queries ](#queries )
- [Collections ](#collections )
2024-08-04 00:03:30 +08:00
- [ECS Root ](#ecs-root )
- [Debug ](#debug )
2024-11-11 14:26:47 +08:00
- [Meta Attributes ](#meta-attributes )
2024-08-04 00:03:30 +08:00
- [EcsDebug ](#ecsdebug )
2024-11-11 14:26:47 +08:00
- [Profiling ](#profiling )
2024-08-04 00:03:30 +08:00
- [Define Symbols ](#define-symbols )
2024-11-11 14:26:47 +08:00
- [Framework Extension Tools ](#framework-extension-tools )
- [World Components ](#world-components )
- [Configs ](#configs )
- [Projects powered by DragonECS ](#projects-powered-by-dragonecs )
- [Extensions ](#extensions )
2024-08-04 00:03:30 +08:00
- [FAQ ](#faq )
2024-11-11 14:26:47 +08:00
- [Feedback ](#feedback )
2026-03-29 18:07:01 +08:00
- [License ](#license )
2024-08-04 00:03:30 +08:00
2024-03-07 08:15:56 +08:00
< / br >
# Installation
2026-03-28 23:54:47 +08:00
Versioning semantics - [Open ](https://gist.github.com/DCFApixels/af79284955bf40e9476cdcac79d7b098#file-dcfapixels_versioning-md )
2024-03-07 08:15:56 +08:00
## Environment
Requirements:
2026-03-18 13:25:34 +08:00
* Minimum C# version: 7.3.
2026-03-18 13:36:52 +08:00
Supported:
* NativeAOT;
2026-03-18 13:25:34 +08:00
* Game engines with C#: Unity, Godot, MonoGame, etc.
2024-03-09 00:18:35 +08:00
Tested with:
2026-03-18 13:25:34 +08:00
* **Unity:** Minimum version 2021.2.0.
2024-03-07 08:15:56 +08:00
## Unity Installation
2026-03-18 13:36:52 +08:00
> It is also recommended to install the Unity engine integration extension [Unity integration](https://github.com/DCFApixels/DragonECS-Unity)
* ### Unity package
2026-03-29 17:11:17 +08:00
Installation as a Unity package is supported by adding the Git URL [in PackageManager ](https://docs.unity3d.com/2023.2/Documentation/Manual/upm-ui-giturl.html ):
2024-03-07 08:15:56 +08:00
```
2024-03-11 00:27:20 +08:00
https://github.com/DCFApixels/DragonECS.git
2024-03-07 08:15:56 +08:00
```
2026-03-29 17:11:17 +08:00
Or add the package entry to `Packages/manifest.json` :
2026-03-18 13:36:52 +08:00
```
"com.dcfa_pixels.dragonecs": "https://github.com/DCFApixels/DragonECS.git",
```
* ### As source code
2026-03-29 01:49:35 +08:00
The framework source code can also be copied directly into the project.
2024-03-07 08:15:56 +08:00
2026-03-18 13:25:34 +08:00
# Extensions
* Integrations:
* [Unity ](https://github.com/DCFApixels/DragonECS-Unity )
* [Godot ](https://gitlab.com/InauniusOwn/Libraries/DraGodot )
* Packages:
* [Dependency autoinjections ](https://github.com/DCFApixels/DragonECS-AutoInjections )
* [Classic C# multithreading ](https://github.com/DCFApixels/DragonECS-ClassicThreads )
* [Recursivity ](https://github.com/DCFApixels/DragonECS-Recursivity )
* [Hybrid ](https://github.com/DCFApixels/DragonECS-Hybrid )
* [Graphs ](https://github.com/DCFApixels/DragonECS-Graphs )
* Utilities:
* [Simple syntax ](https://gist.github.com/DCFApixels/d7bfbfb8cb70d141deff00be24f28ff0 )
* [EcsRefPool ](https://gist.github.com/DCFApixels/73e392ccabdd98b3d4a517017d8a3f22 )
* [Timers ](https://gist.github.com/DCFApixels/71a416275660c465ece76242290400df )
* [One-Frame Components ](https://gist.github.com/DCFApixels/46d512dbcf96c115b94c3af502461f60 )
* [Code Templates for IDE ](https://gist.github.com/ctzcs/0ba948b0e53aa41fe1c87796a401660b ) and [for Unity ](https://gist.github.com/ctzcs/d4c7730cf6cd984fe6f9e0e3f108a0f1 )
2026-03-29 17:11:17 +08:00
> *Your extension? Extensions for DragonECS are welcome, you can share it [here](#feedback).
2024-03-07 08:15:56 +08:00
2024-08-04 00:03:30 +08:00
# Basic Concepts
## Entity
2026-03-29 17:11:17 +08:00
Container for components. Two identifier types are used to reference entities:
2026-03-18 13:25:34 +08:00
* `int` - short-lived identifier, valid within a single tick. Not recommended for long-term storage;
2026-03-29 17:11:17 +08:00
* `entlong` - long-term identifier that includes a generation tag, making it unique across entity lifetimes. Suitable for long-term storage.
2026-03-18 13:25:34 +08:00
```c#
2024-08-04 00:03:30 +08:00
// Creating a new entity in the world.
int entityID = _world.NewEntity();
// Deleting an entity.
_world.DelEntity(entityID);
// Copying components from one entity to another.
_world.CopyEntity(entityID, otherEntityID);
// Cloning an entity.
int newEntityID = _world.CloneEntity(entityID);
```
< details >
< summary > Working with entlong< / summary >
2026-03-18 13:25:34 +08:00
```c#
2024-08-04 00:03:30 +08:00
// Convert int to entlong.
entlong entity = _world.GetEntityLong(entityID);
// or
entlong entity = (_world, entityID);
// Check that the entity is still alive.
if (entity.IsAlive) { }
// Converting entlong to int. Throws an exception if the entity no longer exists.
int entityID = entity.ID;
// or
var (entityID, world) = entity;
// Converting entlong to int. Returns true and the int identifier if the entity is still alive.
if (entity.TryGetID(out int entityID)) { }
```
< / details >
2026-03-29 17:11:17 +08:00
> Entities cannot exist without components. Removing the last component automatically deletes the entity along with it.
2024-08-04 00:03:30 +08:00
## Component
2024-11-11 14:26:47 +08:00
Data for entities.
2024-08-04 00:03:30 +08:00
```c#
2024-11-11 14:26:47 +08:00
// IEcsComponent components are stored in regular storage.
2024-08-04 00:03:30 +08:00
struct Health : IEcsComponent
{
public float health;
public int armor;
}
2024-11-11 14:26:47 +08:00
// Components with IEcsTagComponent are stored in tag-optimized storage.
2024-08-04 00:03:30 +08:00
struct PlayerTag : IEcsTagComponent {}
```
## System
2026-03-29 17:11:17 +08:00
Represents the core logic defining entity behaviors. Systems are implemented as user-defined classes that implement one or more process interfaces. Key processes include:
2024-08-04 00:03:30 +08:00
```c#
class SomeSystem : IEcsPreInit, IEcsInit, IEcsRun, IEcsDestroy
{
// Called once during EcsPipeline.Init() and before IEcsInit.Init().
public void PreInit () { }
// Called once during EcsPipeline.Init() and after IEcsPreInit.PreInit().
public void Init () { }
// Called each time during EcsPipeline.Run().
public void Run () { }
// Called once during EcsPipeline.Destroy().
public void Destroy () { }
}
```
> For implementing additional processes, refer to the [Processes](#Processes) section.
< / br >
# Framework Concepts
## Pipeline
2026-03-29 17:11:17 +08:00
Container and engine of systems. Responsible for setting up the system call queue, providing mechanisms for communication between systems, and dependency injection. Implemented as the `EcsPipeline` class.
2024-08-04 00:03:30 +08:00
### Building
2026-03-29 17:11:17 +08:00
Builder is responsible for building the pipeline. Systems are added to the Builder, and the pipeline is built at the end. Example:
2024-08-04 00:03:30 +08:00
```c#
2026-03-29 17:11:17 +08:00
EcsPipeline pipeline = EcsPipeline.New() // Creates a pipeline builder.
2024-08-04 00:03:30 +08:00
// Adds System1 to the systems queue.
.Add(new System1())
// Adds System2 to the queue after System1.
.Add(new System2())
// Adds System3 to the queue after System2, as a unique instance.
.AddUnique(new System3())
// Completes the pipeline building and returns its instance.
.Build();
pipeline.Init(); // Initializes the pipeline.
```
```c#
class SomeSystem : IEcsRun, IEcsPipelineMember
{
// Gets the pipeline instance to which the system belongs.
public EcsPipeline Pipeline { get ; set; }
public void Run () { }
}
```
2026-03-29 17:11:17 +08:00
> For simultaneous building and initialization, use `Builder.BuildAndInit()`.
2024-08-04 00:03:30 +08:00
### Dependency Injection
2026-03-29 17:11:17 +08:00
The framework implements dependency injection for systems. Injection process occurs during pipeline initialization and injects data passed to the Builder.
> Built-in Dependency injection is optional.
2026-03-18 13:25:34 +08:00
```c#
2024-08-04 00:03:30 +08:00
class SomeDataA { /* ... */ }
class SomeDataB : SomeDataA { /* ... */ }
// ...
SomeDataB _someDataB = new SomeDataB();
EcsPipeline pipeline = EcsPipeline.New()
// ...
// Injects _someDataB into systems implementing IEcsInject< SomeDataB > .
.Inject(_someDataB)
// Adds systems implementing IEcsInject< SomeDataA > to the injection tree,
// now these systems will also receive _someDataB.
.Injector.AddNode< SomeDataA > ()
// ...
.Add(new SomeSystem())
// ...
.BuildAndInit();
// ...
// Injection uses the interface IEcsInject< T > and its method Inject(T obj).
class SomeSystem : IEcsInject< SomeDataA > , IEcsRun
{
2026-03-29 17:11:17 +08:00
SomeDataA _someDataA;
2024-08-04 00:03:30 +08:00
// obj will be an instance of type SomeDataB.
public void Inject(SomeDataA obj) => _someDataA = obj;
public void Run ()
{
_someDataA.DoSomething();
}
}
```
### Modules
2026-03-29 17:11:17 +08:00
Groups of systems that implement a common feature can be grouped into modules and added to the Pipeline.
2026-03-18 13:25:34 +08:00
```c#
2024-08-04 00:03:30 +08:00
using DCFApixels.DragonECS;
class Module1 : IEcsModule
{
public void Import(EcsPipeline.Builder b)
{
b.Add(new System1());
b.Add(new System2());
b.AddModule(new Module2());
// ...
}
}
```
``` c#
EcsPipeline pipeline = EcsPipeline.New()
// ...
.AddModule(new Module1())
// ...
.BuildAndInit();
```
2024-11-11 14:26:47 +08:00
### Sorting
2026-03-29 17:11:17 +08:00
To manage system order in the pipeline regardless of addition order, use Layers and Sorting Order.
2024-11-11 14:26:47 +08:00
#### Layers
2026-03-29 17:11:17 +08:00
Queues of systems can be segmented into layers. A layer defines a position in the queue for inserting systems. For example, to insert a system at the end of the queue regardless of where it is added, add it to the `EcsConsts.END_LAYER` layer.
2024-08-04 00:03:30 +08:00
``` c#
const string SOME_LAYER = nameof(SOME_LAYER);
EcsPipeline pipeline = EcsPipeline.New()
// ...
// Inserts a new layer before the end layer EcsConsts.END_LAYER
.Layers.Insert(EcsConsts.END_LAYER, SOME_LAYER)
// System SomeSystem will be added to the SOME_LAYER layer
2026-03-29 17:11:17 +08:00
.Add(new SomeSystem(), SOME_LAYER)
2024-08-04 00:03:30 +08:00
// ...
.BuildAndInit();
```
The built-in layers are arranged in the following order:
2026-03-18 13:25:34 +08:00
* `EcsConsts.PRE_BEGIN_LAYER`
* `EcsConsts.BEGIN_LAYER`
* `EcsConsts.BASIC_LAYER` (Systems are added here if no layer is specified during addition)
* `EcsConsts.END_LAYER`
* `EcsConsts.POST_END_LAYER`
2024-11-11 14:26:47 +08:00
#### Sorting Order
2026-03-29 17:11:17 +08:00
The sort order integer value is used to sort systems within a layer. By default, systems are added with `sortOrder = 0` .
2024-11-11 14:26:47 +08:00
```c#
EcsPipeline pipeline = EcsPipeline.New()
// ...
// System SomeSystem will be inserted into the layer EcsConsts.BEGIN_LAYER
// and placed after systems with sortOrder less than 10.
2026-03-29 17:11:17 +08:00
.Add(new SomeSystem(), EcsConsts.BEGIN_LAYER, 10)
2024-11-11 14:26:47 +08:00
// ...
.BuildAndInit();
```
2024-08-04 00:03:30 +08:00
## Processes
2026-03-29 17:11:17 +08:00
Processes are queues of systems that implement a common interface, such as `IEcsRun` . Runners are used to start processes. Built-in processes are started automatically; custom processes can be implemented.
2024-08-04 00:03:30 +08:00
< details >
< summary > Built-in processes< / summary >
* `IEcsPreInit` , `IEcsInit` , `IEcsRun` , `IEcsDestroy` - lifecycle processes of `EcsPipeline` .
* `IEcsInject<T>` - [Dependency Injection ](#Dependency-Injection ) processes.
2026-03-29 17:11:17 +08:00
* `IOnInitInjectionComplete` - Similar to the [Dependency Injection ](#Dependency-Injection ) process, but signals completion of initialization injection.
2024-08-04 00:03:30 +08:00
< / details >
< details >
< summary > Custom Processes< / summary >
2026-03-29 17:11:17 +08:00
To add a new process, create an interface inheriting from `IEcsProcess` and create a runner for it. A runner is a class that implements the process interface and inherits from `EcsRunner<TInterface>` . Example:
2024-08-04 00:03:30 +08:00
``` c#
2024-08-07 11:11:48 +08:00
// Process interface.
2024-08-04 00:03:30 +08:00
interface IDoSomethingProcess : IEcsProcess
{
void Do();
}
2026-03-29 17:11:17 +08:00
// Runner implementation. An example of implementation can also be seen in built-in processes.
2024-08-04 00:03:30 +08:00
sealed class DoSomethingProcessRunner : EcsRunner< IDoSomethingProcess > , IDoSomethingProcess
{
public void Do()
{
foreach (var item in Process) item.Do();
}
}
2024-11-11 14:26:47 +08:00
```
``` c#
2024-08-07 11:11:48 +08:00
// Adding the runner when creating the pipeline
2024-08-04 00:03:30 +08:00
_pipeline = EcsPipeline.New()
//...
.AddRunner< DoSomethingProcessRunner > ()
//...
.BuildAndInit();
2024-08-07 11:11:48 +08:00
// Running the runner if it was added
2026-03-18 13:25:34 +08:00
_pipeline.GetRunner< IDoSomethingProcess > ().Do();
2024-08-04 00:03:30 +08:00
2024-08-07 11:11:48 +08:00
// or if the runner was not added (calling GetRunnerInstance will also add the runner to the pipeline).
2026-03-18 13:25:34 +08:00
_pipeline.GetRunnerInstance< DoSomethingProcessRunner > ().Do();
2024-08-04 00:03:30 +08:00
```
2024-11-11 14:26:47 +08:00
< details >
< summary > Advanced Implementation of a Runner< / summary >
``` c#
internal sealed class DoSomethingProcessRunner : EcsRunner< IDoSomethingProcess > , IDoSomethingProcess
{
// RunHelper simplifies the implementation similar to the built-in processes implementation.
// It automatically triggers the profiler marker and also includes a try-catch block.
private RunHelper _helper;
protected override void OnSetup()
{
2026-03-29 17:11:17 +08:00
// The second argument specifies the name of the marker; if not specified, the name will be chosen automatically.
2024-11-11 14:26:47 +08:00
_helper = new RunHelper(this, nameof(Do));
}
public void Do()
{
_helper.Run(p => p.Do());
}
}
```
< / details >
2026-03-29 17:11:17 +08:00
> Runner requirements:
2024-08-07 11:11:48 +08:00
> * Inheritance from `EcsRunner<T>` must be direct.
2026-03-29 17:11:17 +08:00
> * A runner may only contain one interface (excluding `IEcsProcess`).
> * The inheriting class of `EcsRunner<T>` must also implement the `T` interface.
2024-11-11 14:26:47 +08:00
2024-08-04 00:03:30 +08:00
< / details >
## World
2026-03-29 17:11:17 +08:00
A container for entities and components.
2024-08-04 00:03:30 +08:00
``` c#
// Creating an instance of the world.
_world = new EcsDefaultWorld();
// Creating and deleting an entity as shown in the Entities section.
var e = _world.NewEntity();
_world.DelEntity(e);
```
2026-03-29 17:11:17 +08:00
> **NOTICE:** Call `EcsWorld.Destroy()` on the world instance when it is no longer needed to release resources. Otherwise it will remain in memory.
2024-08-04 00:03:30 +08:00
### World Configuration
2026-03-29 17:11:17 +08:00
To initialize the world with a required size upfront and reduce warm-up time, pass an `EcsWorldConfig` instance to the constructor.
2024-08-04 00:03:30 +08:00
``` c#
EcsWorldConfig config = new EcsWorldConfig(
// Pre-initializes the world capacity for 2000 entities.
entitiesCapacity: 2000,
// Pre-initializes the pools capacity for 2000 components.
poolComponentsCapacity: 2000);
_world = new EcsDefaultWorld(config);
```
## Pool
2026-03-29 17:11:17 +08:00
Stash of components, providing methods for adding, reading, editing, and removing components on entities. Several pool types are available:
* `EcsPool` - universal pool for struct components implementing `IEcsComponent` ;
* `EcsTagPool` - pool optimized for tag components implementing `IEcsTagComponent` ;
2024-08-04 00:03:30 +08:00
2026-03-29 17:11:17 +08:00
Pools provide 5 common methods and their variations:
2024-08-04 00:03:30 +08:00
``` c#
// One way to get a pool from the world.
EcsPool< Pose > poses = _world.GetPool< Pose > ();
// Adds component to entity, throws an exception if the entity already has the component.
ref var addedPose = ref poses.Add(entityID);
2026-03-29 17:11:17 +08:00
// Returns existing component, throws an exception if the entity does not have this component.
ref var gottenPose = ref poses.Get(entityID);
2024-08-04 00:03:30 +08:00
// Returns a read-only component, throwing an exception if the entity does not have this component.
ref readonly var readonlyPose = ref poses.Read(entityID);
// Returns true if the entity has the component, otherwise false.
if (poses.Has(entityID)) { /* ... */ }
// Removes component from entity, throws an exception if the entity does not have this component.
poses.Del(entityID);
```
2026-03-29 17:11:17 +08:00
> [!WARNING]
> Exceptions are disabled in the `Release` build.
> There are "Safe" methods that first check presence or absence of a component are prefixed with `Try`.
< details >
< summary > Custom Pools< / summary >
A pool can be any type that implements the IEcsPoolImplementation< T > interface and has a parameterless constructor.
Key points when implementing a pool:
* For an example of a pool implementation, reference can be made to the implementation of the built-in EcsPool< T > .
* The IEcsPoolImplementation interface and its members are not intended for public use; the interface members should be implemented explicitly.
* The type T substituted in the IEcsPoolImplementation< T > interface and the type returned by the ComponentType and ComponentTypeID properties must match.
* All pool changes must be registered with the EcsWorld.PoolsMediator instance passed in the OnInit method.
* EcsWorld.PoolsMediator is intended for use only inside the pool.
* The DISABLE_POOLS_EVENTS define disables the implemented AddListener and RemoveListener methods.
* The static class EcsPoolThrowHelper defines throwing of the most common exception types.
* The OnReleaseDelEntityBuffer method handles cleanup of deleted entities.
* It is recommended to define an interface that marks components intended for the new pool. Based on this interface, extension methods such as GetPool< T > () can be implemented to simplify access to pools.
* Pools must implement locking. Pool locking works only in Debug mode and should throw exceptions when attempting to add or remove a component.
< / details >
2024-11-11 14:26:47 +08:00
## Mask
2026-03-29 17:11:17 +08:00
Used to filter entities by presence or absence of components. Usually masks are not used standalone, they are part of `EcsAspect` and used by queries to filter entities.
2024-11-11 14:26:47 +08:00
``` c#
// Creating a mask that checks if entities have components
// SomeCmp1 and SomeCmp2, but do not have component SomeCmp3.
EcsMask mask = EcsMask.New(_world)
// Inc - Condition for the presence of a component.
.Inc< SomeCmp1 > ()
.Inc< SomeCmp2 > ()
// Exc - Condition for the absence of a component.
.Exc< SomeCmp3 > ()
.Build();
```
< details >
< summary > Static Mask< / summary >
2026-03-29 17:11:17 +08:00
`EcsMask` is tied to specific world instances, which need to be passed to `EcsMask.New(world)` . `EcsStaticMask` can be created without a world reference.
2024-11-11 14:26:47 +08:00
``` c#
class SomeSystem : IEcsRun
{
// EcsStaticMask can be created in static fields.
static readonly EcsStaticMask _staticMask = EcsStaticMask.Inc< SomeCmp1 > ().Inc< SomeCmp2 > ().Exc< SomeCmp3 > ().Build();
// ...
}
```
``` c#
// Converting to a regular mask.
EcsMask mask = _staticMask.ToMask(_world);
```
< / details >
2024-08-04 00:03:30 +08:00
2024-08-07 11:11:48 +08:00
## Aspect
2026-03-29 17:11:17 +08:00
User-defined classes that inherit from `EcsAspect` and describe sets of components a system works with. An aspect serves two purposes:
- Mask — initializes and holds an `EcsMask` used by queries for filtering entities.
2026-03-18 13:25:34 +08:00
- Pool cache — provides fast access to component pools.
In short, an aspect is a convenient way to declare "which entities I work with and how to access their components."
2024-08-04 00:03:30 +08:00
Simplified syntax:
``` c#
using DCFApixels.DragonECS;
// ...
class Aspect : EcsAspect
{
// Caches the Pose pool and adds it to the inclusive constraint.
public EcsPool< Pose > poses = Inc;
// Caches the Velocity pool and adds it to the inclusive constraint.
public EcsPool< Velocity > velocities = Inc;
// Caches the FreezedTag pool and adds it to the exclusive constraint.
public EcsTagPool< FreezedTag > freezedTags = Exc;
// During queries, it checks for the presence of components
// in the inclusive constraint and absence in the exclusive constraint.
2026-03-29 17:11:17 +08:00
// Opt only caches the pool without affecting the mask.
2024-08-04 00:03:30 +08:00
}
```
2026-03-18 13:25:34 +08:00
Purpose of the static markers:
* `Inc` — component must be present (inclusive) and caches the pool.
* `Exc` — component must NOT be present (exclusive) and caches the pool.
2026-03-29 17:11:17 +08:00
* `Opt` — component may be present; does not affect filtering (only caches the pool).
2026-03-18 13:25:34 +08:00
* `Any` — at least one of the components marked with `Any` must be present; caches the pool.
2026-03-29 17:11:17 +08:00
< details >
< summary > Initialization via a method (the result is identical to the example above):< / summary >
2026-03-18 13:25:34 +08:00
2026-03-29 17:11:17 +08:00
Explicit syntax (equivalent result):
2024-08-04 00:03:30 +08:00
``` c#
using DCFApixels.DragonECS;
// ...
class Aspect : EcsAspect
{
public EcsPool< Pose > poses;
public EcsPool< Velocity > velocities;
protected override void Init(Builder b)
{
2026-03-18 13:25:34 +08:00
poses = b.Inc< Pose > ();
velocities = b.Inc< Velocity > ();
b.Exc< FreezedTag > ();
2024-08-04 00:03:30 +08:00
}
}
```
2026-03-29 17:11:17 +08:00
< / details >
2024-08-04 00:03:30 +08:00
< details >
< summary > Combining aspects< / summary >
2026-03-29 17:11:17 +08:00
Aspects can be combined by adding other aspects; constraints are merged accordingly.
2024-08-04 00:03:30 +08:00
``` c#
using DCFApixels.DragonECS;
// ...
class Aspect : EcsAspect
{
public OtherAspect1 otherAspect1;
public OtherAspect2 otherAspect2;
public EcsPool< Pose > poses;
protected override void Init(Builder b)
{
2026-03-29 17:11:17 +08:00
// Combines with OtherAspect1.
2024-08-04 00:03:30 +08:00
otherAspect1 = b.Combine< OtherAspect1 > (1);
2024-08-07 11:11:48 +08:00
// Although Combine was called earlier for OtherAspect1, it will first combine with OtherAspect2 because the default order is 0.
2024-08-04 00:03:30 +08:00
otherAspect2 = b.Combine< OtherAspect2 > ();
2026-03-18 13:25:34 +08:00
// If b.Exc< Pose > () was specified in OtherAspect1 or OtherAspect2, it will be replaced with b.Inc< Pose > () here.
poses = b.Inc< Pose > ();
2024-08-04 00:03:30 +08:00
}
}
```
2026-03-29 17:11:17 +08:00
If conflicting constraints exist between combined aspects, the newer constraints replace earlier ones. Constraints from the root aspect always replace constraints from added aspects. Example of constraint combination:
2026-03-18 13:25:34 +08:00
| | cmp1 | cmp2 | cmp3 | cmp4 | cmp5 | Conflict resolution |
2024-08-04 00:03:30 +08:00
| :--- | :--- | :--- | :--- | :--- | :--- |:--- |
| OtherAspect2 | :heavy_check_mark: | :x: | :heavy_minus_sign: | :heavy_minus_sign: | :heavy_check_mark: | |
2026-03-18 13:25:34 +08:00
| OtherAspect1 | :heavy_minus_sign: | :heavy_check_mark: | :heavy_minus_sign: | :x: | :heavy_minus_sign: | For `cmp2` , :heavy_check_mark: will be chosen. |
| Aspect | :x: | :heavy_minus_sign: | :heavy_minus_sign: | :heavy_minus_sign: | :heavy_check_mark: | For `cmp1` , :x: will be chosen. |
2024-08-07 11:11:48 +08:00
| Final Constraints | :x: | :heavy_check_mark: | :heavy_minus_sign: | :x: | :heavy_check_mark: | |
2024-08-04 00:03:30 +08:00
< / details >
## Queries
2026-03-29 17:11:17 +08:00
Filter entities and return collections of entities that match conditions. The built-in `Where` query filters by component mask and has several overloads:
+ `EcsWorld.Where(EcsMask mask)` - standard filtering by mask;
+ `EcsWorld.Where<TAspect>(out TAspect aspect)` - combines aspect mask filtering and returns the aspect;
2024-11-11 14:26:47 +08:00
2026-03-29 17:11:17 +08:00
`Where` can be applied to both `EcsWorld` and framework collections (similar to LINQ). Overloads for sorting using `Comparison<int>` are available.
2024-11-11 14:26:47 +08:00
Example system:
2024-08-04 00:03:30 +08:00
``` c#
public class SomeDamageSystem : IEcsRun, IEcsInject< EcsDefaultWorld >
{
class Aspect : EcsAspect
{
2024-11-11 14:26:47 +08:00
public EcsPool< Health > healths = Inc;
public EcsPool< DamageSignal > damageSignals = Inc;
2024-08-04 00:03:30 +08:00
public EcsTagPool< IsInvulnerable > isInvulnerables = Exc;
2024-11-11 14:26:47 +08:00
// The presence or absence of this component is not checked.
public EcsTagPool< IsDiedSignal > isDiedSignals = Opt;
2024-08-04 00:03:30 +08:00
}
EcsDefaultWorld _world;
public void Inject(EcsDefaultWorld world) => _world = world;
public void Run()
{
foreach (var e in _world.Where(out Aspect a))
{
2024-11-11 14:26:47 +08:00
// Entities with Health, DamageSignal, and without IsInvulnerable will be here.
ref var health = ref a.healths.Get(e);
if(health.points > 0)
{
health.points -= a.damageSignals.Get(e).points;
if(health.points < = 0)
{ // Create a signal to other systems that the entity has died.
a.isDiedSignals.TryAdd(e);
}
}
2024-08-04 00:03:30 +08:00
}
}
}
```
2024-11-11 14:26:47 +08:00
> You can use an [Extension](#extensions) to simplify query syntax and interactions with components - [Simplified Syntax](https://github.com/DCFApixels/DragonECS-AutoInjections).
2024-08-04 00:03:30 +08:00
## Collections
### EcsSpan
2026-03-29 17:11:17 +08:00
A `ref struct` collection of entities, available only for reading. Consists of a reference to an array, a length, and a world identifier. Similar to `ReadOnlySpan<int>` .
2024-08-04 00:03:30 +08:00
``` c#
// Where query returns entities as EcsSpan.
EcsSpan es = _world.Where(out Aspect a);
// Iteration is possible using foreach and for loops.
foreach (var e in es)
{
// ...
}
for (int i = 0; i < es.Count ; i + + )
{
int e = es[i];
// ...
}
```
2026-03-29 17:11:17 +08:00
> Although `EcsSpan` is an array reference, duplicate entities are not allowed.
2024-08-04 00:03:30 +08:00
### EcsGroup
Sparse Set based auxiliary collection for storing a set of entities with O(1) add/delete/check operations, etc.
``` c#
2026-03-29 17:11:17 +08:00
// Getting a new group. EcsWorld contains a pool of groups,
// a new one will be created or a free one reused.
2024-08-04 00:03:30 +08:00
EcsGroup group = EcsGroup.New(_world);
// Release the group.
group.Dispose();
```
``` c#
// Add entityID to the group.
group.Add(entityID);
// Check if entityID exists in the group.
group.Has(entityID);
// Remove entityID from the group.
group.Remove(entityID);
```
``` c#
// WhereToGroup query returns entities as a read-only group EcsReadonlyGroup.
EcsReadonlyGroup group = _world.WhereToGroup(out Aspect a);
// Iteration is possible using foreach and for loops.
foreach (var e in group)
{
// ...
}
for (int i = 0; i < group.Count ; i + + )
{
int e = group[i];
// ...
}
```
2026-03-29 17:11:17 +08:00
Groups implement the `ISet<int>` interface. Editing methods have two variants: one that writes the result to `groupA` , and another that returns a new group.
2024-08-04 00:03:30 +08:00
``` c#
// Union of groupA and groupB.
groupA.UnionWith(groupB);
EcsGroup newGroup = EcsGroup.Union(groupA, groupB);
2026-03-29 17:11:17 +08:00
```
``` c#
2024-08-04 00:03:30 +08:00
// Intersection of groupA and groupB.
groupA.IntersectWith(groupB);
EcsGroup newGroup = EcsGroup.Intersect(groupA, groupB);
2026-03-29 17:11:17 +08:00
```
``` c#
2024-08-04 00:03:30 +08:00
// Difference of groupA and groupB.
groupA.ExceptWith(groupB);
EcsGroup newGroup = EcsGroup.Except(groupA, groupB);
2026-03-29 17:11:17 +08:00
```
``` c#
2024-08-04 00:03:30 +08:00
// Symmetric difference of groupA and groupB.
groupA.SymmetricExceptWith(groupB);
EcsGroup newGroup = EcsGroup.SymmetricExcept(groupA, groupB);
2026-03-29 17:11:17 +08:00
```
``` c#
2024-08-04 00:03:30 +08:00
// Difference of all entities in world and groupA.
groupA.Inverse();
EcsGroup newGroup = EcsGroup.Inverse(groupA);
```
## ECS Root
2026-03-29 17:11:17 +08:00
A custom class that serves as the entry point for ECS. Its main purpose is to initialize systems, run them on each engine update, and release resources when no longer needed.
2024-08-04 00:03:30 +08:00
### Example for Unity
``` c#
using DCFApixels.DragonECS;
using UnityEngine;
public class EcsRoot : MonoBehaviour
{
private EcsPipeline _pipeline;
private EcsDefaultWorld _world;
private void Start()
{
// Creating world for entities and components.
_world = new EcsDefaultWorld();
// Creating pipeline for systems.
_pipeline = EcsPipeline.New()
// Adding systems.
// .Add(new SomeSystem1())
// .Add(new SomeSystem2())
// .Add(new SomeSystem3())
// Injecting world into systems.
.Inject(_world)
// Other injections.
// .Inject(SomeData)
// Finalizing the pipeline construction.
.Build();
// Initialize the Pipeline and run IEcsPreInit.PreInit()
// and IEcsInit.Init() on all added systems.
_pipeline.Init();
}
private void Update()
{
// Invoking IEcsRun.Run() on all added systems.
_pipeline.Run();
}
private void OnDestroy()
{
// Invoking IEcsDestroy.Destroy() on all added systems.
_pipeline.Destroy();
_pipeline = null;
// Requires deleting worlds that will no longer be used.
_world.Destroy();
_world = null;
}
}
```
### Generic example
``` c#
using DCFApixels.DragonECS;
public class EcsRoot
{
private EcsPipeline _pipeline;
private EcsDefaultWorld _world;
2026-03-29 17:11:17 +08:00
// Engine initialization.
2024-08-04 00:03:30 +08:00
public void Init()
{
// Creating world for entities and components.
_world = new EcsDefaultWorld();
// Creating pipeline for systems.
_pipeline = EcsPipeline.New()
// Adding systems.
// .Add(new SomeSystem1())
// .Add(new SomeSystem2())
// .Add(new SomeSystem3())
2026-03-29 17:11:17 +08:00
// Injecting world into systems.
2024-08-04 00:03:30 +08:00
.Inject(_world)
// Other injections.
// .Inject(SomeData)
// Finalizing the pipeline construction.
.Build();
// Initialize the Pipeline and run IEcsPreInit.PreInit()
// and IEcsInit.Init() on all added systems.
_pipeline.Init();
}
// Engine update loop.
public void Update()
{
// Invoking IEcsRun.Run() on all added systems.
_pipeline.Run();
}
// Engine cleanup.
public void Destroy()
{
// Invoking IEcsDestroy.Destroy() on all added systems.
_pipeline.Destroy();
_pipeline = null;
// Requires deleting worlds that will no longer be used.
_world.Destroy();
_world = null;
}
}
```
< / br >
# Debug
2026-03-29 17:11:17 +08:00
The framework provides tools for debugging and logging, independent of the environment. Many types have DebuggerProxy implementations for more informative display in IDEs.
2024-08-04 00:03:30 +08:00
## Meta Attributes
By default, meta-attributes have no use, but are used in integrations with engines to specify display in debugging tools and editors. And can also be used to generate automatic documentation.
``` c#
using DCFApixels.DragonECS;
// Specifies custom name for the type, defaults to the type name.
[MetaName("SomeComponent")]
// Used for grouping types.
2024-11-11 14:26:47 +08:00
[MetaGroup("Abilities", "Passive", ...)] // or [MetaGroup("Abilities/Passive/...")]
2024-08-04 00:03:30 +08:00
// Sets the type color in RGB format, where each channel ranges from 0 to 255; defaults to white.
[MetaColor(MetaColor.Red)] // or [MetaColor(255, 0, 0)]
// Adds description to the type.
[MetaDescription("The quick brown fox jumps over the lazy dog")]
2024-11-11 14:26:47 +08:00
// Adds a string unique identifier.
[MetaID("8D56F0949201D0C84465B7A6C586DCD6")] // Strings must be unique and cannot contain characters ,< > .
2024-08-04 00:03:30 +08:00
// Adds string tags to the type.
[MetaTags("Tag1", "Tag2", ...)] // [MetaTags(MetaTags.HIDDEN))] to hide in the editor
public struct Component : IEcsComponent { /* ... */ }
```
Getting meta-information:
``` c#
TypeMeta typeMeta = someComponent.GetMeta();
// or
TypeMeta typeMeta = pool.ComponentType.ToMeta();
2024-11-11 14:26:47 +08:00
var name = typeMeta.Name; // [MetaName]
var group = typeMeta.Group; // [MetaGroup]
var color = typeMeta.Color; // [MetaColor]
var description = typeMeta.Description; // [MetaDescription]
var metaID = typeMeta.MetaID; // [MetaID]
var tags = typeMeta.Tags; // [MetaTags]
2024-08-04 00:03:30 +08:00
```
2026-03-29 17:11:17 +08:00
> To simplify generate unique MetaID values, use `MetaID.GenerateNewUniqueID()` or the [Browser Generator](https://dcfapixels.github.io/DragonECS-MetaID_Generator_Online/).
2024-08-04 00:03:30 +08:00
## EcsDebug
2026-03-29 17:11:17 +08:00
Provides methods for debugging and logging. Implemented as a static class that forwards calls to a Debug service. Debug services act as intermediaries between environment-specific debugging systems and EcsDebug, enabling portability.
2024-08-04 00:03:30 +08:00
2026-03-29 17:11:17 +08:00
By default, `DefaultDebugService` outputs logs to the console. To implement a custom service, inherit from `DebugService` and implement its abstract members.
2024-08-04 00:03:30 +08:00
``` c#
// Output log.
EcsDebug.Print("Message");
// Output log with tag.
EcsDebug.Print("Tag", "Message");
// Break execution.
EcsDebug.Break();
// Set another Debug Service.
EcsDebug.Set< OtherDebugService > ();
```
## Profiling
``` c#
// Creating a marker named SomeMarker.
2024-11-11 14:26:47 +08:00
private static readonly EcsProfilerMarker _marker = new EcsProfilerMarker("SomeMarker");
```
``` c#
_marker.Begin();
2024-08-04 00:03:30 +08:00
// Code whose execution time is being measured.
2024-11-11 14:26:47 +08:00
_marker.End();
2024-08-04 00:03:30 +08:00
// or
2024-11-11 14:26:47 +08:00
using (_marker.Auto())
2024-08-04 00:03:30 +08:00
{
// Code whose execution time is being measured.
}
```
< / br >
# Define Symbols
2025-03-15 17:06:45 +08:00
+ `DRAGONECS_DISABLE_POOLS_EVENTS` - Disables reactive behavior in pools.
+ `DRAGONECS_ENABLE_DEBUG_SERVICE` - Enables EcsDebug functionality in release builds.
2026-03-29 17:11:17 +08:00
+ `DRAGONECS_STABILITY_MODE` - By default, many exception checks are skipped in release builds for performance. This define replaces checks with error-resilient code to increase stability at the cost of some performance.
2025-03-15 17:06:45 +08:00
+ `DRAGONECS_DISABLE_CATH_EXCEPTIONS` - Turns off the default exception handling behavior. By default, the framework will catch exceptions with the exception information output via EcsDebug and continue working.
2026-03-29 17:11:17 +08:00
+ `REFLECTION_DISABLED` - Restricts the framework's use of Reflection.
+ `DISABLE_DEBUG` - For environments where manual DEBUG disabling is not supported (e.g., Unity).
2024-08-04 00:03:30 +08:00
< / br >
# Framework Extension Tools
2026-03-29 17:11:17 +08:00
Additional tools improve framework extensibility.
2024-08-04 00:03:30 +08:00
## Configs
2026-03-29 17:11:17 +08:00
Constructors of `EcsWorld` and `EcsPipeline` accept config containers implementing `IConfigContainer` or `IConfigContainerWriter` . These containers pass data and dependencies. The built-in container implementation is `ConfigContainer` . С ustom implementations are supported.
2024-08-04 19:28:49 +08:00
Example of using configs for EcsWorld:
2024-08-04 00:03:30 +08:00
``` c#
var configs = new ConfigContainer()
2026-03-29 17:11:17 +08:00
.Set(new EcsWorldConfig(entitiesCapacity: 2000, poolsCapacity: 2000))
2024-08-04 00:03:30 +08:00
.Set(new SomeDataA(/* ... */))
2026-03-29 17:11:17 +08:00
.Set(new SomeDataB(/* ... */));
2024-08-04 00:03:30 +08:00
EcsDefaultWorld _world = new EcsDefaultWorld(configs);
// ...
var _someDataA = _world.Configs.Get< SomeDataA > ();
var _someDataB = _world.Configs.Get< SomeDataB > ();
```
2024-08-04 19:28:49 +08:00
Example of using configs for EcsPipeline:
2024-08-04 00:03:30 +08:00
``` c#
2026-03-29 17:11:17 +08:00
_pipeline = EcsPipeline.New() // similarly _pipeline = EcsPipeline.New(new ConfigContainer())
2024-08-04 00:03:30 +08:00
.Configs.Set(new SomeDataA(/* ... */))
.Configs.Set(new SomeDataB(/* ... */))
// ...
.BuildAndInit();
// ...
var _someDataA = _pipeline.Configs.Get< SomeDataA > ();
var _someDataB = _pipeline.Configs.Get< SomeDataB > ();
```
## World Components
2026-03-29 17:11:17 +08:00
World components attach additional data to worlds. World components are `struct` types. Access via `Get` is optimized and performs similarly to class field access.
2024-08-07 11:11:48 +08:00
Get component:
2024-08-04 00:03:30 +08:00
``` c#
ref WorldComponent component = ref _world.Get< WorldComponent > ();
```
2026-03-29 17:11:17 +08:00
Component implementation:
2024-08-04 00:03:30 +08:00
``` c#
public struct WorldComponent
{
2024-08-07 11:11:48 +08:00
// Data.
2024-08-04 00:03:30 +08:00
}
```
2026-03-29 17:11:17 +08:00
Or with lifecycle callbacks:
2024-08-04 00:03:30 +08:00
``` c#
public struct WorldComponent : IEcsWorldComponent< WorldComponent >
{
2024-08-07 11:11:48 +08:00
// Data.
2024-08-04 00:03:30 +08:00
void IEcsWorldComponent< WorldComponent > .Init(ref WorldComponent component, EcsWorld world)
{
2024-08-07 11:11:48 +08:00
// Actions during component initialization. Called before the first return from EcsWorld.Get().
2024-08-04 00:03:30 +08:00
}
void IEcsWorldComponent< WorldComponent > .OnDestroy(ref WorldComponent component, EcsWorld world)
{
2024-08-07 11:11:48 +08:00
// Actions when EcsWorld.Destroy is called.
2026-03-29 17:11:17 +08:00
// OnDestroy requires manual reset of the component if necessary.
2024-08-04 00:03:30 +08:00
component = default;
}
}
```
< details >
2024-08-07 11:11:48 +08:00
< summary > Example of use< / summary >
2024-08-04 00:03:30 +08:00
2026-03-29 17:11:17 +08:00
The `IEcsWorldComponent<T>` interface events can be used to initialize component fields and release resources automatically.
2024-08-04 00:03:30 +08:00
``` c#
public struct WorldComponent : IEcsWorldComponent< WorldComponent >
{
2026-03-29 17:11:17 +08:00
private SomeClass _object; // Object to be disposed.
private SomeReusedClass _reusedObject; // Object to be reused.
2024-08-04 00:03:30 +08:00
public SomeClass Object => _object;
public SomeReusedClass ReusedObject => _reusedObject;
void IEcsWorldComponent< WorldComponent > .Init(ref WorldComponent component, EcsWorld world)
{
if (component._reusedObject == null)
component._reusedObject = new SomeReusedClass();
component._object = new SomeClass();
2026-03-29 17:11:17 +08:00
// After getting the component via EcsWorld.Get, _reusedObject and _object will be ready.
2024-08-04 00:03:30 +08:00
}
void IEcsWorldComponent< WorldComponent > .OnDestroy(ref WorldComponent component, EcsWorld world)
{
2026-03-29 17:11:17 +08:00
// Dispose and clear the reference so GC can collect it.
2024-08-04 00:03:30 +08:00
component._object.Dispose();
component._object = null;
2026-03-29 17:11:17 +08:00
// Optionally reset the reused object.
// component._reusedObject.Reset();
// If full reset is needed, uncomment the line below.
2024-08-04 00:03:30 +08:00
// component = default;
}
}
```
< / details >
2024-08-04 19:28:49 +08:00
> Components and configs can be used to create extensions in conjunction with extension methods.
2024-08-04 00:03:30 +08:00
< / br >
2024-05-06 14:45:41 +08:00
# Projects powered by DragonECS
2024-10-27 21:49:49 +08:00
## With sources:
2024-11-12 22:15:08 +08:00
< table >
< tr >
< td align = "center" >
< a href = "https://github.com/DCFApixels/3D-Platformer-DragonECS-Demo" >
3D Platformer (Example)
2026-03-28 02:35:16 +08:00
< img src = "https://github.com/user-attachments/assets/6aba814d-a70b-432f-a905-84d1b6872581" / >
2024-11-12 22:15:08 +08:00
< / a >
< / td >
< td align = "center" >
< a href = "https://github.com/DCFApixels/LD_56_Tiny_Aliens" >
Tiny Aliens (Ludum Dare 56)
< img src = "https://github.com/user-attachments/assets/1a8f06ed-c68d-483a-b880-c9faaf7e0b5f" alt = "screenshot" >
< / a >
< / td >
< / tr >
2026-03-28 22:51:52 +08:00
< tr > < / tr >
< tr >
< td align = "center" >
< a href = "https://github.com/Evileptic/Arkanoid" >
Arkanoid
< img src = "https://github.com/user-attachments/assets/bbdb4a7f-2f59-4a3a-ab51-a3e4fe0ad35e" alt = "screenshot" >
< / a >
< / td >
< td align = "center" >
_____________
< img tabindex = "-1" src = "https://github.com/user-attachments/assets/3fa1ca6d-29f6-43e6-aafe-cc9648d20490" alt = "screenshot" >
< / td >
< / tr >
2024-11-12 22:15:08 +08:00
< / table >
2024-10-27 21:49:49 +08:00
## Released games:
2024-11-12 22:15:08 +08:00
< table >
2026-03-18 13:25:34 +08:00
< tr >
< td align = "center" >
< a href = "https://dcfapixels.github.io/Project8.html" >
Crystal Siege
< img src = "https://github.com/user-attachments/assets/1aa60a50-2668-4919-aca9-d6d2b980c3dd" >
< / a >
< / td >
< td align = "center" >
< a href = "https://play.google.com/store/apps/details?id=com.ZlodeyStudios.OrdersMatter" >
Order matters
< img src = "https://github.com/user-attachments/assets/c55b2647-9b6e-4145-98ff-c3d094600fa1" >
< / a >
< / td >
< / tr >
< tr > < / tr >
2024-11-12 22:15:08 +08:00
< tr >
< td align = "center" >
< a href = "https://yandex.ru/games/app/206024?utm_source=game_popup_menu" >
Башенки Смерти
< img src = "https://github.com/user-attachments/assets/70fc55a0-c911-49f8-ba75-f503437f087f" alt = "screenshot" >
< / a >
< / td >
< td align = "center" >
2026-03-18 13:25:34 +08:00
_____________
2024-11-12 22:15:08 +08:00
< img tabindex = "-1" src = "https://github.com/user-attachments/assets/3fa1ca6d-29f6-43e6-aafe-cc9648d20490" alt = "screenshot" >
< / td >
< / tr >
< / table >
2024-10-09 00:03:40 +08:00
2024-05-06 14:45:41 +08:00
< / br >
2024-08-04 00:03:30 +08:00
# FAQ
2026-03-18 13:25:34 +08:00
## How to enable/disable systems?
2026-03-29 17:11:17 +08:00
Directly — not supported.
2026-03-18 13:25:34 +08:00
2026-03-29 17:11:17 +08:00
The need to enable or disable systems usually appears when the overall game state changes; this may also mean switching a group of systems. Conceptually this is a change of processes. Two solutions are recommended:
2026-03-18 13:25:34 +08:00
2026-03-29 17:11:17 +08:00
- For global process changes, create a new `EcsPipeline` and run the appropriate pipeline in the engine update loop.
- Split `IEcsRun` into multiple processes and run the desired process in the engine update loop. Create a new process interface, implement a runner for it, and obtain the runner via `EcsPipeline.GetRunner<T>()` .
2026-03-18 13:25:34 +08:00
## Recommendations list: [DragonECS-Vault](https://github.com/DCFApixels/DragonECS-Vault)
2026-03-29 18:07:01 +08:00
2024-08-04 00:03:30 +08:00
< / br >
2023-12-18 05:26:02 +08:00
# Feedback
2024-03-29 14:06:30 +08:00
+ Discord (RU-EN) [https://discord.gg/kqmJjExuCf ](https://discord.gg/kqmJjExuCf )
2024-03-29 22:40:41 +08:00
+ QQ (中文) [949562781 ](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=IbDcH43vhfArb30luGMP1TMXB3GCHzxm&authKey=s%2FJfqvv46PswFq68irnGhkLrMR6y9tf%2FUn2mogYizSOGiS%2BmB%2B8Ar9I%2Fnr%2Bs4oS%2B&noverify=0&group_code=949562781 )
2024-08-04 00:03:30 +08:00
2026-03-29 18:07:01 +08:00
< / br >
# License
The MIT License: [Open ](LICENSE.md )
2024-08-04 00:03:30 +08:00
< / br > < / br > < / br >
< / br > < / br > < / br >
< / br > < / br > < / br >
< / br > < / br > < / br >
< / br > < / br > < / br >
2024-08-05 09:00:11 +08:00
< / br > < / br > < / br >
2024-08-05 09:05:50 +08:00
< img width = "0" src = "https://github.com/user-attachments/assets/30528cb5-f38e-49f0-b23e-d001844ae930" > <!-- Чтоб флаг подгружался в любом случае -->