Static ECS

Version Документация Benchmarks Unity модуль Showcase

Гайд миграции

Что нового в 2.0.0

Static ECS - C# Hierarchical Inverted Bitmap ECS framework

  • Производительность
  • Легковесность
  • Отсутствие аллокаций
  • Низкое потребление памяти
  • Без Unsafe в ядре
  • Основан на статике и структурах
  • Типобезопасность
  • Бесплатные абстракции
  • Мощный механизм запросов с поддержкой параллелизма
  • Batch-операции над сущностями
  • Отслеживание изменений компонентов и тегов
  • Группировка сущностей по типам и кластерам
  • Система отношений между сущностями
  • Сериализация снимков мира
  • Система событий
  • Минимум болерплейта
  • Совместимость с Unity с поддержкой Il2Cpp и Burst
  • Совместимость с другими C# движками
  • Совместимость с Native AOT

Оглавление

Контакты

Поддержать проект

Если вам нравится Static ECS и он помогает вашему проекту, вы можете поддержать разработку:

Buy Me A Coffee

Установка

Библиотека имеет зависимость на StaticPack версии 1.1.0 для бинарной сериализации, StaticPack должен быть так же установлен

  • В виде исходников

    Со страницы релизов или как архив из нужной ветки. В ветке master стабильная проверенная версия

  • Установка для Unity

    Через git модуль в Unity PackageManager:

    https://github.com/Felid-Force-Studios/StaticEcs.git
    https://github.com/Felid-Force-Studios/StaticPack.git
    

    Или добавление в манифест Packages/manifest.json:

    "com.felid-force-studios.static-ecs": "https://github.com/Felid-Force-Studios/StaticEcs.git"
    "com.felid-force-studios.static-pack": "https://github.com/Felid-Force-Studios/StaticPack.git"
    
  • NuGet

    dotnet add package FFS.StaticEcs
    

    Для debug-сборки с проверками:

    dotnet add package FFS.StaticEcs.Debug
    

    Пакеты: FFS.StaticEcs · FFS.StaticEcs.Debug

AI Agent Integration

Если вы используете AI-ассистенты (Claude Code, Cursor, Copilot и др.) со StaticEcs:

Концепция

StaticEcs — новая архитектура ECS, основанная на инвертированной иерархической bitmap модели. В отличие от традиционных фреймворков ECS, которые полагаются на архетипы или разреженные наборы, в этой конструкции используется инвертированная индексная структура, в которой каждый тип компонента владеет масками активных сущностей, а не сущности хранят маски компонентов. Иерархическая агрегация этих масок обеспечивает логарифмическое индексирование блоков сущностей, что позволяет осуществлять фильтрацию блоков O(1) и эффективную параллельную итерацию с помощью битовых операций. Этот подход полностью устраняет миграцию архетипов и sparse set индирекцию, предлагая прямой доступ к памяти в стиле SoA с минимальным количеством промахов кэша. Модель обеспечивает до 64 раз меньшее количество запросов к памяти на блок и линейно масштабируется с количеством активных наборов компонентов, что делает ее идеальной для крупномасштабных симуляций, открытых миров со стримингом, сетевых игр с синхронизацией состояния, реактивного ИИ с тысячами агентов и проектов с частой сменой состава компонентов (баффы, эффекты, статусы).

В архетипных ECS (Unity DOTS, Flecs, Bevy, Arch) каждое добавление или удаление компонента вызывает миграцию сущности — копирование всех данных в новый архетип, а количество комбинаций компонентов ведёт к взрывному росту архетипов. В sparse-set ECS (EnTT, DefaultEcs) доступ к компонентам требует косвенной адресации через разреженные таблицы с минимум двумя промахами кэша на каждый lookup. StaticEcs устраняет обе проблемы: сущность занимает фиксированный слот в сегментированных массивах и никогда не перемещается в памяти — Add/Remove это O(1) переключение бита в маске присутствия без копирования данных. Стабильность адреса сущности обеспечивает дешёвые отношения между сущностями через версионированные идентификаторы (EntityGID), включая связи со стримингом, когда часть связанных сущностей находится в выгруженных зонах — идеально для сложных симуляций в открытых мирах. Количество типов компонентов не влияет на структуру хранения, поскольку каждый тип владеет собственной маской независимо от остальных. Двумерное партиционирование EntityType × Cluster дополнительно обеспечивает кэш-локальность: сущности одного типа внутри кластера располагаются в соседних сегментах памяти, а кластеры позволяют загружать и выгружать целые пространственные зоны без затрагивания остальных данных.

