惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

H
Heimdal Security Blog
A
Arctic Wolf
K
Kaspersky official blog
V
Vulnerabilities – Threatpost
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
Simon Willison's Weblog
Simon Willison's Weblog
L
LINUX DO - 热门话题
MongoDB | Blog
MongoDB | Blog
T
Threat Research - Cisco Blogs
D
Docker
爱范儿
爱范儿
T
Tenable Blog
C
Check Point Blog
B
Blog
C
Cisco Blogs
Vercel News
Vercel News
The Cloudflare Blog
T
Threatpost
NISL@THU
NISL@THU
T
Tor Project blog
V2EX - 技术
V2EX - 技术
P
Palo Alto Networks Blog
Application and Cybersecurity Blog
Application and Cybersecurity Blog
T
Tailwind CSS Blog
G
GRAHAM CLULEY
P
Privacy & Cybersecurity Law Blog
SecWiki News
SecWiki News
博客园 - 司徒正美
S
Security @ Cisco Blogs
GbyAI
GbyAI
S
Secure Thoughts
Microsoft Security Blog
Microsoft Security Blog
The Register - Security
The Register - Security
Recorded Future
Recorded Future
Cloudbric
Cloudbric
Webroot Blog
Webroot Blog
N
News and Events Feed by Topic
Y
Y Combinator Blog
博客园_首页
T
Troy Hunt's Blog
The Hacker News
The Hacker News
雷峰网
雷峰网
Google DeepMind News
Google DeepMind News
U
Unit 42
AWS News Blog
AWS News Blog
PCI Perspectives
PCI Perspectives
V
Visual Studio Blog
博客园 - 聂微东
有赞技术团队
有赞技术团队
酷 壳 – CoolShell
酷 壳 – CoolShell

Все публикации подряд на Хабре

