Entity
Описание
Модуль Entity является вторым ключевым элементом ECS архитектуры EmpressCore. Он предоставляет механизм для создания и управления игровыми объектами (сущностями), которые могут содержать различные компоненты.
Назначение
Решаемые проблемы
- Организация игровых объектов без жесткой иерархии наследования
- Гибкое управление составом и поведением объектов
- Эффективный поиск и фильтрация объектов по их свойствам
Ключевые кейсы
- Создание игровых объектов с уникальными наборами компонентов
- Управление состоянием объектов через включение/отключение компонентов
- Фильтрация объектов для системной обработки
Преимущества
- Гибкая композиция объектов через компоненты
- Строгая типизация и контроль компонентов
- Эффективное управление состоянием через включение/отключение
- Уникальная идентификация каждого объекта
Ограничения
- Один тип компонента может быть добавлен к сущности только один раз
- Сущности не могут содержать логику, только компоненты
Архитектура
Основные классы и типы
Entity
- основной класс для создания игровых объектовIEntity
- интерфейс, определяющий поведение сущностейComponentCollection
- класс для управления набором компонентовComponentFilter
- интерфейс для фильтрации сущностей
Взаимодействие с другими модулями
Component
- используется для хранения данных сущностейSystem
- обрабатывает сущности на основе их компонентовEntityStorage
- хранит и управляет всеми сущностями в игре
Использование
Начало работы
Предварительные требования
- TypeScript версии 4.0 или выше
- Понимание принципов ECS
- Модуль Component для создания компонентов
Основные сценарии
Создание и настройка сущности
- Создание новой сущности с уникальным ID
- Добавление необходимых компонентов
- Настройка состояния компонентов
Управление компонентами
- Получение компонентов по типу
- Включение/отключение компонентов
- Удаление компонентов
Примеры
Простые примеры
Создание игрового персонажа
// Создаем сущность игрока
const player = new Entity('player-1');
// Добавляем необходимые компоненты
player.addComponent(new Position(0, 0));
player.addComponent(new Health(100));
player.addComponent(new Inventory());
// Получаем и используем компоненты
const position = player.getComponent(Position);
position.x += 10;
const health = player.getComponent(Health);
health.current -= 20;
Управление состоянием
// Создаем сущность с несколькими компонентами
const entity = new Entity('enemy-1');
entity.addComponent(new Position());
entity.addComponent(new Movement());
entity.addComponent(new AI());
// Отключаем AI на время паузы
entity.disableComponent(AI);
// Проверяем наличие компонентов
if (entity.hasComponents([Position, Movement])) {
// Обрабатываем движение
}
// Включаем AI после паузы
entity.enableComponent(AI);
Продвинутые примеры
Фильтрация сущностей
// Создаем фильтр для поиска движущихся объектов
const filter: ComponentFilter = {
includes: [Position, Movement], // Должны быть оба компонента
excludes: [Frozen] // Не должно быть компонента заморозки
};
// Проверяем соответствие сущности фильтру
if (entity.isSatisfiedFilter(filter)) {
// Обрабатываем движущийся объект
}
Массовое управление компонентами
// Создаем сущность с множеством компонентов
const boss = new Entity('boss-1');
boss.addComponent(new Position());
boss.addComponent(new Health());
boss.addComponent(new Attack());
boss.addComponent(new Defense());
boss.addComponent(new SpecialAbility());
// Отключаем все компоненты во время катсцены
boss.disableAllComponents();
// Включаем все компоненты после катсцены
boss.enableAllComponents();
API
Классы и типы
Entity
class Entity implements IEntity {
readonly uuid: string; // Уникальный идентификатор
name: string; // Имя сущности
active: boolean; // Статус активности
readonly components: Component[]; // Список активных компонентов
readonly disabledComponents: Component[]; // Список отключенных компонентов
constructor(uuid: string, name?: string);
addComponent(component: Component, enabled?: boolean): void;
getComponent<T extends Component>(ctor: ComponentType<T>): T;
hasComponents(types: ComponentType<any>[]): boolean;
removeComponent(ctor: ComponentType<any>): Component;
enableComponent<T extends Component>(ctor: ComponentType<T>): void;
disableComponent<T extends Component>(ctor: ComponentType<T>): void;
disableAllComponents(): void;
enableAllComponents(): void;
isSatisfiedFilter(filter: ComponentFilter): boolean;
}
ComponentFilter
interface ComponentFilter {
includes: ComponentType<any>[]; // Компоненты, которые должны присутствовать
excludes?: ComponentType<any>[]; // Компоненты, которых не должно быть
}
ComponentCollection
class ComponentCollection {
readonly items: Component[]; // Список компонентов
set(component: Component): void;
get<T extends Component>(type: ComponentType<T>): T | undefined;
has<T extends Component>(type: ComponentType<T>): boolean;
delete(type: ComponentType<any>): boolean;
clear(): void;
}
Частые вопросы
Как правильно организовать сущности?
Создавайте сущности с минимально необходимым набором компонентов. Добавляйте компоненты по мере необходимости, а не все сразу.
Когда использовать включение/отключение компонентов?
Используйте эту функциональность для временного изменения поведения объекта без удаления компонентов.
Что лучше: удалить компонент или отключить его?
Удаляйте компонент, если он больше не понадобится. Отключайте, если планируете включить его позже.