Память организована иерархически: чанки (4096 сущностей) → сегменты (256) → блоки (64). Запрос к миру начинается с AND эвристических масок на уровне чанка — одна битовая операция покрывает до 4096 сущностей, отсекая пустые блоки целиком, — а затем уточняется на уровне 64-сущностного блока. Пакетные операции (BatchAdd, BatchRemove, BatchSet) обрабатывают до 64 сущностей одной битовой операцией.

  • Основная идея данной реализации в статике, все данные о мире и компонентах находятся в статических generic-классах (World<TWorld>), что дает возможность избегать дорогостоящих виртуальных вызовов и аллокаций, иметь удобный API со множеством сахара. JIT-компилятор устраняет мёртвый код для неиспользуемых хуков компонентов
  • Данный фреймворк нацелен на максимальную простоту использования, скорость и комфорт написания кода без жертв в производительности
  • Доступно создание мульти-миров, строгая типизация, обширные бесплатные абстракции
  • Система бинарной сериализации со снапшотами мира, кластеров и отдельных сущностей, с версионированием схемы и поддержкой сжатия
  • Система отношений сущностей с автоматическими двусторонними хуками для иерархий, групп и связей
  • Реактивное отслеживание изменений для сетевой синхронизации, UI и триггеров
  • Мульти-компоненты — переменное количество данных на сущность (инвентарь, баффы) без heap-аллокаций
  • Многопоточная обработка с параллельными запросами и гарантиями безопасности на уровне блоков
  • Низкое потребление памяти, SoA-layout (Structure of Arrays) — компоненты одного типа в непрерывных массивах
  • Основан на Bitmap архитектуре, нет архетипов, нет sparse-set
  • Фреймворк создан для нужд частного проекта и выложен в open-source.

Быстрый старт

using FFS.Libraries.StaticEcs;

// Определяем тип мира
public struct WT : IWorldType { }

// Определяем тип-алиас для удобного доступа
public abstract class W : World<WT> { }

// Определяем тип систем
public struct GameSystems : ISystemsType { }

// Определяем тип-алиас для систем
public abstract class GameSys : W.Systems<GameSystems> { }

// Определяем компоненты
public struct Position : IComponent { public Vector3 Value; }
public struct Direction : IComponent { public Vector3 Value; }
public struct Velocity : IComponent { public float Value; }

// Определяем систему
public struct VelocitySystem : ISystem {
    public void Update() {
        // Итерация через foreach
        foreach (var entity in W.Query<All<Position, Velocity, Direction>>().Entities()) {
            ref var pos = ref entity.Ref<Position>();
            ref readonly var dir = ref entity.Read<Direction>();
            ref readonly var vel = ref entity.Read<Velocity>();
            pos.Value += dir.Value * vel.Value;
        }

        // Или через делегат (быстрее, без аллокаций)
        W.Query().For(
            static (ref Position pos, in Velocity vel, in Direction dir) => {
                pos.Value += dir.Value * vel.Value;
            }
        );
    }
}

public class Program {
    public static void Main() {
        // Создаем мир
        W.Create();

        // Авторегистрация всех компонентов, тегов, событий и т.д. из текущей сборки
        W.Types().RegisterAll();

        // Инициализируем мир
        W.Initialize();

        // Создаем и настраиваем системы
        GameSys.Create();
        GameSys.Add(new VelocitySystem(), order: 0);
        GameSys.Initialize();

        // Создание сущности с компонентами
        var entity = W.NewEntity<Default>().Set(
            new Position { Value = Vector3.Zero },
            new Direction { Value = Vector3.UnitX },
            new Velocity { Value = 1f }
        );

        // Обновление всех систем — вызывается в каждом кадре
        GameSys.Update();
        // Продвижение трекинга изменений (изменения видны в следующем кадре)
        W.Tick();

        // Уничтожение систем
        GameSys.Destroy();
        // Уничтожение мира и очистка всех данных
        W.Destroy();
    }
}

Лицензия

MIT license


This site uses Just the Docs, a documentation theme for Jekyll.