Привет, читатель! В этой статье не будет практики, а только спецификация формата и примеры для его объяснения. Код парсера будет в следующей части статьи!
Что такое ECS
ECS - Entity Component System (Сущности Компоненты Системы), это распространённый паттер программирования игр, используемый для упрощения разработки. В этом паттерне есть 3 определения:
Entity (Сущность) - контейнеры, хранящие в себе Компоненты (Данные). Сами не хранят свойства.
Component (Компонент) - классы/структуры, хранящие в себе всевозможные данные, нужные для игровой логики (Систем).
System (Системы) - Логические ядра игры. Они манипулируют сущностями и данными внутри них. Чаще всего реализуются классами
Если же вы хотите разобраться в теме подробнее, предлагаю вам вот эту статью.
Хочу отметить, что в Формате будет возможно описывать только ПЕРВЫЕ два определения, так как для описания игровой логики нужен язык программирования, а это задача сложная.
Зачем?
"Зачем?" - задаст вопрос читатель. И я отвечу - упрощение. Вот пример объявления компонента во ECS-фреймворке BrokenBricksECS:
namespace ECSExample {
[Serializable]
public struct FloatComponent : IComponent {
public float value;
public FloatComponent(float value) {
this.value = value;
}
}
public class FloatDataComponent : ComponentDataWrapper<FloatComponent> { }
}
Сначала мы здесь должны создать структуру, внутри которой будет значения и конструктор. Потом мы создаем FloatDataComponent. Это обёрнутый в ComponentDataWrapper FloatComponent. Разве это нужно?
Решение
Для этого я предлагаю создать формат описания компонентов и прототипов сущностей (префабов). И все что мне пришло на ум, так только это.
@component Component1 {
value: int,
}
@component Comp2(Component1) {
value2: str = "default_value" # Будет иметь value: int и value2: str
} # Родитель - Component1
Используя ключевое слово @component
, мы создаем новый компонент. Внутри фигурных скобок типы объявляются так: <name>: <type>
, также можно указать дефолтное значение <name>: <type> = <value>
Комментарии начинаются с символа решётки # Комментарий
Наследование компонентов?
Компоненты могут наследоваться друг от друга. Это делается так:
@component <name>(<parent>) {
data_from_parent,
...,
new_and_overriden_data
}
# Пример
@component Component1 {
value: int,
}
@component Comp2(Component1) {
value2: str = "default_value" # Будет иметь value: int и value2: str
} # Родитель - Component1
Множественного наследования формат не поддерживает, так как есть проблема ромба.
Хочу заметить, ради оптимизации компоненты при наследовании просто сливаются.
Прототипы сущностей (Префабы)
А тут, во время написания, мне приходит мысль? А почему бы и не добавить в формат описание сущностей! И я добавил их:
@entity Name {
Component1(value=10)
}
@entity Name2(Name) {
Comp2(10, 'default_value')
}
@entity Name3 { # Равен Name
Component1(10),
Comp2(value=10)
}
Используя ключевое слово @entity <name>
, мы создаем новый прототип сущности. Внутри фигурных скобок компоненты добавляются так: <name>(<value>, <field_name>=<value>)
Наследование же работает также, как у компонентов.
Итог
Итого, у нас есть вот такой example-файл:
@component Component1 {
value: int,
}
@component Comp2(Component1) {
value2: str = "default_value" # Будет иметь value: int и value2: str
} # Родитель - Component1
@entity Name {
Component1(value=10)
}
@entity Name2(Name) {
Comp2(10, 'default_value')
}
@entity Name3 { # Равен Name
Component1(10),
Comp2(value=10)
}
В следующей части я напишу парсинговый механизм для этого формата. Удачи!
Комментарии (11)
Nognomar
07.11.2023 10:00Складывается впечатление что автор перепутал ecs и компонентную архитектуру (на подобие той что используется в юнити). В ecs не используется никакого наследования и уж тем более компоненты не должны содержать внутри другие компоненты. Вы взяли ecs, но притащили за собой ровно те же проблемы из ооп от которых этот самый ecs должен был вас избавить.
gagarinten Автор
07.11.2023 10:00-1Ну как вам сказать, каждый компонент НЕ содержит родителей, их содержимое складывается.
Nognomar
07.11.2023 10:00В таком случае это практически тоже самое наследование, разве нет? В любом случае, мой вам совет - уберите эти фичи. Наследование в компонентах в любом его проявлении - путь в ад. Вы упростите себе как написание парсера, так и жизнь тем, кто решит пользоваться вашим фреймворком. Вся прелесть ecs в том что у вас плоская архииектура без лишних иерархий и абстракций. Соответственно её сравнительно легко расширять и рефакторить. Зачем сознательно тащить проблемы ооп подхода - непонятно.
gagarinten Автор
07.11.2023 10:00На самом деле, формат вам не запрещает использовать наследования. Ваше дело не использовать эту фичу, но это не отменять, что она должна быть.
DizzyJump
07.11.2023 10:00+1Плоховато описана проблематика, которую пытается решить этот подход. Бойлерплейтный однотипный код при обьявлении компонентов? Ну допустим, а чем предложенный подход лучше тех же сниппетов? Пока выглядит так что вы пытаетесь заменить один бойлерплейт другим велосипедным.
kipar
Сущности есть смысл грузить (и сохранять) в рантайме, этакая сериализации в текстовый формат (для конфигов, дерева исследований и подобных вещей которые хочется редактировать в виде текста).
А компоненты - наоборот, если уж грузить то во время компиляции/кодогенерации, потому что если грузить их в рантайме, то половина преимуществ ецс (скорость обработки за счёт фиксированной раскладки в памяти) уйдёт, да и работать с ними из компилируемого языка не выйдет.
Так что возникает вопрос - а есть ли смысл объединять одно с другим в один формат?
Lainhard
Зависит от библиотеки. Есть flecs, в котором можно в рантайие определять компоненты.
А как насчёт кодогенерации? ????
Но в целом согласен. Сущностям в рантайме быть. Компонентам - нихть!
gagarinten Автор
Компоненты должны быть как структуры. Это желательно.
gagarinten Автор
Почему бы и нет? Можно сделать разные реализации, например - метапрограммирование компонентов.