Hexecs 0.4.0

dotnet add package Hexecs --version 0.4.0
                    
NuGet\Install-Package Hexecs -Version 0.4.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Hexecs" Version="0.4.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Hexecs" Version="0.4.0" />
                    
Directory.Packages.props
<PackageReference Include="Hexecs" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Hexecs --version 0.4.0
                    
#r "nuget: Hexecs, 0.4.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Hexecs@0.4.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Hexecs&version=0.4.0
                    
Install as a Cake Addin
#tool nuget:?package=Hexecs&version=0.4.0
                    
Install as a Cake Tool

.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 пассажиров (в зависимости от типа самолёта, конечно).

Product Compatible and additional computed target framework versions.
.NET net9.0 is compatible.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • net9.0

    • No dependencies.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.4.0 142 6/5/2025