Ловим музу за клавиатуру: как айтишнику стать автором Что умеет Midjourney в 2026? Мой немного грустный разбор этого шикарного инструмента Никто не любит писать тесты, но ИИ может исправить это IPv8 выглядит как мечта. Поэтому почти наверняка не взлетит Производители вернули в продажу материнки с DDR3. Что происходит? Управление агентом с телефона через Telegram теперь в KodaCode От координации к лидерству: как меняется роль руководителя разработки Я сделала родителям бизнес вместо пенсии: зарабатываем 70 тысяч, мама не даёт продать В три раза быстрее приемка товара и оптимизация трудозатрат на 73%: как «РСТ-Инвент» помог Gulliver Group ИИ-шечный мир победил? О влиянии искусственного интеллекта на игропром Кремль снижает давление на Телеграмм пока Европа строит интернет по паспорту Как CEO, CTO и CIO за 8 часов собрали ИИ-директора, который умеет держать позицию под давлением Как (не) потерять домен за выходные Вместо 8 разных VPS: как я организовал практику студентам на одном сервере Почему твой Open Source проект не замечают? R&D: искусство управления неопределенностью в разработке AI-дефляция: вакансий для разработчиков больше, а рост зарплат — худший за 15 лет Мы отдали управление роботами OpenClaw. Что из этого вышло Галактический ID: система идентификации для всех форм разумной жизни Шесть основ бизнес-анализа: начинаем с вопроса «Кто в игре?» Код-ревью, в котором дело не в коде Данные переехали. Команда — нет Системной подход к сдаче OSWE в 2025 Почему комната управления реактором покрашена в цвет морской пены 4 YAML-файла вместо PySpark: как аналитикам строить пайплайны без разработчиков LLM-агент для поиска свободных доменов: автоматизируем подбор Когда, зачем и как правильно начинать новую сессию в Claude Code? Как я заставил нейросеть писать макросы для FreeCAD Анатомия ИИ‑агента для подбора персонала. От тысячи резюме к топ‑10 за минуты Опыт разработчика как экономика внимания Автономность как точка невозврата: кто будет субъектом в цифровом будущем Обучение ИИ в «диких» условиях: как рутинные действия превращаются в датасеты Как измерить LLM для задач кибербеза: обзор открытых бенчмарков Где хранить код? Сравнение GitHub, GitLab и Bitbucket Математика объясняет, почему нормальное распределение встречается повсюду Почему ваш FinOps не работает: 12 тезисов от практиков Как подписать проектную документацию УКЭП с использованием бесплатных лицензий Pilot Адаптивное администрирование Sigla Vision Я грузил уран в бочки, а потом 20 лет строил ИТ в атомной отрасли Чем позвонить с Эвереста? История и обзор спутниковой связи. Часть 2 Как языковая модель помогает контролировать качество инструктажей по охране труда в металлургии Как не передать на desktop свой IP в РКН Анатомия SAP Privileges: как устроено управление правами в macOS MoneyDev: Сказка про три главных слова Обновлённый токенизатор видео K-VAE 2.0 от Сбера Как сделать диспетчеризацию дома на 1284 квартиры почти бесплатно Как мы разогнали железную дорогу Мы дали агентам рутину. Теперь надо решить — что делать с освободившимся временем Токсичный контент, промпт-хакинг и защита ИИ — всё о Guardrails для LLM Умный город начинается с точного взгляда: как «Фалькон Тех» меняет пространство к лучшему Навайбкодил приложение для анализа графов Почему Дюну так интересно читать? Упрощаем работу с рутиной или как стать Гендальфом Белым Деконструкция Go: CPU, RAM и что там происходит. Go Assembler база. Часть 1.1 Какие профессии исчезнут из-за ИИ, а какие появятся? И что с этим делать Как мы построили IT-отдел, где хочется расти: архитектурные встречи, прозрачные метрики и книжные подарки Rufler: Делаем из Claude Code автономный рой через один YAML-конфиг Sing-box и белый список приложений Как построить надёжный обмен сообщениями в микросервисах: лучшие практики для enterprise OpenAI строит MLM-пирамиду, а McKinsey и Accenture помогают ей в этом Дом, который не построил Фишер (Часть 2) «Сверхзвуковой математик» против «Вдумчивого логиста»: битва алгоритмов 3D-упаковки Мультимодальные модели – грубый и дорогой инструмент Разговоры ничего не стоят. Код тоже Проверки физических лиц: с кого начнет ФНС Топ-10 бесплатных нейросетей для создания видео в 2026 году Первые слои кода: как наши решения сегодня определяют архитектуру ИИ на десятилетия Разработка нового статического анализатора: PVS-Studio JavaScript Поиск уязвимостей ПО: базовый минимум или роскошный максимум Почему оценка персонала не работает как инструмент управления Как мы разработали ИИ-ассистента и сократили рутину продуктовой команды на 50% Как я ушел из найма, нажарил косточек и продал на маркетплейсах на 168 млн в год Когда 1С:ERP уже внедрена, а нормального производственного плана всё ещё нет Как я сделал Claude мультимодальным, подключив к нему Qwen Omni Как приглашение на вакансию мечты превращается в атаку Infrastructure as Code: философия и лучшие практики IaC Тестируем Yandex Code Assistant на задаче, в которой нужно хранить секреты nxs-universal-chart v3.0: новое поколение универсального Helm-чарта Callback Injection: Техника, которая отправила Microsoft Defender в глухой нокаут «Все идеи на стол»: митап как способ вывести проект из тупика Сегодня я узнал нечто новое о GPU благодаря багу в своей игре Как заставить LLM ̶ ̶г̶а̶л̶л̶ю̶ ̶ эволюционировать Карта событий как фундамент аналитики: практический кейс для E-commerce Что выбрать для AI: x86, ARM или RISC-V? Дайджест железа за март Роль соматических мутаций в развитии аутоиммунных заболеваний: путь к избирательной терапии Mythos от Anthropic — тревожный сигнал для всех, а не только для банков Guardrails для LLM на Java: как приручить промпт‑инъекции и токсичные ответы Green-VLA: как мы собрали VLA-модель для реального антропоморфного робота и не потеряли обобщение Финансовая гонка вооружений: почему умные люди добровольно в ней участвуют Эра ИИ-агентов наступила: выбираем лучшего цифрового сотрудника # Практический опыт внедрения WinCC Redundancy на производственном предприятии Сделал MVP за 3 дня, а потом неделю прикручивал оплату. Оно того стоило? Физика против Маска: почему Starship V3 может оказаться ещё одной катастрофой Нефть Венесуэлы: крупнейшие запасы в мире, но не крупнейшая нефтяная держава JPA 4. Переосмысление Hibernate Почему зеркальная фотокамера Nikon D5 десятилетней давности идеально подошла для миссии «Артемида-2» Проект «Уровень-Спутник» или как мы сделали платформу для гидрологов «Замедлиться, чтобы ускориться»: почему ИИ повышает цену ошибок в требованиях и архитектуре Как с нуля поднять трафик IT-компании на 1657% при бюджете 55 тыс. и выжить Pixel-perfect Downsampling — идеальная отрисовка 50 миллионов точек без потерь
Статический архетип в ECS: локальность данных без неявных перемещений компонент
Tidehunter · 2026-05-04 · via Все публикации подряд на Хабре

Уровень сложностиСредний

Время на прочтение7 мин

Охват и читатели1.3K

Обзор

У архетипов в ECS есть неотъемлимое преимущество перед обычными sparse set'ами - локальность данных. Но есть и существенная проблема - хаотическое копирование данных при миграции из одного архетипа в другой. Что если попробовать избавиться от этого недостатка, оставив локальность данных? Предлагаю рассмотреть статический архетип, который требует явного вызова метода для миграции данных.

