Skip to content
/ Hexecs Public

Это маленький движок логики, имплементирующий ECS (Entity-Component-System), который весьма удобен для создания игровой логики на C#

License

Notifications You must be signed in to change notification settings

teoadal/Hexecs

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

15 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

.NET Core NuGet NuGet codecov CodeFactor

HexECS

Привет! Это движок, имплементирующий ECS. Тут ты можешь делать многое, к чему и так привык. При этом, ты можешь чуть больше, чем обычные ECS, так как помимо ECS в библиотеке есть встроенный CQRS и DI. CQRS в связке с ECS представляется мне максимально подходящей штукой для управления состоянием сущностей через системы. DI необходим для того, чтобы всё это работало удобно.

Базовые возможности

Так как всё в этом мире завязано на "мир" (World), то мы начнём с его создания.

var world = new WorldBuilder()
    .DebugWorld() // чтобы получать больше информации в Debug-режиме 
    .CreateLogger(logger => logger.UseConsoleSink()); // чтобы логи выводились в консоль
    .Build();    

Этот код создаёт сконфигурированный объект World. Так как библиотека AOT-friendly, тут нет сканеров assembly для регистрации отдельных компонентов. Всё нужно регистрировать вручную.

Создание компонента

Компонент - это один из базовых кирпичиков ECS. Для того, чтобы системы могли наполнить сущность жизнью, сущность должна обладать признаками - компоненты и есть эти признаки.

public struct FlyComponent(int current, int max) : IActorComponent { // необходимо указать маркерный интерфейс
    public int CurrentSpeed;
    public int MaxSpeed;
}

Компоненты должны быть struct, чтобы всё в вашем мире могло работать эффективно и быстро. Например, обрабатывать в системах или обработчиках шины сообщений.

Создание сущности

В этом мире есть две сущности - Actor'ы и Asset'ы.

  • Актёры - это динамичные изменяемые сущности, которые могут создаваться и удаляться, и которым можно добавлять и удалять компоненты.
  • Ассеты - это сущности, которые нельзя изменить, представляющие из себя настройки мира, но которые также имеют компоненты. Единственное отличие - их нельзя создавать в процессе работы, так как они, по задумке, представляют из себя ресурсы, на основе которых будут создаваться актёры.

Создать актёра можно следующим образом:

Actor actor = world.Actors.CreateActor();
actor.Add(new FlyComponent(10, 20));
Actor<FlyComponent> typedActor = actor.As<FlyComponent>();

В последней строчку был создан т.н. "типизированный актёр" или актёр, в котором точно есть летающий компонент. Эта щепотка типизации очень важна в случаях, когда вам хотелось бы знать заранее - действительно ли актёр содержит нужный компонент. Например, в шине сообщений.

Структура Actor и Actor<T> предоставляют удобную обёртку для взаимодействия с актёром. Актёр всегда знает к какому ActorContext он принадлежит и просто проксирует методы к контексту, чтобы программисту было проще.

Более привычным типом entity для классических ECS будет являться содержимое поля Actor.Id, которое содержит в себе идентификатор актёра. Если вам удобнее работать с ними, можно сделать так:

ActorId actorId = typedActor;

Добавление компонентов

Созданный актёр может быть создан с использованием метода world.Actors.Create(). В этом случае он не будет обладать компонентом FlyComponent. Если мы не добавили его ранее, то добавим его:

actor.Add(new FlyComponent { CurrentSpeed = 5, MaxSpeed = 7 });

То, что компонент теперь принадлежит актёру можно легко проверить с помощью метода:

actor.Has<FlyComponent>(); // it's true!

Поздравляю, мы создали компонент и теперь можем его редактировать. Для этого достаточно получить ссылку на компонент и отредактировать его.

ref var component = ref actor.Get<FlyComponent>();
component.Current = 15;

Однако, в данном случае никто никогда не узнает о том, что было что-то изменено. Иногда это важно, например, чтобы держать кэш позиций некоторых актёров.

Чтобы все точно узнали, что были изменения, нужно использовать специальный метод:

var component = actor.Get<FlyComponent>(); // копируем компонент
component.Current = 15; // изменяем значение копии
actor.Update(component); // в этот момент все узнают, что что-то изменилось

Удаление компонентов

Тут всё тоже предельно просто. Представим, что у нас есть компонент Pilot (на самом деле структура PilotComponent : IActorComponent), который мы уже добавили к актёру-самолёту. После того как пилот захотел спать, он покинет самолёт следующим способом:

actor.Remove<PilotComponent>();

Если пилота там не было, метод вернёт false и мы сможем обработать это вопиющее нарушение правил полётов. Если компонент был, то метод вернёт true, а значит мы можем быть уверенными, что пассажиры в безопасности.

Дочерние элементы

Пассажиры! Их тоже можно создать примерно так:

Asset passengerAsset = world.GetAsset<PassengerAsset>(); 
Actor passenger = world.Actor.BuildActor(passengerAsset, Arg.Rent("plane", planeActor))

Мы находим ассет пассажира (вспомним, что у него есть компоненты). Допустим, что он всего один. На второй строке, с помощью зарегистрированных IActorBuilder (имплементации "строителей" актёров по компонентам ассета), буквательно создаём актёра. Кстати, если нам будет нужно, мы сможем проверить, по какому ассету был построен актёр, запросив actor.TryGetAsset.

После создания пассажиров, мы можем в прямом смысле посадить их на самолёт. Для этого нужно воспользоваться следующим методом:

Actor<FlyComponent> plane = ...; 
Actor passenger = ...;
plane.AddChild(passenger);

Если повторить это много раз, то в самолёте будут сидеть 2-400 пассажиров (в зависимости от типа самолёта, конечно).

About

Это маленький движок логики, имплементирующий ECS (Entity-Component-System), который весьма удобен для создания игровой логики на C#

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages