mirror of
https://github.com/DCFApixels/DragonECS.git
synced 2025-09-18 09:54:35 +08:00
update readme
This commit is contained in:
parent
e1dcbab935
commit
4ecf676fd7
30
README-RU.md
30
README-RU.md
@ -63,6 +63,7 @@ DragonECS - это [ECS](https://en.wikipedia.org/wiki/Entity_component_system)
|
||||
- [Процессы](#процессы)
|
||||
- [Мир](#мир)
|
||||
- [Пул](#пул)
|
||||
- [Маска](#маска)
|
||||
- [Аспект](#аспект)
|
||||
- [Запросы](#запросы)
|
||||
- [Коллекции](#Коллекции)
|
||||
@ -148,7 +149,7 @@ if (entity.TryGetID(out int entityID)) { }
|
||||
|
||||
</details>
|
||||
|
||||
> **NOTICE:** Сущности не могут существовать без компонентов, пустые сущности будут автоматически удаляться сразу после удаления последнего компонента либо в конце тика.
|
||||
> Сущности не могут существовать без компонентов, пустые сущности будут автоматически удаляться сразу после удаления последнего компонента либо в конце тика.
|
||||
## Component
|
||||
**Компоненты** - это данные которые крепятся к сущностям.
|
||||
```c#
|
||||
@ -296,7 +297,7 @@ EcsPipeline pipeline = EcsPipeline.New()
|
||||
* `EcsConst.END_LAYER`
|
||||
* `EcsConst.POST_END_LAYER`
|
||||
#### Порядок сортировки
|
||||
Для сортировки систем в рамках слоя используется int значение порядка сортировки. По умолчанию системы добавляются с sortOrder = 0.
|
||||
Для сортировки систем в рамках слоя используется int значение порядка сортировки. По умолчанию системы добавляются с `sortOrder = 0`.
|
||||
``` c#
|
||||
EcsPipeline pipeline = EcsPipeline.New()
|
||||
// ...
|
||||
@ -354,6 +355,7 @@ _pipeline.GetRunner<IDoSomethingProcess>.Do()
|
||||
// Или если раннер не был добавлен(Вызов GetRunnerInstance так же добавит раннер в пайплайн).
|
||||
_pipeline.GetRunnerInstance<DoSomethingProcessRunner>.Do()
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary>Расширенная реализация раннера</summary>
|
||||
|
||||
@ -361,11 +363,11 @@ _pipeline.GetRunnerInstance<DoSomethingProcessRunner>.Do()
|
||||
internal sealed class DoSomethingProcessRunner : EcsRunner<IDoSomethingProcess>, IDoSomethingProcess
|
||||
{
|
||||
// RunHelper упрощает реализацию по подобию реализации встроенных процессов.
|
||||
// Автоматически вызывает макрер профайлера, а так же содержит try catch блок.
|
||||
// Автоматически вызывает маркер профайлера, а так же содержит try catch блок.
|
||||
private RunHelper _helper;
|
||||
protected override void OnSetup()
|
||||
{
|
||||
// вторым аргументом задается имя маркера, если не указать, то имя будет выбрано автоматически.
|
||||
// Вторым аргументом задается имя маркера, если не указать, то имя будет выбрано автоматически.
|
||||
_helper = new RunHelper(this, nameof(Do));
|
||||
}
|
||||
public void Do()
|
||||
@ -383,6 +385,7 @@ internal sealed class DoSomethingProcessRunner : EcsRunner<IDoSomethingProcess>,
|
||||
> * Наследуемый класс `EcsRunner<T>`, должен так же реализовать интерфейс `T`;
|
||||
|
||||
> Не рекомендуется в цикле вызывать `GetRunner`, иначе кешируйте полученный раннер.
|
||||
|
||||
</details>
|
||||
|
||||
## Мир
|
||||
@ -485,7 +488,7 @@ EcsMask mask = EcsMask.New(_world)
|
||||
class SomeSystem : IEcsRun
|
||||
{
|
||||
// EcsStaticMask можно создавать в статических полях.
|
||||
static EcsStaticMask _staticMask = EcsStaticMask.Inc<SomeCmp1>().Inc<SomeCmp2>().Exc<SomeCmp3>().Build();
|
||||
static readonly EcsStaticMask _staticMask = EcsStaticMask.Inc<SomeCmp1>().Inc<SomeCmp2>().Exc<SomeCmp3>().Build();
|
||||
|
||||
// ...
|
||||
}
|
||||
@ -590,7 +593,7 @@ public class SomeDamageSystem : IEcsRun, IEcsInject<EcsDefaultWorld>
|
||||
public EcsPool<Health> healths = Inc;
|
||||
public EcsPool<DamageSignal> damageSignals = Inc;
|
||||
public EcsTagPool<IsInvulnerable> isInvulnerables = Exc;
|
||||
// Наличие или отсутвие этого компонента не проверяется.
|
||||
// Наличие или отсутствие этого компонента не проверяется.
|
||||
public EcsTagPool<IsDiedSignal> isDiedSignals = Opt;
|
||||
}
|
||||
EcsDefaultWorld _world;
|
||||
@ -859,17 +862,16 @@ EcsDebug.Set<OtherDebugService>();
|
||||
За реализацию профайлера так же отвечает Debug-сервис. Для выделения участка кода используется `EcsProfilerMarker`;
|
||||
``` c#
|
||||
// Создание маркера с именем SomeMarker.
|
||||
private static readonly EcsProfilerMarker marker = new EcsProfilerMarker("SomeMarker");
|
||||
|
||||
// ...
|
||||
|
||||
marker.Begin();
|
||||
private static readonly EcsProfilerMarker _marker = new EcsProfilerMarker("SomeMarker");
|
||||
```
|
||||
``` c#
|
||||
_marker.Begin();
|
||||
// Код для которого замеряется скорость.
|
||||
marker.End();
|
||||
_marker.End();
|
||||
|
||||
// или
|
||||
|
||||
using (marker.Auto())
|
||||
using (_marker.Auto())
|
||||
{
|
||||
// Код для которого замеряется скорость.
|
||||
}
|
||||
@ -997,7 +999,7 @@ public struct WorldComponent : IEcsWorldComponent<WorldComponent>
|
||||
</br>
|
||||
|
||||
# Расширения
|
||||
* Расширения:
|
||||
* Пакеты:
|
||||
* [Интеграция с движком Unity](https://github.com/DCFApixels/DragonECS-Unity)
|
||||
* [Автоматическое внедрение зависимостей](https://github.com/DCFApixels/DragonECS-AutoInjections)
|
||||
* [Классическая C# многопоточность](https://github.com/DCFApixels/DragonECS-ClassicThreads)
|
||||
|
175
README-ZH.md
175
README-ZH.md
@ -61,10 +61,11 @@ DragonECS 是一个[实体组件系统](https://www.imooc.com/article/331544)框
|
||||
- [初始化](#初始化)
|
||||
- [依赖注入](#依赖注入)
|
||||
- [模块](#模块)
|
||||
- [层级](#层级)
|
||||
- [排序](#排序)
|
||||
- [流程](#流程)
|
||||
- [世界](#世界)
|
||||
- [池子](#池子)
|
||||
- [掩码](#掩码)
|
||||
- [方面](#方面)
|
||||
- [查询](#查询)
|
||||
- [集合](#集合)
|
||||
@ -154,18 +155,17 @@ if (entity.TryGetID(out int entityID)) { }
|
||||
> **NOTICE:** 没有组件的实体不能存在,空实体会在最后一个组件被删除。
|
||||
|
||||
## 组件
|
||||
**组件**是实体的数据。必须实现`IEcsComponent`接口或其他指定类型的组件。
|
||||
**组件**是实体的数据。
|
||||
```c#
|
||||
// IEcsComponent 组件存储在普通的存储中。
|
||||
struct Health : IEcsComponent
|
||||
{
|
||||
public float health;
|
||||
public int armor;
|
||||
}
|
||||
// IEcsTagComponent 组件存储在为标签优化的存储中。
|
||||
struct PlayerTag : IEcsTagComponent {}
|
||||
```
|
||||
内置组件类型:
|
||||
* `IEcsComponent` - 包含数据的组件。 通用类型的组件。
|
||||
* `IEcsTagComponent` - 标签组件。 没有数据。
|
||||
|
||||
## 系统
|
||||
**系统**这是基本逻辑,这里定义了实体的行为。系统以用户类的形式实现,用户类至少要实现一个流程接口。基本流程:
|
||||
@ -274,7 +274,9 @@ EcsPipeline pipeline = EcsPipeline.New()
|
||||
.BuildAndInit();
|
||||
```
|
||||
|
||||
### 层级
|
||||
### 排序
|
||||
为了管理系统在管线中的位置,无论添加顺序如何,有两种方式:层级和排序顺序。
|
||||
#### 层级
|
||||
系统的队列可以分为层。层定义了队列中插入系统的位置。如果要在队列末尾插入一个系统,无论添加的地方如,可以把这个系统添加到 `EcsConsts.END_LAYER` 层级.
|
||||
``` c#
|
||||
const string SOME_LAYER = nameof(SOME_LAYER);
|
||||
@ -290,9 +292,20 @@ EcsPipeline pipeline = EcsPipeline.New()
|
||||
嵌入层按以下顺序排列:
|
||||
* `EcsConst.PRE_BEGIN_LAYER`
|
||||
* `EcsConst.BEGIN_LAYER`
|
||||
* `EcsConst.BASIC_LAYER`(如果在添加系统时没有指定层级,则会在这里添加)
|
||||
* `EcsConst.BASIC_LAYER`(默认情况下,系统添加到此层)
|
||||
* `EcsConst.END_LAYER`
|
||||
* `EcsConst.POST_END_LAYER`
|
||||
#### 排序顺序
|
||||
在同一层内,可以使用 `int` 类型的排序值来排序系统。默认情况下,系统的排序值为 `sortOrder = 0`。
|
||||
```c#
|
||||
EcsPipeline pipeline = EcsPipeline.New()
|
||||
// ...
|
||||
// 将 SomeSystem 系统插入到 EcsConsts.BEGIN_LAYER 层
|
||||
// 并且放置在排序值小于 10 的系统之后。
|
||||
.Add(New SomeSystem(), EcsConsts.BEGIN_LAYER, 10)
|
||||
// ...
|
||||
.BuildAndInit();
|
||||
```
|
||||
|
||||
## 流程
|
||||
流程是实现共同接口的系统队列,例如`IcsRun`接口。用于启动这些流程的是启动器。内置流程会自动启动。还可以实现用户流程。
|
||||
@ -324,8 +337,8 @@ sealed class DoSomethingProcessRunner : EcsRunner<IDoSomethingProcess>, IDoSomet
|
||||
foreach (var item in Process) item.Do();
|
||||
}
|
||||
}
|
||||
//...
|
||||
|
||||
```
|
||||
``` c#
|
||||
// 添加启动器到管线
|
||||
_pipeline = EcsPipeline.New()
|
||||
//...
|
||||
@ -339,12 +352,36 @@ _pipeline.GetRunner<IDoSomethingProcess>.Do()
|
||||
// 如果启动器尚未添加,使用 GetRunnerInstance 将其添加并运行
|
||||
_pipeline.GetRunnerInstance<DoSomethingProcessRunner>.Do()
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary>扩展的启动器实现</summary>
|
||||
|
||||
``` c#
|
||||
internal sealed class DoSomethingProcessRunner : EcsRunner<IDoSomethingProcess>, IDoSomethingProcess
|
||||
{
|
||||
// RunHelper 简化了实现,类似于内置流程的实现。
|
||||
// 自动调用分析器的标记,同时包含 try-catch 块。
|
||||
private RunHelper _helper;
|
||||
protected override void OnSetup()
|
||||
{
|
||||
// 第二个参数是标记的名称,如果不指定,将自动选择名称。
|
||||
_helper = new RunHelper(this, nameof(Do));
|
||||
}
|
||||
public void Do()
|
||||
{
|
||||
_helper.Run(p => p.Do());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
> 启动器的实现有一些要求:
|
||||
> * 必须直接继承自 `EcsRunner<T>`;
|
||||
> * 启动器只能包含一个接口(除了 `IEcsProcess` 接口);
|
||||
> * 继承的 `EcsRunner<T>,` 类必须实现接口 `T`;
|
||||
|
||||
不建议在循环中频繁调用 `GetRunner` 方法,建议缓存获取的启动器实例。
|
||||
> 不建议在循环中频繁调用 `GetRunner` 方法,建议缓存获取的启动器实例。
|
||||
</details>
|
||||
|
||||
## 世界
|
||||
@ -372,8 +409,8 @@ _world = new EcsDefaultWorld(config);
|
||||
|
||||
## 池子
|
||||
是组件的存储库,池子有添加/读取/编辑/删除实体上组件的方法。有几种类型的池,用于不同的目的:
|
||||
* `EcsPool` - 通用池,存储实现`IEcsComponent`接口的 struct 组件;
|
||||
* `EcsTagPool` - 专门用于存储实现`IEcsTagComponent`接口的空标签 struct 组件的池。存储的组件仅作为bool值存储,因此与EcsPool相比,具有更好的内存和速度优化;
|
||||
* `EcsPool` - 通用池,存储实现 `IEcsComponent` 接口的 struct 组件;
|
||||
* `EcsTagPool` - 为标签组件优化的特殊池,用于存储带有 `IEcsTagComponent` 的组件;
|
||||
|
||||
池有5种主要方法及其品种:
|
||||
``` c#
|
||||
@ -398,7 +435,40 @@ poses.Del(entityID);
|
||||
> 有一些 “安全 ”方法会首先检查组件是否存在,这些方法的名称以 “Try ”开头。
|
||||
|
||||
> 可以实现用户池。稍后将介绍这一功能。
|
||||
|
||||
|
||||
## 掩码
|
||||
用于根据组件的存在与否来过滤实体。
|
||||
``` c#
|
||||
// 创建一个掩码,检查实体是否具有组件
|
||||
// SomeCmp1 和 SomeCmp2,但没有组件 SomeCmp3。
|
||||
EcsMask mask = EcsMask.New(_world)
|
||||
// Inc - 组件存在的条件。
|
||||
.Inc<SomeCmp1>()
|
||||
.Inc<SomeCmp2>()
|
||||
// Exc - 组件不存在的条件。
|
||||
.Exc<SomeCmp3>()
|
||||
.Build();
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary>静态掩码</summary>
|
||||
|
||||
`EcsMask` 是与特定世界实例绑定的,需要将世界实例传递给 `EcsMask.New(world)`,但是也有 `EcsStaticMask`,它可以在不绑定到世界的情况下创建。
|
||||
|
||||
``` c#
|
||||
class SomeSystem : IEcsRun
|
||||
{
|
||||
// EcsStaticMask 可以在静态字段中创建。
|
||||
static readonly EcsStaticMask _staticMask = EcsStaticMask.Inc<SomeCmp1>().Inc<SomeCmp2>().Exc<SomeCmp3>().Build();
|
||||
|
||||
// ...
|
||||
}
|
||||
```
|
||||
``` c#
|
||||
// 转换为常规掩码。
|
||||
EcsMask mask = _staticMask.ToMask(_world);
|
||||
```
|
||||
|
||||
## 方面
|
||||
这些是继承自 EcsAspect 的用户类,用于与实体进行交互。方面同时充当池的缓存和实体组件的过滤掩码。可以把方面视为系统处理哪些实体的描述。
|
||||
|
||||
@ -473,16 +543,23 @@ class Aspect : EcsAspect
|
||||
</details>
|
||||
|
||||
## 查询
|
||||
要获取所需的实体集,需要使用 `EcsWorld.Where<TAspect>(out TAspect aspect)` 查询方法。在 TAspect 参数中指定的是一个方面,实体将按照指定方面的掩码进行过滤。`Where`查询既适用于`EcsWorld`也适用于框架的集合(在这方面,Where与Linq中的类似查询方式有些相似)。
|
||||
示例:
|
||||
过滤实体并返回满足特定条件的实体集合。内置查询 `Where` 通过组件掩码匹配条件进行过滤,并有多个重载版本:
|
||||
+ `EcsWorld.Where(EcsMask mask)` - 基于掩码的普通过滤;
|
||||
+ `EcsWorld.Where<TAspect>(out TAspect aspect)` - 结合了基于方面掩码的过滤和获取方面;
|
||||
|
||||
`Where` 查询既可以应用于 `EcsWorld`,也可以应用于框架的集合(在这方面,`Where` 有点类似于 Linq 中的 `Where`)。此外,还提供了根据 `Comparison<int>` 对实体进行排序的重载。
|
||||
|
||||
示例系统:
|
||||
``` c#
|
||||
public class SomeDamageSystem : IEcsRun, IEcsInject<EcsDefaultWorld>
|
||||
{
|
||||
class Aspect : EcsAspect
|
||||
{
|
||||
public EcsPool<Health> healths = Inc;
|
||||
public EcsPool<DamageSignal> damageSignals = Inc;
|
||||
public EcsPool<Health> healths = Inc;
|
||||
public EcsPool<DamageSignal> damageSignals = Inc;
|
||||
public EcsTagPool<IsInvulnerable> isInvulnerables = Exc;
|
||||
// 不检查此组件的存在与否。
|
||||
public EcsTagPool<IsDiedSignal> isDiedSignals = Opt;
|
||||
}
|
||||
EcsDefaultWorld _world;
|
||||
public void Inject(EcsDefaultWorld world) => _world = world;
|
||||
@ -492,11 +569,21 @@ public class SomeDamageSystem : IEcsRun, IEcsInject<EcsDefaultWorld>
|
||||
foreach (var e in _world.Where(out Aspect a))
|
||||
{
|
||||
// 在这里处理具有 Health 和 DamageSignal,但没有 IsInvulnerable 组件的实体。
|
||||
a.healths.Get(e).points -= a.damageSignals.Get(e).points;
|
||||
ref var health = ref a.healths.Get(e);
|
||||
if(health.points > 0)
|
||||
{
|
||||
health.points -= a.damageSignals.Get(e).points;
|
||||
if(health.points <= 0)
|
||||
{ // 向其他系统发送实体死亡信号。
|
||||
a.isDiedSignals.TryAdd(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
> 有一个简化查询语法和组件访问的[扩展](#扩展) - [简单语法](https://gist.github.com/DCFApixels/d7bfbfb8cb70d141deff00be24f28ff0)。
|
||||
|
||||
## 集合
|
||||
|
||||
@ -549,7 +636,7 @@ for (int i = 0; i < es.Count; i++)
|
||||
// ...
|
||||
}
|
||||
```
|
||||
由于组是没有重复元素的集合,因此组支持集合运算,并包含类似于`Iset<T>`的方法。编辑方法有两种方式:一种是将结果写入到 groupA 中,另一种是返回一个新的群组:
|
||||
由于组是没有重复元素的集合,因此组支持集合运算,并实现了接口 `ISet<int>`。编辑方法有两种方式:一种是将结果写入到 groupA 中,另一种是返回一个新的群组:
|
||||
|
||||
``` c#
|
||||
// 合集 groupA 和 groupB。
|
||||
@ -685,14 +772,17 @@ using DCFApixels.DragonECS;
|
||||
[MetaName("SomeComponent")]
|
||||
|
||||
// 用于对类型进行分组。
|
||||
[MetaGroup("Abilities/Passive/")] // 或者 [MetaGroup("Abilities", "Passive")]
|
||||
[MetaGroup("Abilities", "Passive", ...)] // 或者 [MetaGroup("Abilities/Passive/...")]
|
||||
|
||||
// 使用 RGB 编码设置显示颜色,每个通道的值范围从0到255,默认为白色。
|
||||
[MetaColor(MetaColor.Red)] // 或者 [MetaColor(255, 0, 0)]
|
||||
|
||||
// 为类型添加描述。
|
||||
[MetaDescription("The quick brown fox jumps over the lazy dog")]
|
||||
|
||||
|
||||
// 添加字符串唯一标识符.
|
||||
[MetaID("8D56F0949201D0C84465B7A6C586DCD6")] // 字符串必须是唯一的,并且不允许包含字符 ,<> 。
|
||||
|
||||
// 添加字符串标签。
|
||||
[MetaTags("Tag1", "Tag2", ...)] // 使用 [MetaTags(MetaTags.HIDDEN))] 可隐藏在编辑器中。
|
||||
public struct Component : IEcsComponent { /* ... */ }
|
||||
@ -703,12 +793,14 @@ TypeMeta typeMeta = someComponent.GetMeta();
|
||||
// 或者
|
||||
TypeMeta typeMeta = pool.ComponentType.ToMeta();
|
||||
|
||||
var name = typeMeta.Name;
|
||||
var color = typeMeta.Color;
|
||||
var description = typeMeta.Description;
|
||||
var group = typeMeta.Group;
|
||||
var tags = typeMeta.Tags;
|
||||
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]
|
||||
```
|
||||
> 为了自动生成唯一的标识符 MetaID,可以使用 `MetaID.GenerateNewUniqueID()` 方法和 [浏览器生成器](https://dcfapixels.github.io/DragonECS-MetaID_Generator_Online/)。
|
||||
|
||||
## EcsDebug
|
||||
具有调试和日志记录方法集. 实现为一个静态类,调用 DebugService 的方法. DebugService 是环境调试系统与 EcsDebug 之间的中介. 这使得可以将项目移植到其他引擎上,而无需修改项目的调试代码,只需要实现特定的 DebugService 即可。
|
||||
@ -732,17 +824,16 @@ EcsDebug.Set<OtherDebugService>();
|
||||
## 性能分析
|
||||
``` c#
|
||||
// 创建名为 SomeMarker 的标记器。
|
||||
private static readonly EcsProfilerMarker marker = new EcsProfilerMarker("SomeMarker");
|
||||
|
||||
// ...
|
||||
|
||||
marker.Begin();
|
||||
private static readonly EcsProfilerMarker _marker = new EcsProfilerMarker("SomeMarker");
|
||||
```
|
||||
``` c#
|
||||
_marker.Begin();
|
||||
// 要测量速度的代码。
|
||||
marker.End();
|
||||
_marker.End();
|
||||
|
||||
// 或者
|
||||
|
||||
using (marker.Auto())
|
||||
using (_marker.Auto())
|
||||
{
|
||||
// 要测量速度的代码。
|
||||
}
|
||||
@ -873,14 +964,16 @@ public struct WorldComponent : IEcsWorldComponent<WorldComponent>
|
||||
</br>
|
||||
|
||||
# 扩展
|
||||
* [Unity集成](https://github.com/DCFApixels/DragonECS-Unity)
|
||||
* [自动依赖注入](https://github.com/DCFApixels/DragonECS-AutoInjections)
|
||||
* [简单语法](https://gist.github.com/DCFApixels/d7bfbfb8cb70d141deff00be24f28ff0)
|
||||
* [单帧组件](https://gist.github.com/DCFApixels/46d512dbcf96c115b94c3af502461f60)
|
||||
* [经典C#多线程](https://github.com/DCFApixels/DragonECS-ClassicThreads)
|
||||
* [Hybrid](https://github.com/DCFApixels/DragonECS-Hybrid)
|
||||
* [IDE代码模板](https://gist.github.com/ctzcs/0ba948b0e53aa41fe1c87796a401660b) и [Unity代码模板](https://gist.github.com/ctzcs/d4c7730cf6cd984fe6f9e0e3f108a0f1)
|
||||
* Graphs (Work in progress)
|
||||
* Packages:
|
||||
* [Unity集成](https://github.com/DCFApixels/DragonECS-Unity)
|
||||
* [自动依赖注入](https://github.com/DCFApixels/DragonECS-AutoInjections)
|
||||
* [经典C#多线程](https://github.com/DCFApixels/DragonECS-ClassicThreads)
|
||||
* [Hybrid](https://github.com/DCFApixels/DragonECS-Hybrid)
|
||||
* Graphs (Work in progress)
|
||||
* 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的扩展,可以[在此处发布](#反馈).
|
||||
|
||||
|
212
README.md
212
README.md
@ -53,36 +53,37 @@ The [ECS](https://en.wikipedia.org/wiki/Entity_component_system) Framework aims
|
||||
> If there are unclear points, you can ask questions here [Feedback](#Feedback)
|
||||
|
||||
## Table of Contents
|
||||
- [Installation](#Installation)
|
||||
- [Basic Concepts](#Basic-Concepts)
|
||||
- [Installation](#installation)
|
||||
- [Basic Concepts](#basic-concepts)
|
||||
- [Entity](#entity)
|
||||
- [Component](#component)
|
||||
- [System](#system)
|
||||
- [Framework Concepts](#Framework-Concepts)
|
||||
- [Pipeline](#Pipeline)
|
||||
- [Building](#Building)
|
||||
- [Dependency Injection](#Dependency-Injection)
|
||||
- [Modules](#Modules)
|
||||
- [Layers](#Layers)
|
||||
- [Framework Concepts](#framework-concepts)
|
||||
- [Pipeline](#pipeline)
|
||||
- [Building](#building)
|
||||
- [Dependency Injection](#dependency-injection)
|
||||
- [Modules](#modules)
|
||||
- [Sorting](#sorting)
|
||||
- [Processes](#Processes)
|
||||
- [World](#World)
|
||||
- [Pool](#Pool)
|
||||
- [Aspect](#Aspect)
|
||||
- [Queries](#Queries)
|
||||
- [Collections](#Collections)
|
||||
- [Mask](#mask)
|
||||
- [Aspect](#aspect)
|
||||
- [Queries](#queries)
|
||||
- [Collections](#collections)
|
||||
- [ECS Root](#ecs-root)
|
||||
- [Debug](#debug)
|
||||
- [Meta Attributes](#Meta-Attributes)
|
||||
- [Meta Attributes](#meta-attributes)
|
||||
- [EcsDebug](#ecsdebug)
|
||||
- [Profiling](#Profiling)
|
||||
- [Profiling](#profiling)
|
||||
- [Define Symbols](#define-symbols)
|
||||
- [Framework Extension Tools](#Framework-Extension-tools)
|
||||
- [World Components](#World-Components)
|
||||
- [Configs](#Configs)
|
||||
- [Projects powered by DragonECS](#Projects-powered-by-DragonECS)
|
||||
- [Extensions](#Extensions)
|
||||
- [Framework Extension Tools](#framework-extension-tools)
|
||||
- [World Components](#world-components)
|
||||
- [Configs](#configs)
|
||||
- [Projects powered by DragonECS](#projects-powered-by-dragonecs)
|
||||
- [Extensions](#extensions)
|
||||
- [FAQ](#faq)
|
||||
- [Feedback](#Feedback)
|
||||
- [Feedback](#feedback)
|
||||
|
||||
</br>
|
||||
|
||||
@ -152,21 +153,20 @@ if (entity.TryGetID(out int entityID)) { }
|
||||
|
||||
</details>
|
||||
|
||||
> **NOTICE:** Entities cannot exist without components, empty entities will be automatically deleted immediately after the last component is deleted.
|
||||
> Entities cannot exist without components, empty entities will be automatically deleted immediately after the last component is deleted.
|
||||
|
||||
## Component
|
||||
Data for entities. Must implement the ``IEcsComponent`` interface or other specifying type of component.
|
||||
Data for entities.
|
||||
```c#
|
||||
// IEcsComponent components are stored in regular storage.
|
||||
struct Health : IEcsComponent
|
||||
{
|
||||
public float health;
|
||||
public int armor;
|
||||
}
|
||||
// Components with IEcsTagComponent are stored in tag-optimized storage.
|
||||
struct PlayerTag : IEcsTagComponent {}
|
||||
```
|
||||
Built-in component types:
|
||||
* `IEcsComponent` - Components with data. Universal component type.
|
||||
* `IEcsTagComponent` - Tag components. Components without data.
|
||||
|
||||
## System
|
||||
Represent the core logic defining entity behaviors. They are implemented as user-defined classes that implement at least one of the process interfaces. Key processes include:
|
||||
@ -278,7 +278,9 @@ EcsPipeline pipeline = EcsPipeline.New()
|
||||
.BuildAndInit();
|
||||
```
|
||||
|
||||
### Layers
|
||||
### Sorting
|
||||
To manage the position of systems in the pipeline, regardless of the order in which they are added, there are two methods: Layers and Sorting Order.
|
||||
#### Layers
|
||||
Queues in the system can be segmented into layers. A layer defines a position in the queue for inserting systems. For example, if a system needs to be inserted at the end of the queue regardless of where it is added, you can add this system to the `EcsConsts.END_LAYER` layer.
|
||||
``` c#
|
||||
const string SOME_LAYER = nameof(SOME_LAYER);
|
||||
@ -297,6 +299,18 @@ The built-in layers are arranged in the following order:
|
||||
* `EcsConst.BASIC_LAYER` (Systems are added here if no layer is specified during addition)
|
||||
* `EcsConst.END_LAYER`
|
||||
* `EcsConst.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`.
|
||||
|
||||
```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.
|
||||
.Add(New SomeSystem(), EcsConsts.BEGIN_LAYER, 10)
|
||||
// ...
|
||||
.BuildAndInit();
|
||||
```
|
||||
|
||||
## Processes
|
||||
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. It is possible to implement custom processes.
|
||||
@ -328,8 +342,8 @@ sealed class DoSomethingProcessRunner : EcsRunner<IDoSomethingProcess>, IDoSomet
|
||||
foreach (var item in Process) item.Do();
|
||||
}
|
||||
}
|
||||
// ...
|
||||
|
||||
```
|
||||
``` c#
|
||||
// Adding the runner when creating the pipeline
|
||||
_pipeline = EcsPipeline.New()
|
||||
//...
|
||||
@ -343,12 +357,38 @@ _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()
|
||||
```
|
||||
|
||||
<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;
|
||||
private RunHelper _helper;
|
||||
protected override void OnSetup()
|
||||
{
|
||||
// The second argument specifies the name of the marker, if not specified, the name will be chosen automatically.
|
||||
_helper = new RunHelper(this, nameof(Do));
|
||||
}
|
||||
public void Do()
|
||||
{
|
||||
_helper.Run(p => p.Do());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
> Runners have several implementation requirements:
|
||||
> * Inheritance from `EcsRunner<T>` must be direct.
|
||||
> * Runner can only contain one interface (except `IEcsProcess`);
|
||||
> * The inheriting class of `EcsRunner<T>,` must also implement the `T` interface;
|
||||
|
||||
> It's not recommended to call `GetRunner` in a loop; instead, cache the retrieved runner instance.
|
||||
|
||||
</details>
|
||||
|
||||
## World
|
||||
@ -377,7 +417,7 @@ _world = new EcsDefaultWorld(config);
|
||||
## Pool
|
||||
Stash of components, providing methods for adding, reading, editing, and removing components on entities. There are several types of pools designed for different purposes:
|
||||
* `EcsPool` - universal pool, stores struct components implementing the `IEcsComponent` interface;
|
||||
* `EcsTagPool` - special pool for empty tag components, stores struct-components with `IEcsTagComponent` as bool values, which in comparison with `EcsPool` implementation has better memory and speed optimization;
|
||||
* `EcsTagPool` - special pool optimized for tag components, stores struct-components with `IEcsTagComponent`;
|
||||
|
||||
Pools have 5 main methods and their variations:
|
||||
``` c#
|
||||
@ -402,6 +442,41 @@ poses.Del(entityID);
|
||||
> There are "safe" methods that first perform a check for the presence or absence of a component. Such methods are prefixed with `Try`.
|
||||
|
||||
> 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.
|
||||
``` 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>
|
||||
|
||||
`EcsMask` is tied to specific world instances, which need to be passed to `EcsMask.New(world)`, but there is also `EcsStaticMask`, which can be created without being tied to a world.
|
||||
|
||||
``` 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>
|
||||
|
||||
## 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.
|
||||
@ -477,16 +552,23 @@ If there are conflicting constraints between the combined aspects, the new const
|
||||
</details>
|
||||
|
||||
## Queries
|
||||
To get the set of required entities, there is a query method `EcsWorld.Where<TAspect>(out TAspect aspect)`. Aspect is specified as `TAspect`, the entities will be filtered by the mask of the specified aspect. The `Where` query is applicable to both `EcsWorld` and framework collections (in this respect, Where is somewhat similar to a similar one from Linq).
|
||||
Example:
|
||||
Filter entities and return collections of entities that matching conditions. The built-in `Where` query filters by component mask matching and has several overloads:
|
||||
+ `EcsWorld.Where(EcsMask mask)` - Standard filtering by mask;
|
||||
+ `EcsWorld.Where<TAspect>(out TAspect aspect)` - Combines filtering by aspect mask and aspect return;
|
||||
|
||||
The `Where` query can be applied to both `EcsWorld` and framework collections (in this sense, `Where` is somewhat similar to the one in Linq). There are also overloads for sorting entities using `Comparison<int>`.
|
||||
|
||||
Example system:
|
||||
``` c#
|
||||
public class SomeDamageSystem : IEcsRun, IEcsInject<EcsDefaultWorld>
|
||||
{
|
||||
class Aspect : EcsAspect
|
||||
{
|
||||
public EcsPool<Health> healths = Inc;
|
||||
public EcsPool<DamageSignal> damageSignals = Inc;
|
||||
public EcsPool<Health> healths = Inc;
|
||||
public EcsPool<DamageSignal> damageSignals = Inc;
|
||||
public EcsTagPool<IsInvulnerable> isInvulnerables = Exc;
|
||||
// The presence or absence of this component is not checked.
|
||||
public EcsTagPool<IsDiedSignal> isDiedSignals = Opt;
|
||||
}
|
||||
EcsDefaultWorld _world;
|
||||
public void Inject(EcsDefaultWorld world) => _world = world;
|
||||
@ -495,12 +577,22 @@ public class SomeDamageSystem : IEcsRun, IEcsInject<EcsDefaultWorld>
|
||||
{
|
||||
foreach (var e in _world.Where(out Aspect a))
|
||||
{
|
||||
// Сюда попадают сущности с компонентами Health, DamageSignal и без IsInvulnerable.
|
||||
a.healths.Get(e).points -= a.damageSignals.Get(e).points;
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
> You can use an [Extension](#extensions) to simplify query syntax and interactions with components - [Simplified Syntax](https://github.com/DCFApixels/DragonECS-AutoInjections).
|
||||
|
||||
## Collections
|
||||
|
||||
@ -553,7 +645,7 @@ for (int i = 0; i < group.Count; i++)
|
||||
// ...
|
||||
}
|
||||
```
|
||||
Since groups are sets, they have methods similar to `ISet<T>`. Editing methods have 2 variants: either they modify `groupA` directly or return a new group:
|
||||
Groups are sets and implement the `ISet<int>` interface. The editing methods have two variants: one that writes the result to `groupA`, and another that returns a new group.
|
||||
|
||||
``` c#
|
||||
// Union of groupA and groupB.
|
||||
@ -689,14 +781,17 @@ using DCFApixels.DragonECS;
|
||||
[MetaName("SomeComponent")]
|
||||
|
||||
// Used for grouping types.
|
||||
[MetaGroup("Abilities/Passive/")] // or [MetaGroup("Abilities", "Passive")]
|
||||
[MetaGroup("Abilities", "Passive", ...)] // or [MetaGroup("Abilities/Passive/...")]
|
||||
|
||||
// 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")]
|
||||
|
||||
|
||||
// Adds a string unique identifier.
|
||||
[MetaID("8D56F0949201D0C84465B7A6C586DCD6")] // Strings must be unique and cannot contain characters ,<> .
|
||||
|
||||
// Adds string tags to the type.
|
||||
[MetaTags("Tag1", "Tag2", ...)] // [MetaTags(MetaTags.HIDDEN))] to hide in the editor
|
||||
public struct Component : IEcsComponent { /* ... */ }
|
||||
@ -707,12 +802,14 @@ TypeMeta typeMeta = someComponent.GetMeta();
|
||||
// or
|
||||
TypeMeta typeMeta = pool.ComponentType.ToMeta();
|
||||
|
||||
var name = typeMeta.Name;
|
||||
var color = typeMeta.Color;
|
||||
var description = typeMeta.Description;
|
||||
var group = typeMeta.Group;
|
||||
var tags = typeMeta.Tags;
|
||||
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]
|
||||
```
|
||||
> To automatically generate unique identifiers MetaID, there is the method `MetaID.GenerateNewUniqueID()` and the [Browser Generator](https://dcfapixels.github.io/DragonECS-MetaID_Generator_Online/).
|
||||
|
||||
## EcsDebug
|
||||
Has a set of methods for debugging and logging. It is implemented as a static class calling methods of Debug services. Debug services are intermediaries between the debugging systems of the environment and EcsDebug. This allows projects to be ported to other engines without modifying the debug code, by implementing the corresponding Debug service.
|
||||
@ -736,17 +833,16 @@ EcsDebug.Set<OtherDebugService>();
|
||||
## Profiling
|
||||
``` c#
|
||||
// Creating a marker named SomeMarker.
|
||||
private static readonly EcsProfilerMarker marker = new EcsProfilerMarker("SomeMarker");
|
||||
|
||||
// ...
|
||||
|
||||
marker.Begin();
|
||||
private static readonly EcsProfilerMarker _marker = new EcsProfilerMarker("SomeMarker");
|
||||
```
|
||||
``` c#
|
||||
_marker.Begin();
|
||||
// Code whose execution time is being measured.
|
||||
marker.End();
|
||||
_marker.End();
|
||||
|
||||
// or
|
||||
|
||||
using (marker.Auto())
|
||||
using (_marker.Auto())
|
||||
{
|
||||
// Code whose execution time is being measured.
|
||||
}
|
||||
@ -875,14 +971,16 @@ public struct WorldComponent : IEcsWorldComponent<WorldComponent>
|
||||
</br>
|
||||
|
||||
# Extensions
|
||||
* [Unity integration](https://github.com/DCFApixels/DragonECS-Unity)
|
||||
* [Dependency autoinjections](https://github.com/DCFApixels/DragonECS-AutoInjections)
|
||||
* [Simple syntax](https://gist.github.com/DCFApixels/d7bfbfb8cb70d141deff00be24f28ff0)
|
||||
* [One-Frame Components](https://gist.github.com/DCFApixels/46d512dbcf96c115b94c3af502461f60)
|
||||
* [Classic C# multithreading](https://github.com/DCFApixels/DragonECS-ClassicThreads)
|
||||
* [Hybrid](https://github.com/DCFApixels/DragonECS-Hybrid)
|
||||
* [Code Templates for IDE](https://gist.github.com/ctzcs/0ba948b0e53aa41fe1c87796a401660b) and [for Unity](https://gist.github.com/ctzcs/d4c7730cf6cd984fe6f9e0e3f108a0f1)
|
||||
* Graphs (Work in progress)
|
||||
* 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)
|
||||
* [Hybrid](https://github.com/DCFApixels/DragonECS-Hybrid)
|
||||
* Graphs (Work in progress)
|
||||
* 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 Dragoness, you can share it [here](#feedback).
|
||||
|
||||
</br>
|
||||
|
Loading…
Reference in New Issue
Block a user