remove IEntityTemplate / update readme

This commit is contained in:
Mikhail 2026-03-02 17:01:46 +08:00
parent dc0aae1c67
commit 9aa4d21e79
9 changed files with 57 additions and 122 deletions

View File

@ -48,10 +48,10 @@
# Оглавление # Оглавление
- [Установка](#установка) - [Установка](#установка)
- [Debug](#debug) - [Debug](#debug)
- [Debug Модуль](#debug-модуль)
- [Debug Сервис](#debug-сервис) - [Debug Сервис](#debug-сервис)
- [Debug Модуль](#debug-модуль)
- [Визуальная отладка](#визуальная-отладка) - [Визуальная отладка](#визуальная-отладка)
- [Шаблон Сущности](#шаблон-сущности) - [Шаблоны](#шаблоны)
- [Связь с GameObject](#связь-с-gameobject) - [Связь с GameObject](#связь-с-gameobject)
- [World Provider](#world-provider) - [World Provider](#world-provider)
- [Шаблон Пайплайна](#шаблон-пайплайна) - [Шаблон Пайплайна](#шаблон-пайплайна)
@ -85,21 +85,8 @@ https://github.com/DCFApixels/DragonECS-Unity.git
</br> </br>
# Debug # Debug
## Debug Модуль
Подключение модуля отладки в Unity.
```c#
EcsDefaultWorld _world = new EcsDefaultWorld();
EcsEventWorld _eventWorld = new EcsDefaultWorld();
_pipeline = EcsPipeline.New()
//...
// Подключение и инициализация отладки для миров _world и _eventWorld
.AddUnityDebug(_world, _eventWorld)
//...
.BuildAndInit();
```
## Debug Сервис ## Debug Сервис
`UnityDebugService`- реализация [Debug-сервиса для `EcsDebug`](https://github.com/DCFApixels/DragonECS/blob/main/README-RU.md#ecsdebug). В редакторе по умолчанию автоматически инициализируется и связывает `EcsDebug.Print` с консолью Unity, `EcsProfilerMarker` c профайлером и т.д. `UnityDebugService` - реализация [Debug-сервиса для `EcsDebug`](https://github.com/DCFApixels/DragonECS/blob/main/README-RU.md#ecsdebug). В редакторе он инициализируется автоматически и обеспечивает интеграцию: например, вызовы `EcsDebug.Print` направляются в консоль Unity, а `EcsProfilerMarker` подключается к встроенному профайлеру и т.д.
```c# ```c#
//Ручная активация. //Ручная активация.
UnityDebugService.Activate(); UnityDebugService.Activate();
@ -115,8 +102,19 @@ someMarker.End();
//Остановка игрового режима. //Остановка игрового режима.
EcsDebug.Break(); EcsDebug.Break();
``` ```
## Визуальная отладка ## Визуальная отладка
Выполнена в виде специальных объектов-мониторов в которых отображается состояние разных аспектов фреймворка. Найти эти мониторы можно в Play Mode в разделе `DontDestroyOnLoad`.
Выполнена в виде специальных объектов-мониторов в которых отображается состояние разных частей фреймворка. Найти эти мониторы можно в `Play Mode` в разделе `DontDestroyOnLoad`.
```c#
_pipeline = EcsPipeline.New()
//...
// Инициализация отладки для пайплайна и миров
.AddUnityDebug(_world, _eventWorld)
//...
.BuildAndInit();
```
<p align="center"> <p align="center">
<img src="https://github.com/DCFApixels/DragonECS-Unity/assets/99481254/54e3f6d1-13c4-4226-a983-c672a29d33bb"> <img src="https://github.com/DCFApixels/DragonECS-Unity/assets/99481254/54e3f6d1-13c4-4226-a983-c672a29d33bb">
@ -143,7 +141,7 @@ EcsDebug.Break();
----- -----
* ### `WorldMonitor` * ### `WorldMonitor`
Показывает состояние `EcsWorld`. на каждый казанный мир создается отдельный монитор. Показывает состояние `EcsWorld`. на каждый мир, переданный в `AddUnityDebug(...)`, создается отдельный монитор.
<p align="center"> <p align="center">
<img src="https://github.com/DCFApixels/DragonECS-Unity/assets/99481254/7b6455fc-9211-425c-b0b8-288077e61543"> <img src="https://github.com/DCFApixels/DragonECS-Unity/assets/99481254/7b6455fc-9211-425c-b0b8-288077e61543">
@ -162,26 +160,11 @@ EcsDebug.Break();
</br> </br>
# Шаблон Сущности # Шаблоны
Настраиваемый набор компонентов которые можно применить к сущностям. Шаблоны должны реализовывать интерфейс `ITemplateNode`. Интеграция содержит шаблоны расширяющие `ITemplateNode`, предназначенные для настройки сущностей из редактора.
```c#
ITemplateNode someTemplate = /*...*/;
//...
foreach (var e in _world.Where(out Aspect a))
{
// Применение шаблона сущности.
someTemplate.Apply(e, _world.id);
}
```
```c#
// Применение шаблона сразу при создании сущности.
int e = _world.NewEntity(someTemplate);
```
По умолчанию расширение содержит 2 вида шаблонов: `ScriptableEntityTemplate`, `MonoEntityTemplate`.
## ScriptableEntityTemplate ## ScriptableEntityTemplate
Хранится как отдельный ассет. Наследуется от `ScriptableObject`. Хранится как отдельный ассет. Наследуется от `ScriptableObject`.
Действия чтобы создать `ScriptableEntityTemplate` ассет:
<details> <details>
<summary>Создать ассет: Asset > Create > DragonECS > ScriptableEntityTemplate.</summary> <summary>Создать ассет: Asset > Create > DragonECS > ScriptableEntityTemplate.</summary>
@ -222,8 +205,9 @@ int e = _world.NewEntity(someTemplate);
## Шаблон компонента ## Шаблон компонента
Чтобы компонент попал в меню `Add Component` требуется шаблон. Шаблоны компонента это типы реализующие `IComponentTemplate`.
### Реализация ### Реализация
Чтобы компонент попал в меню `Add Component` нужно реализовать шаблон компонента. Шаблоны компонента это типы реализующие `IComponentTemplate`.
* Упрощенная реализация: * Упрощенная реализация:
```c# ```c#
@ -243,6 +227,8 @@ class SomeTagComponentTemplate : TagComponentTemplate<SomeComponent> { }
<details> <details>
<summary>* Полная реализация:</summary> <summary>* Полная реализация:</summary>
Если не подходят `ComponentTemplate<T>` или `TagComponentTemplate<T>`, можно напрямую реализовать интерфейс `IComponentTemplate`. Например это может пригодиться для работы в связке с кастомной реализацией пула. В большинстве случаев достаточно упрощенной.
```c# ```c#
[Serializable] [Serializable]
struct SomeComponent : IEcsComponent { /* ... */ } struct SomeComponent : IEcsComponent { /* ... */ }
@ -251,6 +237,7 @@ class SomeComponentTemplate : IComponentTemplate
[SerializeField] [SerializeField]
protected SomeComponent component; protected SomeComponent component;
public Type Type { get { return typeof(SomeComponent); } } public Type Type { get { return typeof(SomeComponent); } }
public bool IsUnique { get { return true; } }
public void Apply(int worldID, int entityID) public void Apply(int worldID, int entityID)
{ {
EcsWorld.GetPoolInstance<EcsPool<SomeComponent>>(worldID).TryAddOrGet(entityID) = component; EcsWorld.GetPoolInstance<EcsPool<SomeComponent>>(worldID).TryAddOrGet(entityID) = component;
@ -268,28 +255,37 @@ class SomeComponentTemplate : IComponentTemplate
### Кастомизация отображения типов ### Кастомизация отображения типов
В раскрывающемся при нажатии `Add Component` меню выбора компонента поддерживается иерархическое группирование. Производится группирование на основе мета-атрибута `[MetaGroup]`. В раскрывающемся при нажатии `Add Component` меню выбора компонента поддерживается иерархическое группирование. Производится группирование на основе мета-атрибута `[MetaGroup]`.
Компоненты в инспекторе по умолчанию отображаются окрашенными в случайный цвет сгенерированный на основе имени компонента, выбрать другой режим окраски можно в [окне настроек](#окно-настроек) фреймворка. Задать конкретный цвет можно при помощи мета-атрибута `[MetaColor]`. Компоненты в инспекторе по умолчанию отображаются со случайным цветом, зависящим от его имени, выбрать другой режим окраски можно в [окне настроек](#окно-настроек) фреймворка. Задать конкретный цвет можно при помощи мета-атрибута `[MetaColor]`.
Если редактор смог автоматически определить связанный с компонентом скрипт, то слева от крестика удаления компонента будет иконка файла. Клик по иконке выделит файл скрипта в папке проекта, двойной клик откроет скрип для редактирования. Связанный файл ищется по сопоставлению имени типа и имени файла скрипта. Если интеграции удается найти соответствующий скрипт (по совпадению имени типа и файла, либо при наличии `[MetaID]`), рядом с крестиком удаления появляется иконка файла — клик выделяет скрипт в проекте, двойной клик открывает его.
Если у компонента есть мета-атрибут `[MetaDescription]`, то слева от крестика удаления компонента будет иконка подсказки, при наведении курсора покажется информация из `[MetaDescription]`. При наличии атрибута `[MetaDescription]` показывается иконка подсказки с текстом из него.
</br> </br>
### Применение шаблонов компонентов вне стандартных шаблонов сущностей ### Применение шаблонов компонентов вне стандартных шаблонов сущностей
При необходимости создания пользовательского шаблона, шаблоны компонентов поддерживают отображение вне стандартных `MonoEntityTemplate` и `ScriptableEntityTemplate`. Шаблоны компонентов можно использовать не только внутри стандартных `MonoEntityTemplate` и `ScriptableEntityTemplate`, но и в любых пользовательских классах. Для этого предусмотрены два способа:
Атрибут `[ComponentTemplateReference]`:
```c# ```c#
// ComponentTemplateReference добавляет кнопку выбора доступной реализации IComponentTemplate // Добавляет кнопку выбора доступной реализации IComponentTemplate
// и отображает шаблон компонента аналогично компонентам в MonoEntityTemplate или ScriptableEntityTemplate. // и отображает шаблон компонента аналогично компонентам в MonoEntityTemplate или ScriptableEntityTemplate.
[SerializeReference, ComponentTemplateReference] [SerializeReference, ComponentTemplateReference]
private IComponentTempalte _someComponent1; private IComponentTemplate _someComponent1;
```
// Обертка над IComponentTempalte, которая работает аналогично примеру с атрибутом ComponentTemplateReference. Обертка `ComponentTemplateProperty`:
```c#
// Обертка над IComponentTemplate, аналогично примеру с атрибутом ComponentTemplateReference.
private ComponentTemplateProperty _someComponent2; private ComponentTemplateProperty _someComponent2;
```
// Все это работает и для массивов. Оба подхода работают и для массивов:
```c#
[SerializeReference, ComponentTemplateReference] [SerializeReference, ComponentTemplateReference]
private IComponentTempalte[] _components; private IComponentTemplate[] _components;
// или
private ComponentTemplateProperty[] _components;
``` ```
</br> </br>

View File

@ -113,9 +113,9 @@ namespace DCFApixels.DragonECS
{ {
get { return _monoTemplates; } get { return _monoTemplates; }
} }
public IEnumerable<IEntityTemplate> AllTemplates public IEnumerable<ITemplateNode> AllTemplates
{ {
get { return ((IEnumerable<IEntityTemplate>)_scriptableTemplates).Concat(_monoTemplates); } get { return ((IEnumerable<ITemplateNode>)_scriptableTemplates).Concat(_monoTemplates); }
} }
#endregion #endregion

View File

@ -84,6 +84,12 @@ namespace DCFApixels.DragonECS
public static class GameObjectRefExt public static class GameObjectRefExt
{ {
public static entlong NewEntityWithGameObject(this EcsWorld world, ITemplateNode template, string name = "Entity", GameObjectIcon icon = GameObjectIcon.NONE)
{
entlong e = world.NewEntityWithGameObject(name, icon);
template.Apply(world.ID, e.ID);
return e;
}
public static entlong NewEntityWithGameObject(this EcsWorld self, string name = "Entity", GameObjectIcon icon = GameObjectIcon.NONE) public static entlong NewEntityWithGameObject(this EcsWorld self, string name = "Entity", GameObjectIcon icon = GameObjectIcon.NONE)
{ {
entlong result = self.NewEntityLong(); entlong result = self.NewEntityLong();

View File

@ -33,7 +33,6 @@ namespace DCFApixels.DragonECS.Unity.Editors
private static ComponentTemplatesDropDown _componentDropDown; private static ComponentTemplatesDropDown _componentDropDown;
#region Properties #region Properties
private float SingleLineWithPadding => OneLineHeight + Padding * 4f;
private float Padding => Spacing; private float Padding => Spacing;
protected override bool IsStaticInit => _componentDropDown != null; protected override bool IsStaticInit => _componentDropDown != null;
#endregion #endregion

View File

@ -7,7 +7,7 @@ using UnityEngine;
namespace DCFApixels.DragonECS.Unity.Editors namespace DCFApixels.DragonECS.Unity.Editors
{ {
internal abstract class EntityTemplateEditorBase : ExtendedEditor<IEntityTemplateInternal> internal abstract class EntityTemplateEditorBase : ExtendedEditor<ITemplateNode>
{ {
private ComponentTemplatesDropDown _componentDropDown; private ComponentTemplatesDropDown _componentDropDown;
@ -17,15 +17,13 @@ namespace DCFApixels.DragonECS.Unity.Editors
protected abstract bool IsSO { get; } protected abstract bool IsSO { get; }
//public virtual bool IsStaticData { get { return false; } }
#region Init #region Init
protected override bool IsInit { get { return _componentDropDown != null; } } protected override bool IsInit { get { return _componentDropDown != null; } }
protected override void OnInit() protected override void OnInit()
{ {
_componentDropDown = new ComponentTemplatesDropDown(); _componentDropDown = new ComponentTemplatesDropDown();
_componentsProp = serializedObject.FindProperty(Target.ComponentsPropertyName); _componentsProp = serializedObject.FindProperty("_componentTemplates");
_reorderableComponentsList = new ReorderableList(serializedObject, _componentsProp, true, false, false, false); _reorderableComponentsList = new ReorderableList(serializedObject, _componentsProp, true, false, false, false);
_reorderableComponentsList.onAddCallback += OnReorderableComponentsListAdd; _reorderableComponentsList.onAddCallback += OnReorderableComponentsListAdd;
@ -142,19 +140,6 @@ namespace DCFApixels.DragonECS.Unity.Editors
#endregion #endregion
#region Add/Remove
private void OnRemoveComponentAt(int index)
{
if (this.target is IEntityTemplateInternal target)
{
SerializedProperty componentsProp = serializedObject.FindProperty(target.ComponentsPropertyName);
componentsProp.DeleteArrayElementAtIndex(index);
serializedObject.ApplyModifiedProperties();
EditorUtility.SetDirty(this.target);
}
}
#endregion
protected override void DrawCustom() protected override void DrawCustom()
{ {
Init(); Init();
@ -204,7 +189,7 @@ namespace DCFApixels.DragonECS.Unity.Editors
{ {
using (EcsGUI.Layout.BeginVertical(UnityEditorUtility.GetTransperentBlackBackgrounStyle())) using (EcsGUI.Layout.BeginVertical(UnityEditorUtility.GetTransperentBlackBackgrounStyle()))
{ {
DrawTop(Target, _componentsProp); DrawTop(_componentsProp);
_reorderableComponentsList.DoLayoutList(); _reorderableComponentsList.DoLayoutList();
} }
} }
@ -214,7 +199,7 @@ namespace DCFApixels.DragonECS.Unity.Editors
} }
} }
} }
private void DrawTop(IEntityTemplateInternal target, SerializedProperty componentsProp) private void DrawTop(SerializedProperty componentsProp)
{ {
GUILayout.Space(2f); GUILayout.Space(2f);

View File

@ -1,26 +0,0 @@
#if DISABLE_DEBUG
#undef DEBUG
#endif
namespace DCFApixels.DragonECS
{
public interface IEntityTemplate : ITemplateNode { }
public static class ITemplateNodeExtensions
{
public static entlong NewEntityWithGameObject(this EcsWorld world, ITemplateNode template, string name = "Entity", GameObjectIcon icon = GameObjectIcon.NONE)
{
entlong e = world.NewEntityWithGameObject(name, icon);
template.Apply(world.ID, e.ID);
return e;
}
}
}
namespace DCFApixels.DragonECS.Unity.Internal
{
internal interface IEntityTemplateInternal : IEntityTemplate
{
string ComponentsPropertyName { get; }
}
}

View File

@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 33a7d50d86178eb43a36d5e8bdd70982
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -11,7 +11,7 @@ using UnityEngine.Serialization;
namespace DCFApixels.DragonECS namespace DCFApixels.DragonECS
{ {
public abstract class MonoEntityTemplateBase : MonoBehaviour, IEntityTemplate public abstract class MonoEntityTemplateBase : MonoBehaviour, ITemplateNode
{ {
public abstract void Apply(short worldID, int entityID); public abstract void Apply(short worldID, int entityID);
} }
@ -22,20 +22,13 @@ namespace DCFApixels.DragonECS
[MetaGroup(EcsUnityConsts.PACK_GROUP, EcsUnityConsts.ENTITY_BUILDING_GROUP)] [MetaGroup(EcsUnityConsts.PACK_GROUP, EcsUnityConsts.ENTITY_BUILDING_GROUP)]
[MetaDescription(EcsConsts.AUTHOR, nameof(MonoBehaviour) + " implementation of an entity template. Templates are a set of components that are applied to entities.")] [MetaDescription(EcsConsts.AUTHOR, nameof(MonoBehaviour) + " implementation of an entity template. Templates are a set of components that are applied to entities.")]
[MetaID("DragonECS_C734BA8092014833C14F21E05D7B1551")] [MetaID("DragonECS_C734BA8092014833C14F21E05D7B1551")]
public class MonoEntityTemplate : MonoEntityTemplateBase, IEntityTemplateInternal public class MonoEntityTemplate : MonoEntityTemplateBase, ITemplateNode
{ {
[SerializeReference] [SerializeReference]
[ReferenceButton(true, typeof(IComponentTemplate))] [ReferenceButton(true, typeof(IComponentTemplate))]
[FormerlySerializedAs("_components")] [FormerlySerializedAs("_components")]
private IComponentTemplate[] _componentTemplates; private IComponentTemplate[] _componentTemplates;
#region Properties
string IEntityTemplateInternal.ComponentsPropertyName
{
get { return nameof(_componentTemplates); }
}
#endregion
#region Methods #region Methods
public ReadOnlySpan<IComponentTemplate> GetComponentTemplates() public ReadOnlySpan<IComponentTemplate> GetComponentTemplates()
{ {

View File

@ -11,7 +11,7 @@ using UnityEngine.Serialization;
namespace DCFApixels.DragonECS namespace DCFApixels.DragonECS
{ {
public abstract class ScriptableEntityTemplateBase : ScriptableObject, IEntityTemplate public abstract class ScriptableEntityTemplateBase : ScriptableObject, ITemplateNode
{ {
public abstract void Apply(short worldID, int entityID); public abstract void Apply(short worldID, int entityID);
} }
@ -21,7 +21,7 @@ namespace DCFApixels.DragonECS
[MetaDescription(EcsConsts.AUTHOR, nameof(ScriptableObject) + " implementation of an entity template. Templates are a set of components that are applied to entities.")] [MetaDescription(EcsConsts.AUTHOR, nameof(ScriptableObject) + " implementation of an entity template. Templates are a set of components that are applied to entities.")]
[CreateAssetMenu(fileName = nameof(ScriptableEntityTemplate), menuName = EcsConsts.FRAMEWORK_NAME + "/" + nameof(ScriptableEntityTemplate), order = 1)] [CreateAssetMenu(fileName = nameof(ScriptableEntityTemplate), menuName = EcsConsts.FRAMEWORK_NAME + "/" + nameof(ScriptableEntityTemplate), order = 1)]
[MetaID("DragonECS_7C4DBA809201D959401A5BDFB6363EC0")] [MetaID("DragonECS_7C4DBA809201D959401A5BDFB6363EC0")]
public class ScriptableEntityTemplate : ScriptableEntityTemplateBase, IEntityTemplateInternal public class ScriptableEntityTemplate : ScriptableEntityTemplateBase, ITemplateNode
{ {
[SerializeField] [SerializeField]
private ScriptableEntityTemplateBase[] _templates; private ScriptableEntityTemplateBase[] _templates;
@ -30,13 +30,6 @@ namespace DCFApixels.DragonECS
[FormerlySerializedAs("_components")] [FormerlySerializedAs("_components")]
private IComponentTemplate[] _componentTemplates; private IComponentTemplate[] _componentTemplates;
#region Properties
string IEntityTemplateInternal.ComponentsPropertyName
{
get { return nameof(_componentTemplates); }
}
#endregion
#region Methods #region Methods
public ReadOnlySpan<ScriptableEntityTemplateBase> GetTemplates() public ReadOnlySpan<ScriptableEntityTemplateBase> GetTemplates()
{ {