Проблема

Рассмотрим проблему на конкретном примере: на игровой механике стрельбы, которую реализуем с помощью ECS.

Есть снаряд, выпущенный из оружия. Он летит в определенном направлении и попадает в цель. В момент попадания снаряд взрывается. Сущность снаряда перейдет в сущность взрыва. Ненужные компоненты будут удалены, новые добавлены.

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

Итак, для полета снаряда нам понадобятся компоненты:

  • Position - текущая позиция,

  • Velocity - скорость и направление полета,

  • Damage - урон.

А попадание будет представлено:

  • Position - местом попадания,

  • ExplosionRadius - радиусом поражения,

  • Damage - уроном.

Сами компоненты будут иметь вот такой вид:

public struct Position        : IComponent { public Vector3 Vector; }

public struct Velocity        : IComponent { public Vector3 Vector; }

public struct ExplosionRadius : IComponent { public float Value; }

public struct Damage          : IComponent { public int Value; }

При переходе сущности из снаряда во взрыв, т.е. при переходе из одного архетипа в другой система вынуждена:

  • скопировать все компоненты сущности из старого архетипа,

  • вставить в подходящие массивы нового архетипа,

  • удалить запись в старом архетипе.

Чем больше компонентов, тем дороже каждая миграция. В сценариях с частыми переходами между состояниями (снаряды, бафы, фазы атак) это становится заметными накладными расходами.

Вопрос напрашивается сам собой: можно ли сохранить локальность данных и при этом взять контроль над моментом копирования в свои руки?

Как возможный вариант решения проблемы, предлагаю рассмотреть статический архетип.

Статический архетип

Идея проста: набор компонентов архетипа фиксируется на этапе инициализации. После этого сущность попадает в архетип и находится там до тех пор, пока мы сами не решим её переместить или удалить. Никакого неявного перемещения, только мы контролируем этот процесс.

Данные, как и в классическом архетипе, хранятся в плотных типизированных массивах. Но добавление и удаление отдельных компонент невозможно.

Рассмотрим механизм работы таких статических архетипов. В этом нам поможет библиотека PewPew.Ecs. Там как раз реализованы статические архетипы.

Нам понадобится nuget пакет PewPew.Ecs, который включает в себя всё, что нужно.

Объявление статических архетипов

Из примера выше объявим 2 архетипа: Projectile, будет отвечать за снаряд, и Explosion, будет отвечать за взрыв, с нужным набором компонент.

Нам понадобится атрибут [Archetype]. Он нужен для SourceGenerator, который добавит нужные методы и структуры.

// объявляем архетип снаряда
[Archetype]
public ref struct Projectile
{
    // добавляем нужные компоненты как ref поля
    public ref Position Position;
    public ref Velocity Velocity;
    public ref Damage Damage;

    // объявляем конструктор
    public Projectile(ref Position position, ref Velocity velocity, ref Damage damage)
    {
        Position = ref position;
        Velocity = ref velocity;
        Damage = ref damage;
    }
}

// по аналогии объявляем архетип взрыва с нужным набором полей
[Archetype]
public ref struct Explosion
{
    public ref Position Position;
    public ref ExplosionRadius ExplosionRadius;
    public ref Damage Damage;

    public Explosion(ref Position position, ref ExplosionRadius explosionRadius, ref Damage damage)
    {
        Position = ref position;
        ExplosionRadius = ref explosionRadius;
        Damage = ref damage;
    }
}

Инициализация

Для работы со статическими архетипами нам понадобится HybridWorld - мир, который умеет хранить сущности как в обычных sparse set'ах, так и в статических архетипах.

// создаем HybridWorld
HybridWorld world = WorldFactory.Shared.CreateHybridWorld();

// объявляем архетип летящего снаряда Projectile с компонентами Position, Velocity, Damage
world.InitProjectileArchetype();

// объявляем архетип взрывающегося снаряда Explosion с компонентами Position, ExplosionRadius, Damage
world.InitExplosionArchetype();

Явная инициализация имеет большое преимущество перед неявной. Процесс прогрева всех внутренних коллекций происходит в понятное для разработчика время.

Добавление сущности

После инициализации можно добавлять сущности в архетип:

// получаем сгенерированный архетип для Projectile
ProjectileArchetype projectileArchetype = world.GetProjectileArchetype();

// создаем идентификатор сущности
EntityId entityId = world.CreateEntityId();

// добавляем в архетип новую сущность
Projectile projectile = projectileArchetype.Add(entityId);

// назначаем компонентам нужные значения
projectile.Position.Vector = spawnPoint;
projectile.Velocity.Vector = direction;
projectile.Damage.Value    = 50;

Метод Add возвращает refструктуру Projectile с refссылками на компоненты прямо в массивах архетипа. Никаких промежуточных копий.

