update readme

This commit is contained in:
Mikhail 2026-03-18 13:25:34 +08:00
parent cfaed5b6d7
commit 5b9e23f852
3 changed files with 219 additions and 157 deletions

View File

@ -20,28 +20,26 @@
<tr>
<td nowrap width="100">
<a href="README-RU.md">
<img src="https://github.com/user-attachments/assets/3c699094-f8e6-471d-a7c1-6d2e9530e721"></br>
<img src="https://github.com/user-attachments/assets/3c699094-f8e6-471d-a7c1-6d2e9530e721"><br/>
<span>Русский</span>
</a>
</td>
<td nowrap width="100">
<a href="https://github.com/DCFApixels/DragonECS">
<img src="https://github.com/user-attachments/assets/30528cb5-f38e-49f0-b23e-d001844ae930"></br>
<img src="https://github.com/user-attachments/assets/30528cb5-f38e-49f0-b23e-d001844ae930"><br/>
<span>English</span>
</a>
</td>
<td nowrap width="100">
<a href="README-ZH.md">
<img src="https://github.com/user-attachments/assets/8e598a9a-826c-4a1f-b842-0c56301d2927"></br>
<img src="https://github.com/user-attachments/assets/8e598a9a-826c-4a1f-b842-0c56301d2927"><br/>
<span>中文</span>
</a>
</td>
</tr>
</table>
</br>
DragonECS - это [ECS](https://en.wikipedia.org/wiki/Entity_component_system) фреймворк нацеленный на максимальную удобность, модульность, расширяемость и производительность динамического изменения сущностей. Разработан на чистом C#, без зависимостей и генерации кода. Вдохновлен [LeoEcs Lite](https://github.com/Leopotam/ecslite).
DragonECS - это [ECS](https://en.wikipedia.org/wiki/Entity_component_system) фреймворк, нацеленный на максимальную удобность, модульность, расширяемость и производительность динамического изменения сущностей. Разработан на чистом C#, без зависимостей и генерации кода. Вдохновлен [LeoEcs Lite](https://github.com/Leopotam/ecslite).
> [!WARNING]
> Проект предрелизной версии, поэтому API может меняться. В ветке main актуальная и рабочая версия.
@ -81,20 +79,20 @@ DragonECS - это [ECS](https://en.wikipedia.org/wiki/Entity_component_system)
- [FAQ](#faq)
- [Обратная связь](#обратная-связь)
</br>
# Установка
Семантика версионирования - [Открыть](https://gist.github.com/DCFApixels/e53281d4628b19fe5278f3e77a7da9e8#file-dcfapixels_versioning_ru-md)
## Окружение
Обязательные требования:
+ Минимальная версия C# 7.3;
+ Минимальная версия C#: 7.3.
Поддерживает:
+ NativeAOT;
+ Игровые движки с C#: Unity, Godot, MonoGame и т.д.
Протестировано:
+ **Unity:** Минимальная версия 2021.2.0;
Протестировано на:
* **Unity:** Минимальная версия 2021.2.0.
## Установка для Unity
> Рекомендуется так же установить расширение [Интеграция с движком Unity](https://github.com/DCFApixels/DragonECS-Unity)
@ -111,7 +109,7 @@ https://github.com/DCFApixels/DragonECS.git
* ### В виде исходников
Можно так же напрямую скопировать в проект исходники фреймворка.
</br>
# Расширения
* Интеграции:
@ -131,11 +129,11 @@ https://github.com/DCFApixels/DragonECS.git
* [Шаблоны кода IDE](https://gist.github.com/ctzcs/0ba948b0e53aa41fe1c87796a401660b) и [для Unity](https://gist.github.com/ctzcs/d4c7730cf6cd984fe6f9e0e3f108a0f1)
> *Твое расширение? Если разрабатываешь расширение для DragonECS, пиши [сюда](#обратная-связь).
</br>
# Основные концепции
## Entity
**Сущности** - это то к чему крепятся данные. Для доступа к сущности используются идентификаторы двух типов:
**Сущности** - это то, к чему крепятся данные. Для доступа к сущности используются идентификаторы двух типов:
* `int` - однократный идентификатор, применяется в пределах одного тика. Не рекомендуется использовать для хранения;
* `entlong` - долговременный идентификатор, содержит метку поколения, что делает его уникальным навсегда. Подходит для хранения.
```c#
@ -254,7 +252,7 @@ public struct Health : IEcsComponent
</details>
</br>
# Концепции фреймворка
## Пайплайн
@ -285,7 +283,7 @@ class SomeSystem : IEcsRun, IEcsPipelineMember
```
> Для одновременного построения и инициализации есть метод Builder.BuildAndInit();
### Внедрение зависимостей
Фреймворк реализует внедрение зависимостей для систем. это процесс который запускается вместе с инициализацией пайплайна и внедряет данные переданные в Builder.
Фреймворк реализует внедрение зависимостей для систем. Это процесс, который запускается вместе с инициализацией пайплайна и внедряет данные, переданные в Builder.
```c#
class SomeDataA { /* ... */ }
@ -347,7 +345,7 @@ EcsPipeline pipeline = EcsPipeline.New()
```
### Сортировка
Дла управления расположением систем в пайплайне, вне зависимости от порядка добавления, есть 2 способа: Слои и Порядок сортировки.
Для управления расположением систем в пайплайне, вне зависимости от порядка добавления, есть 2 способа: Слои и Порядок сортировки.
#### Слои
Слой определяет место в пайплайне для вставки систем. Например, если необходимо чтобы система была вставлена в конце пайплайна, эту систему можно добавить в слой `EcsConsts.END_LAYER`.
```c#
@ -362,11 +360,11 @@ EcsPipeline pipeline = EcsPipeline.New()
.BuildAndInit();
```
Встроенные слои расположены в следующем порядке:
* `EcsConst.PRE_BEGIN_LAYER`
* `EcsConst.BEGIN_LAYER`
* `EcsConst.BASIC_LAYER` (По умолчанию системы добавляются сюда)
* `EcsConst.END_LAYER`
* `EcsConst.POST_END_LAYER`
* `EcsConsts.PRE_BEGIN_LAYER`
* `EcsConsts.BEGIN_LAYER`
* `EcsConsts.BASIC_LAYER` (По умолчанию системы добавляются сюда)
* `EcsConsts.END_LAYER`
* `EcsConsts.POST_END_LAYER`
#### Порядок сортировки
Для сортировки систем в рамках слоя используется int значение порядка сортировки. По умолчанию системы добавляются с `sortOrder = 0`.
```c#
@ -421,10 +419,10 @@ _pipeline = EcsPipeline.New()
.BuildAndInit();
// Запуск раннера если раннер был добавлен.
_pipeline.GetRunner<IDoSomethingProcess>.Do()
_pipeline.GetRunner<IDoSomethingProcess>().Do();
// Или если раннер не был добавлен(Вызов GetRunnerInstance так же добавит раннер в пайплайн).
_pipeline.GetRunnerInstance<DoSomethingProcessRunner>.Do()
// Или если раннер не был добавлен (вызов GetRunnerInstance также добавит раннер в пайплайн).
_pipeline.GetRunnerInstance<DoSomethingProcessRunner>().Do();
```
<details>
@ -542,8 +540,8 @@ poses.Del(entityID);
Обычно маски не используются в чистом виде, а являются частью [Аспекта](#Аспект).
```c#
// Создание маски с улосвием наличия компонентов SomeCmp1 и SomeCmp2,
// и с улосвием отсутсвия компонента SomeCmp3.
// Создание маски с условием наличия компонентов SomeCmp1 и SomeCmp2,
// и с условием отсутствия компонента SomeCmp3.
EcsMask mask = EcsMask.New(_world)
// Inc - Условие наличия компонента.
.Inc<SomeCmp1>()
@ -704,7 +702,7 @@ public class SomeDamageSystem : IEcsRun, IEcsInject<EcsDefaultWorld>
``` c#
// Запрос Where возвращает сущности в виде EcsSpan.
EcsSpan es = _world.Where(out Aspect a);
// Итерироваться можно по foreach и for.
// Итерироваться можно с помощью `foreach` и `for`.
foreach (var e in es)
{
// ...
@ -737,7 +735,7 @@ group.Remove(entityID);
``` c#
// Запрос WhereToGroup возвращает сущности в виде группы только для чтения EcsReadonlyGroup.
EcsReadonlyGroup group = _world.WhereToGroup(out Aspect a);
// Итерироваться можно по foreach и for.
// Итерироваться можно с помощью `foreach` и `for`.
foreach (var e in group)
{
// ...
@ -963,7 +961,7 @@ using (_marker.Auto())
+ `DRAGONECS_STABILITY_MODE` - включает опускаемые в релизном билде проверки.
+ `DRAGONECS_DISABLE_CATH_EXCEPTIONS` - Выключает поведение по умолчанию по обработке исключений. По умолчанию фреймворк будет ловить исключения с выводом информации из исключений через EcsDebug и продолжать работу.
+ `REFLECTION_DISABLED` - Полностью ограничивает работу фреймворка с Reflection.
+ `DISABLE_DEBUG` - Для среды где не поддерживается ручное отключение DEBUG, например Unity.
+ `DISABLE_DEBUG` - Для среды, где не поддерживается ручное отключение DEBUG, например Unity.
</br>
@ -1124,7 +1122,7 @@ public struct WorldComponent : IEcsWorldComponent<WorldComponent>
# FAQ
## Как Выключать/Включать системы?
Напрямую - никак. </br>
Напрямую — никак.
Обычно потребность выключить/включить систему появляется когда поменялось общее состояние игры, это может так же значить что нужно переключить сразу группу систем, все это в совокупности можно рассматривать как изменения процессов. Есть 2 решения:</br>
+ Если изменения процесса глобальные, то создать новый `EcsPipeline` и в цикле обновления движка запускать соответствующий пайплайн.
+ Разделить `IEcsRun` на несколько процессов и в цикле обновления движка запускать соответствующий процесс. Для этого создайте новый интерфейс процесса, раннер для запуска этого интерфейса и получайте раннер через `EcsPipeline.GetRunner<T>()`.

View File

@ -20,19 +20,19 @@
<tr>
<td nowrap width="100">
<a href="README-RU.md">
<img src="https://github.com/user-attachments/assets/7bc29394-46d6-44a3-bace-0a3bae65d755"></br>
<img src="https://github.com/user-attachments/assets/7bc29394-46d6-44a3-bace-0a3bae65d755"><br/>
<span>Русский</span>
</a>
</td>
<td nowrap width="100">
<a href="https://github.com/DCFApixels/DragonECS">
<img src="https://github.com/user-attachments/assets/30528cb5-f38e-49f0-b23e-d001844ae930"></br>
<img src="https://github.com/user-attachments/assets/30528cb5-f38e-49f0-b23e-d001844ae930"><br/>
<span>English</span>
</a>
</td>
<td nowrap width="100">
<a href="README-ZH.md">
<img src="https://github.com/user-attachments/assets/3c699094-f8e6-471d-a7c1-6d2e9530e721"></br>
<img src="https://github.com/user-attachments/assets/3c699094-f8e6-471d-a7c1-6d2e9530e721"><br/>
<span>中文</span>
</a>
</td>
@ -52,6 +52,7 @@ DragonECS 是一个[实体组件系统](https://www.imooc.com/article/331544)框
## 目录
- [安装](#安装)
- [扩展](#扩展)
- [基础概念](#基础概念)
- [实体](#实体)
- [组件](#组件)
@ -79,7 +80,6 @@ DragonECS 是一个[实体组件系统](https://www.imooc.com/article/331544)框
- [世界组件](#世界组件)
- [配置](#配置)
- [使用DragonECS的项目](#使用dragonecs的项目)
- [扩展](#扩展)
- [FAQ](#faq)
- [反馈](#反馈)
@ -89,14 +89,14 @@ DragonECS 是一个[实体组件系统](https://www.imooc.com/article/331544)框
版本的语义 [[打开]](https://gist.github.com/DCFApixels/e53281d4628b19fe5278f3e77a7da9e8#file-dcfapixels_versioning_ru-md)
## 环境
必备要求:
+ C# 7.3 的最低版本;
* 最低 C# 版本7.3。
可选要求:
+ 支持NativeAOT
+ 使用 C# 的游戏引擎Unity、Godot、MonoGame等。
* 支持 NativeAOT
* 使用 C# 的游戏引擎Unity、Godot、MonoGame 等。
已测试:
+ **Unity:** 最低版本 2020.1.0
已测试
* **Unity:** 最低版本 2021.2.0。
## 为Unity安装
> 还建议安装[Unity引擎集成](https://github.com/DCFApixels/DragonECS-Unity)扩展。
@ -108,13 +108,31 @@ https://github.com/DCFApixels/DragonECS.git
* ### 作为源代码
框架也可以通过复制源代码添加到项目中。
</br>
# 扩展
* 集成:
* [Unity](https://github.com/DCFApixels/DragonECS-Unity)
* [Godot](https://gitlab.com/InauniusOwn/Libraries/DraGodot)
* 包:
* [自动依赖注入](https://github.com/DCFApixels/DragonECS-AutoInjections)
* [经典C#多线程](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)
* 工具:
* [简单语法](https://gist.github.com/DCFApixels/d7bfbfb8cb70d141deff00be24f28ff0)
* [EcsRefPool](https://gist.github.com/DCFApixels/73e392ccabdd98b3d4a517017d8a3f22)
* [计时器](https://gist.github.com/DCFApixels/71a416275660c465ece76242290400df)
* [单帧组件](https://gist.github.com/DCFApixels/46d512dbcf96c115b94c3af502461f60)
* [IDE代码模板](https://gist.github.com/ctzcs/0ba948b0e53aa41fe1c87796a401660b) 和 [Unity 模板](https://gist.github.com/ctzcs/d4c7730cf6cd984fe6f9e0e3f108a0f1)
> *你的扩展?如果你正在开发 DragonECS 的扩展,可以[在此处发布](#反馈).
# 基础概念
## 实体
**实体**是附加数据的基础。它们以标识符的形式实现,有两种类型:
* `int` - 是在单个更新中使用的一次性标识符。不建议存储`int`标识符,而应使用 `entlong`
* `entlong` - 是一个长期标识符,包含一整套用于明确识别的信息;
**实体**是附加数据的基础。有两种用于引用实体的标识符
* `int` - 短期标识符仅在单次更新tick内有效。不建议用于长期存储
* `entlong` - 长期标识符包含一个世代标记generation tag使其在实体的整个生命周期内保持唯一。适合长期保存和存储使用
```c#
// 在世界中创建一个新实体。
int entityID = _world.NewEntity();
@ -251,6 +269,12 @@ class SomeSystem : IEcsInject<SomeDataA>, IEcsRun
}
```
静态标记的含义:
* `Inc` — 组件必须存在(包含条件)并缓存该池。
* `Exc` — 组件不得存在(排除条件)并缓存该池。
* `Opt` — 组件可以存在,但不影响过滤(仅缓存池以便访问)。
* `Any` — 至少要存在被 `Any` 标记的组件之一;同时缓存该池。
### 模块
实现一个共同特性的系统组可以组合成模块,模块也可以简单地添加到管线中。
```c#
@ -290,11 +314,11 @@ EcsPipeline pipeline = EcsPipeline.New()
.BuildAndInit();
```
嵌入层按以下顺序排列:
* `EcsConst.PRE_BEGIN_LAYER`
* `EcsConst.BEGIN_LAYER`
* `EcsConst.BASIC_LAYER`(默认情况下,系统添加到此层)
* `EcsConst.END_LAYER`
* `EcsConst.POST_END_LAYER`
* `EcsConsts.PRE_BEGIN_LAYER`
* `EcsConsts.BEGIN_LAYER`
* `EcsConsts.BASIC_LAYER`(默认情况下,系统添加到此层)
* `EcsConsts.END_LAYER`
* `EcsConsts.POST_END_LAYER`
#### 排序顺序
在同一层内,可以使用 `int` 类型的排序值来排序系统。默认情况下,系统的排序值为 `sortOrder = 0`
```c#
@ -322,7 +346,7 @@ EcsPipeline pipeline = EcsPipeline.New()
<details>
<summary>用户流程</summary>
Для добавления нового процесса создайте интерфейс наследованный от `IEcsProcess` и создайте раннер для него. Раннер это класс реализующий интерфейс запускаемого процесса и наследуемый от `EcsRunner<TInterface>`. Пример:
要添加新的流程,请创建一个继承自 `IEcsProcess` 的接口,并为其创建一个 Runner。Runner 是一个实现该流程接口并继承自 `EcsRunner<TInterface>` 的类。示例:
``` c#
// 流程接口。
interface IDoSomethingProcess : IEcsProcess
@ -347,10 +371,10 @@ _pipeline = EcsPipeline.New()
.BuildAndInit();
// 如果启动器已经添加,运行它。
_pipeline.GetRunner<IDoSomethingProcess>.Do()
_pipeline.GetRunner<IDoSomethingProcess>().Do();
// 如果启动器尚未添加,使用 GetRunnerInstance 将其添加并运行
_pipeline.GetRunnerInstance<DoSomethingProcessRunner>.Do()
_pipeline.GetRunnerInstance<DoSomethingProcessRunner>().Do();
```
<details>
@ -437,7 +461,7 @@ poses.Del(entityID);
> 可以实现用户池。稍后将介绍这一功能。
## 掩码
用于根据组件的存在与否来过滤实体。
用于根据组件的存在与否来过滤实体。通常掩码不单独使用,而是作为 `EcsAspect` 的一部分,用于查询时过滤实体。
``` c#
// 创建一个掩码,检查实体是否具有组件
// SomeCmp1 和 SomeCmp2但没有组件 SomeCmp3。
@ -472,7 +496,11 @@ EcsMask mask = _staticMask.ToMask(_world);
</details>
## 方面
这些是继承自 EcsAspect 的用户类,用于与实体进行交互。方面同时充当池的缓存和实体组件的过滤掩码。可以把方面视为系统处理哪些实体的描述。
方面是继承自 `EcsAspect` 的用户自定义类,用于描述系统要处理的一组组件。方面同时承担两个职责:
- 掩码 — 初始化并保存一个 `EcsMask`,使其可在查询中用于过滤实体。
- 池缓存 — 提供对组件池的快速访问。
简而言之,方面是描述“我处理哪些实体以及如何访问它们的组件”的便捷方式。
简化语法:
``` c#
@ -503,9 +531,9 @@ class Aspect : EcsAspect
public EcsPool<Velocity> velocities;
protected override void Init(Builder b)
{
poses = b.Include<Pose>();
velocities = b.Include<Velocity>();
b.Exclude<FreezedTag>();
poses = b.Inc<Pose>();
velocities = b.Inc<Velocity>();
b.Exc<FreezedTag>();
}
}
```
@ -529,13 +557,13 @@ class Aspect : EcsAspect
otherAspect1 = b.Combine<OtherAspect1>(1);
// 即使对 OtherAspect1 调用 Combine 方法更早Aspect 会首先与 OtherAspect2 进行组合,因为默认情况下 order = 0。
otherAspect2 = b.Combine<OtherAspect2>();
// 如果 OtherAspect1 或 OtherAspect2 中有 b.Exclude<Pose>() 的限制条件,这里将被替换为 b.Include<Pose>()。
poses = b.Include<Pose>();
// 如果 OtherAspect1 或 OtherAspect2 中有 b.Exc<Pose>() 的限制条件,这里将被替换为 b.Inc<Pose>().
poses = b.Inc<Pose>();
}
}
```
如果组合的方面存在冲突的限制条件,则新的限制条件将替换先前添加的限制条件。根方面的限制条件始终会替换添加的方面中的限制条件。限制条件组合的视觉示例:
| | cmp1 | cmp2 | cmp3 | cmp4 | cmp5 | разрешение конфликтных ограничений|
| | cmp1 | cmp2 | cmp3 | cmp4 | cmp5 | 冲突限制的解析 |
| :--- | :--- | :--- | :--- | :--- | :--- |:--- |
| OtherAspect2 | :heavy_check_mark: | :x: | :heavy_minus_sign: | :heavy_minus_sign: | :heavy_check_mark: | |
| OtherAspect1 | :heavy_minus_sign: | :heavy_check_mark: | :heavy_minus_sign: | :x: | :heavy_minus_sign: | 对于 `cmp2` 将选择 :heavy_check_mark: |
@ -978,6 +1006,23 @@ public struct WorldComponent : IEcsWorldComponent<WorldComponent>
## Released games:
<table>
<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>
<tr>
<td align="center">
<a href="https://yandex.ru/games/app/206024?utm_source=game_popup_menu">
@ -986,40 +1031,26 @@ public struct WorldComponent : IEcsWorldComponent<WorldComponent>
</a>
</td>
<td align="center">
<span>
_____________
<img tabindex="-1" src="https://github.com/user-attachments/assets/3fa1ca6d-29f6-43e6-aafe-cc9648d20490" alt="screenshot">
</span>
</td>
</tr>
</table>
</br>
# 扩展
* Packages:
* [Unity集成](https://github.com/DCFApixels/DragonECS-Unity)
* [自动依赖注入](https://github.com/DCFApixels/DragonECS-AutoInjections)
* [经典C#多线程](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:
* [简单语法](https://gist.github.com/DCFApixels/d7bfbfb8cb70d141deff00be24f28ff0)
* [单帧组件](https://gist.github.com/DCFApixels/46d512dbcf96c115b94c3af502461f60)
* [IDE代码模板](https://gist.github.com/ctzcs/0ba948b0e53aa41fe1c87796a401660b) и [Unity代码模板](https://gist.github.com/ctzcs/d4c7730cf6cd984fe6f9e0e3f108a0f1)
> *你的扩展?如果你正在开发 DragonECS 的扩展,可以[在此处发布](#反馈).
</br>
# FAQ
## 'ReadOnlySpan<>' could not be found
在Unity 2020.1.x版本中控制台可能会出现以下错误
```
The type or namespace name 'ReadOnlySpan<>' could not be found (are you missing a using directive or an assembly reference?)
```
要解决这个问题,需要在`Project Settings/Player/Other Settings/Scripting Define Symbols`中添加`ENABLE_DUMMY_SPAN`指令.
## 如何启用/禁用系统?
直接——不能。
通常当游戏的整体状态发生变化时会需要启用或禁用系统有时需要切换一组系统。从概念上讲这可以视为流程process的变更。这里有两种解决方案
- 如果流程变更是全局性的,可以创建一个新的 `EcsPipeline`并在引擎的更新循环中运行相应的管线pipeline
- 将 `IEcsRun` 拆分为多个流程,并在引擎更新循环中运行所需的流程。为此,创建一个新的流程接口,为其实现一个 Runner并通过 `EcsPipeline.GetRunner<T>()` 获取该 Runner。
## 建议清单: [DragonECS-Vault](https://github.com/DCFApixels/DragonECS-Vault)
</br>
# 反馈

139
README.md
View File

@ -22,26 +22,26 @@
<tr>
<td nowrap width="100">
<a href="README-RU.md">
<img src="https://github.com/user-attachments/assets/7bc29394-46d6-44a3-bace-0a3bae65d755"></br>
<img src="https://github.com/user-attachments/assets/7bc29394-46d6-44a3-bace-0a3bae65d755"><br/>
<span>Русский</span>
</a>
</td>
<td nowrap width="100">
<a href="https://github.com/DCFApixels/DragonECS">
<img src="https://github.com/user-attachments/assets/3c699094-f8e6-471d-a7c1-6d2e9530e721"></br>
<img src="https://github.com/user-attachments/assets/3c699094-f8e6-471d-a7c1-6d2e9530e721"><br/>
<span>English</span>
</a>
</td>
<td nowrap width="100">
<a href="README-ZH.md">
<img src="https://github.com/user-attachments/assets/8e598a9a-826c-4a1f-b842-0c56301d2927"></br>
<img src="https://github.com/user-attachments/assets/8e598a9a-826c-4a1f-b842-0c56301d2927"><br/>
<span>中文</span>
</a>
</td>
</tr>
</table>
</br>
The [ECS](https://en.wikipedia.org/wiki/Entity_component_system) Framework aims to maximize usability, modularity, extensibility and performance of dynamic entity changes. Without code generation and dependencies. Inspired by [LeoEcs Lite](https://github.com/Leopotam/ecslite).
@ -54,6 +54,7 @@ The [ECS](https://en.wikipedia.org/wiki/Entity_component_system) Framework aims
## Table of Contents
- [Installation](#installation)
- [Extensions](#extensions)
- [Basic Concepts](#basic-concepts)
- [Entity](#entity)
- [Component](#component)
@ -91,14 +92,14 @@ The [ECS](https://en.wikipedia.org/wiki/Entity_component_system) Framework aims
Versioning semantics - [Open](https://gist.github.com/DCFApixels/e53281d4628b19fe5278f3e77a7da9e8#file-dcfapixels_versioning_ru-md)
## Environment
Requirements:
+ Minimum version of C# 7.3;
* Minimum C# version: 7.3.
Optional:
+ Support for NativeAOT
+ Game engines with C#: Unity, Godot, MonoGame, etc.
* Support for NativeAOT
* Game engines with C#: Unity, Godot, MonoGame, etc.
Tested with:
+ **Unity:** Minimum version 2020.1.0;
* **Unity:** Minimum version 2021.2.0.
## Unity Installation
* ### Unity Package
@ -109,13 +110,31 @@ https://github.com/DCFApixels/DragonECS.git
* ### Source Code
The framework can also be added to the project as source code.
</br>
# 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)
> *Your extension? If you are developing an extension for DragonECS, you can share it [here](#feedback).
# Basic Concepts
## Entity
Сontainer for components. They are implemented as identifiers, of which there are two types:
* `int` - a short-term identifier used within a single tick. Storing `int` identifiers is not recommended, use `entlong` instead;
* `entlong` - long-term identifier, contains a full set of information for unique identification;
Container for components. There are two identifier types used to reference entities:
* `int` - short-lived identifier, valid within a single tick. Not recommended for long-term storage;
* `entlong` - long-term identifier that includes a generation tag, which makes it unique across entity lifetimes. Suitable for storing and long-term usage.
```c#
// Creating a new entity in the world.
int entityID = _world.NewEntity();
@ -294,11 +313,11 @@ EcsPipeline pipeline = EcsPipeline.New()
.BuildAndInit();
```
The built-in layers are arranged in the following order:
* `EcsConst.PRE_BEGIN_LAYER`
* `EcsConst.BEGIN_LAYER`
* `EcsConst.BASIC_LAYER` (Systems are added here if no layer is specified during addition)
* `EcsConst.END_LAYER`
* `EcsConst.POST_END_LAYER`
* `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`
#### Sorting Order
The sort order int value is used to sort systems within a layer. By default, systems are added with `sortOrder = 0`.
@ -352,10 +371,10 @@ _pipeline = EcsPipeline.New()
.BuildAndInit();
// Running the runner if it was added
_pipeline.GetRunner<IDoSomethingProcess>.Do()
_pipeline.GetRunner<IDoSomethingProcess>().Do();
// or if the runner was not added (calling GetRunnerInstance will also add the runner to the pipeline).
_pipeline.GetRunnerInstance<DoSomethingProcessRunner>.Do()
_pipeline.GetRunnerInstance<DoSomethingProcessRunner>().Do();
```
<details>
@ -444,7 +463,7 @@ poses.Del(entityID);
> It is possible to implement a user pool. This feature will be described shortly.
## Mask
Used to filter entities by the presence or absence of components.
Used to filter entities by the presence or absence of components. Usually masks are not used standalone, they are part of `EcsAspect` and used by queries to filter entities.
``` c#
// Creating a mask that checks if entities have components
// SomeCmp1 and SomeCmp2, but do not have component SomeCmp3.
@ -479,7 +498,11 @@ EcsMask mask = _staticMask.ToMask(_world);
</details>
## Aspect
These are custom classes inherited from `EcsAspect` and used to interact with entities. Aspects are both a pool cache and a component mask for filtering entities. You can think of aspects as a description of what entities the system is working with.
These are 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`, so it can be used in queries to filter entities.
- 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."
Simplified syntax:
``` c#
@ -500,6 +523,13 @@ class Aspect : EcsAspect
}
```
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.
* `Opt` — component may be present, but does not affect filtering (only caches the pool for access).
* `Any` — at least one of the components marked with `Any` must be present; caches the pool.
Explicit syntax (the result is identical to the example above):
``` c#
using DCFApixels.DragonECS;
@ -510,9 +540,9 @@ class Aspect : EcsAspect
public EcsPool<Velocity> velocities;
protected override void Init(Builder b)
{
poses = b.Include<Pose>();
velocities = b.Include<Velocity>();
b.Exclude<FreezedTag>();
poses = b.Inc<Pose>();
velocities = b.Inc<Velocity>();
b.Exc<FreezedTag>();
}
}
```
@ -536,17 +566,17 @@ class Aspect : EcsAspect
otherAspect1 = b.Combine<OtherAspect1>(1);
// Although Combine was called earlier for OtherAspect1, it will first combine with OtherAspect2 because the default order is 0.
otherAspect2 = b.Combine<OtherAspect2>();
// If b.Exclude<Pose>() was specified in OtherAspect1 or OtherAspect2, it will be replaced with b.Include<Pose>() here.
poses = b.Include<Pose>();
// If b.Exc<Pose>() was specified in OtherAspect1 or OtherAspect2, it will be replaced with b.Inc<Pose>() here.
poses = b.Inc<Pose>();
}
}
```
If there are conflicting constraints between the combined aspects, the new constraints will replace those added earlier. Constraints from the root aspect always replace constraints from added aspects. Here's a visual example of constraint combination:
| | cmp1 | cmp2 | cmp3 | cmp4 | cmp5 | разрешение конфликтных ограничений|
| | cmp1 | cmp2 | cmp3 | cmp4 | cmp5 | Conflict resolution |
| :--- | :--- | :--- | :--- | :--- | :--- |:--- |
| OtherAspect2 | :heavy_check_mark: | :x: | :heavy_minus_sign: | :heavy_minus_sign: | :heavy_check_mark: | |
| OtherAspect1 | :heavy_minus_sign: | :heavy_check_mark: | :heavy_minus_sign: | :x: | :heavy_minus_sign: | For `cmp2` will be chosen. :heavy_check_mark: |
| Aspect | :x: | :heavy_minus_sign: | :heavy_minus_sign: | :heavy_minus_sign: | :heavy_check_mark: | For `cmp1` will be chosen. :x: |
| 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. |
| Final Constraints | :x: | :heavy_check_mark: | :heavy_minus_sign: | :x: | :heavy_check_mark: | |
</details>
@ -984,6 +1014,23 @@ public struct WorldComponent : IEcsWorldComponent<WorldComponent>
## Released games:
<table>
<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>
<tr>
<td align="center">
<a href="https://yandex.ru/games/app/206024?utm_source=game_popup_menu">
@ -992,40 +1039,26 @@ public struct WorldComponent : IEcsWorldComponent<WorldComponent>
</a>
</td>
<td align="center">
<span>
_____________
<img tabindex="-1" src="https://github.com/user-attachments/assets/3fa1ca6d-29f6-43e6-aafe-cc9648d20490" alt="screenshot">
</span>
</td>
</tr>
</table>
</br>
# Extensions
* Packages:
* [Unity integration](https://github.com/DCFApixels/DragonECS-Unity)
* [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)
* [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)
> *Your extension? If you are developing an extension for DragonECS, you can share it [here](#feedback).
</br>
# FAQ
## 'ReadOnlySpan<>' could not be found
In Unity 2020.1.x, you may encounter this error in the console:
```
The type or namespace name 'ReadOnlySpan<>' could not be found (are you missing a using directive or an assembly reference?)
```
To fix this, add the define symbol `ENABLE_DUMMY_SPAN` to `Project Settings/Player/Other Settings/Scripting Define Symbols`.
## How to enable/disable systems?
Directly — you can't.
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. There are two solutions:
- If the process changes are global, 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. To do this create a new process interface, implement a runner for it, and obtain the runner via `EcsPipeline.GetRunner<T>()`.
## Recommendations list: [DragonECS-Vault](https://github.com/DCFApixels/DragonECS-Vault)
</br>
# Feedback