Явная миграция

Перейдём к ключевой части. Рассмотрим сценарий: в каждом тике мы проверяем летящие снаряды и переводим те, что во что-то попали, в архетип взрыва.

// получаем сгенерированный архетип для Projectile
ProjectileArchetype projectileArchetype = world.GetProjectileArchetype();

// получаем сгенерированный архетип для Explosion
ExplosionArchetype explosionArchetype   = world.GetExplosionArchetype();

// получаем все сущности хранящиеся в архетипе
Span<EntityId> entities = projectileArchetype.Entities;

// пример итерации с перемещением сущности
for (var i = 0; i < entities.Length; i++)
{
    EntityId entityId = entities[i];

    // проверка столкновений
    if (HitSomething(entityId))
        // копирование компонент из старого архетипа в новый
        projectileArchetype.MoveEntityTo(entityId, explosionArchetype);
}

Метод MoveEntityTo копирует общие компонентыPosition и Damage из старого архетипа в новый, а компонент Velocity просто отбрасывается, т.к. в новом архетипе взрыва его нет.

Также в новом архетипе есть компонент ExplosionRadius , который будет содержать значения по умолчанию. Этот компонент мы можем инициализировать строкой ниже чуть позже.

Именно метод MoveEntityTo выполняет копирование компонентов. Но копирование происходит только тогда, когда мы сами этого захотели. Никакой скрытой работы за кадром.

Локальность данных

Осталось продемонстрировать, что итерация по сущностям работает также, как и для классического архетипа. Заодно продемострируем использование SIMD инструкций.

Поскольку компоненты одного архетипа хранятся в плотных массивах, мы можем обрабатывать их пачками. Для этого воспользуемся HybridWorld , который поддерживает батч-запросы через IBatchQuery:

// создаем фильтр для сущностей
FilterDefinition flyingDefinition = new FilterDefinition()
    .With<Position>()
    .With<Velocity>();

// создаем запрос, который будет выполняться для сущностей, удовлетворяющих фильтру
FlightBatchQuery flightBatchQuery = new FlightBatchQuery();

// запускаем запрос
world.ExecuteBatchQuery<FlightBatchQuery, Position, Velocity>(flyingDefinition, flightBatchQuery);

// именно структуры позволяют инлайнить методы, что позволяет убрать накладные расходы на вызовы метода
public readonly struct FlightBatchQuery : IBatchQuery<Position, Velocity>
{
    // метод обработает сущности пачкой, когда сущности хранятся в статическом архетипе,
    // тут содержится SIMD специфичная логика сложения значений,
    // метод SparseUpdate ниже делает тоже самое, только поштучно для каждой сущности
    public void BatchUpdate(Span<Position> positions, Span<Velocity> velocities)
    {
        int vectorCount = Vector<float>.Count;
        int length = positions.Length - positions.Length % vectorCount;
        var posVectors = MemoryMarshal.Cast<Position,  Vector<float>>(positions[..length]);
        var velVectors = MemoryMarshal.Cast<Velocity, Vector<float>>(velocities[..length]);
        for (int i = 0; i < posVectors.Length; i++)
            posVectors[i] += velVectors[i];

        for (int i = length; i < positions.Length; i++)
            positions[i].Vector += velocities[i].Vector;
    }

    // поштучно обрабатывает сущности, когда сущности хранятся в sparse set коллекциях
    public void SparseUpdate(ref Position position, ref Velocity velocity)
        => position.Vector += velocity.Vector;
}

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

Ограничения

Статический архетип, в свою очередь, тоже имеет свои минусы:

  • Сущность не может иметь компоненты, кроме тех, что объявлены в статическом архетипе. Но сущность может существовать отдельно от архетипа и хранить компоненты в sparse set'ах.

  • Сущность может находиться только в одном архетипе одновременно.

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

Когда использовать?

Статические архетипы хорошо работают в следующих сценариях:

  • количество сущностей достаточно большое (более 10000 сущностей в одном мире), чтобы реализовать преимущество локальности данных, как и в случае классического архетипа,

  • структура сущностей известна заранее и меняется дискретно: снаряд летит / взрывается, враг жив / мёртв, юнит стоит / бежит,

  • важна предсказуемость момента копирования данных,

  • нужна SIMD-обработка или локальность данных.

Если же структура данных непредсказуема, то, вероятно, стоит рассмотреть обычные sparse set хранилища.

Заключение

Статический архетип это компромисс между классическим архетипом и sparse set хранилищем. Он позволяет сохранить локальность данных и получить SIMD-дружественный доступ к компонентам, но взамен требует явно вызывать копирование данных с помощью метода MoveEntityTo. Копированием управляет разработчик, а не ECS.

P.S. Буду рад обсудить статические и классические архетипы в комментариях!