Десятилетиями нам рассказывают, что есть только два пути: громоздкие иерархии ООП или стерильная бюрократия ECS. Нас заставили поверить в то, что создание игр — это выбор между анархией и диктатурой.

Это ложь. Оба этих пути — ненужные усложнения. Есть прямой и эффективный способ, который мы променяли на модные, но непрактичные примочки. Эта статья — о том, как вернуться к здравому смыслу.

Как нам продали «серебряную пулю» ООП

Целые поколения разработчиков учили моделировать мир как иерархию классов. В институтских курсах и учебниках их гоняли по UML и «правильным» диаграммам: наследование, полиморфизм, интерфейсы. Легендарный пример — «объектная модель микроволновки»: класс Microwave, подклассы состояний, входов, кнопок, таймеров. Идея проста — всё в мире «есть объект», значит, мир можно объяснить деревом наследования.

Дальше случилось ожидаемое. «ООП головного мозга» переехало в игровую разработку. «Минотавр — это Humanoid, Humanoid это Creature, а Creature — это GameObject, у каждого Humanoid есть Inventory, а в Inventory лежат Item, а Item — это... ещё одна ветка дерева». Любая новая механика в RPG — квестовая роль, новая аура, модификатор предмета — врезается в дерево не по оси, ломая предположения, исходя из которых дерево строили. Базовые классы пухнут, инкапсуляция мешает системным проходам, а код разваливается на исключения и костыли.

Второй аспект «ООП головного мозга» — ритуал «состояние объекта меняет сам объект через свои методы». На бумаге это красиво: инкапсуляция, инварианты, «правильные» точки входа. На практике в RPG это превращает прямую, настольную логику в головоломку о том, «кто внутри кого вызывает кого».

В настольной RPG игрок двигает фишку, бросает кубик, вычитает броню цели, записывает урон на листе — последовательность очевидна и внешняя по отношению к «объектам». В ООП-версии мы вдруг пытаемся «наносить урон изнутри меча»: метод Sword.hit(target) лезет за параметрами атакующего, целится в защитника, спрашивает у «брони по которой ударили» модификатор, затем уведомляет target.takeDamage(...). А если урон идёт от «огненной ауры» на перчатках? Кто «владеет» действием — меч, рука, аура или персонаж? Где должен жить код? Как не нарушить инварианты десятка объектов одновременно?

Вместо простого внешнего правила «рассчитать урон по данным атакующего, защищающегося и типа попадания» мы создаём клубок взаимодействий методов, которым нужно «легально» менять чужое состояние. Любая новая механика (контрудар, шипы на броне, сопротивляемость части урона) приводит к комбинаторному взрыву количества мест, куда надо «проползти» и аккуратно вставить вызовы. Поток расчёта перестаёт быть видимым — он размазан по «правильным» классам и их методам. Так ритуал инкапсуляции начинает мешать самой сути игры: прозрачному, детерминированному конвейеру правил.

ООП отлично подходит как способ описания мира, но как «серебряная пуля» для игр он породил «деревья смерти». Реальные игры — это композиция данных и потоков обработки, а не учебная иерархия «микроволновки» с кнопками и дверцей.

А финальный акт трагедии выглядел так: команды, которые дотаскивали RPG до релиза, вынужденно «снимали корону» с ООП и сводили множество классов в один или несколько god-object — «менеджеров всего». В этих классах оказывались почти все данные и почти весь код боёвки/эффектов/квестов. Там, где раньше планировались «красивые виртуальные вызовы», появлялись switch/if‑цепочки по типам и флагам: быстрее, прозрачнее и ремонтопригоднее в дедлайны, чем разгонять сигналы по дереву наследования и ловить сайд‑эффекты инкапсуляции. Это важный симптом: даже убеждённые ООП‑практики в критический момент переходят к явному конвейеру и таблицам решений — потому что так проще контролировать логику игры.

Новая «пуля»: как ECS из оптимизации превратили в религию

Естественная реакция на гибельные деревья — отделить данные от логики. Ранние практики и публикации по ECS честно об этом: избавиться от «толстого базового класса», дать дизайнерам свободу комбинировать свойства через компоненты и получить предсказуемые линейные проходы по данным. Там ECS — это способ оптимизации доступа к памяти и ускорения итераций.

Что именно подразумевалось под «ранним» ECS:

  • Сущность (Entity) — лишь уникальный целочисленный ID. В сущности нет методов/данных.

  • Компонент (Component<T>) — только данные (plain struct).

  • Система (System) — обычная функция/функтор, которая объявляет требования к компонентам и выполняется в фиксированном порядке кадра. Система не хранит состояние игры, а действует над данными компонентов. Порядок выполнения систем — детерминирован: например, Input → Physics → Gameplay → Animation → Rendering. Задача ECS — ускорять внутри каждого шага массовые однотипные операции, а не скрывать порядок.

Этот «ранний» подход хорош тем, что возвращает контроль к данным и последовательным проходам: простые структуры, плотные контейнеры, детерминированный порядок систем. Он отлично масштабируется на массовые однотипные операции (позиции, физика, эффекты по срезам), упрощает профилирование и дебаг, а дизайнерам даёт гибкость — поведение задаётся комбинацией компонентов, без переписывания иерархий.

У него, впрочем, есть и недостатки, вот некоторые из них: структурные изменения (add/remove) стоят дороже при архетипном подходе — приходится использовать снимки/буферы команд, чтобы не ломать итерацию; запросы по нескольким компонентам — это всегда выбор компромисса между локальностью и простотой (sparse-set быстрее модифицируется, но хуже бьёт в кэш, архетипы локальны, но тяжелее на миграциях).

Люди любят догмы. Из инструмента сделали догмат: «на любое явление — компонент, на любую реакцию — система», на любое свойство — компонент. В результате простое событие «горение» размазывается по BurningComponent, BurningDamageComponent, IgniteEvent, BurningSystem, DamageOverTimeSystem, «тегу одного кадра» и буферу команд, только чтобы не нарушить «чистоту». Структурные изменения дорожают, последовательность систем превращается в скрытый протокол, а «архитектура ради архитектуры» начинает доминировать над геймдизайном.

Да, ECS может помочь ускорить обработку больших массивов данных (в основном за счёт уменьшения размера массива до состояния, когда он помещается в кэш). Но догматическое требование «всё разнести на компоненты и сотни микросистем» доводит подход до абсурда. Там, где нужен один ясный проход по данным и пара локальных структур, возникает «Скрытый протокол порядка систем»: поведение проекта зависит от последовательности апдейтов, что-то происходит сразу, а что-то, что тоже должно происходить мгновенно, происходит только через несколько кадров. Меняем порядок апдейтов — получаем регрессии класса «эффект сработал до урона», «статус сняли раньше проверки». Да, любой пайплайн имеет порядок, но в ECS он размазан по файлам регистраций и систем, плохо обозревается и тестируется изолированно.

Часто происходит «Взрыв микросистем»: требование «компоненты — только данные» толкает к дроблению поведения на десятки «малых систем». На бумаге это красиво («single responsibility»), на практике — каша из крошечных этапов, которые трудно держать в голове. Удобство рефакторинга покупается потерей когерентной картины.

Реальный выигрыш ECS — там, где много однотипных апдейтов каждый кадр (RTS с тысячами юнитов). В RPG и экшен-адвенчурах значимая часть логики — события, триггеры, эффекты, квестовые проверки, то есть «редкие изменения» и «короткие срезы». Там накладные расходы на инфраструктуру ECS могут превышать выигрыш от кэш‑локальности.

Простая реакция на событие («промах по летуну замедляет стрелка») превращается в цепочку новых компонент и микросистем «ради чистоты», вместо одного явного шага в конвейере тика. Когда код всё чаще объясняют словами «так требует наш фреймворк», а не «так проще и надёжнее», это красный флаг.

Сначала было ООП, где нам предложили идею, что фишка на доске должна сама себя двигать. Это породило монстров — хрупкие иерархии классов, где логика и данные были спутаны в один неразрывный ком. Их разбили на мелкие компоненты, и продолжили растирать компоненты в пыль, дойдя до полного абсурда. Отладка превратилась в кошмар. Как же быть?

Вернуться к истокам

ECS и ООП — инструменты. Они не обязаны становиться религией. Когда «плюсы» превращаются в ритуалы, пора положить код на стол и спросить: «Какие данные и в каком порядке мне реально нужно обрабатывать в каждом кадре?»

Посмотрите на настольную RPG: движение фишек по полю и записи на бумаге — это модель «плотные общие данные + редкие расширения». Часто используемые значения (позиция, очки здоровья, инициатива) лежат «в таблице на столе» — их видно всем и они обновляются каждый ход. Редкие свойства (ядовитая кровь, разовая метка заклинания, долговременный дебафф) фиксируются отдельно — условно «на полях листа» — и учитываются только когда актуальны.

Ровно так и должен выглядеть практичный рантайм:

  • держим «скелет» сущности с часто используемыми полями в одном плотном массиве,

  • добавляем при необходимости редкие/большие блоки данных динамически из пулов и обращаемся к ним только из тех функций, которым это нужно.

Это перекликается с тем, как писали игры в 80-е, когда места на диске у ЭВМ было меньше, чем объём кэша у современного процессора: один или несколько массивов записей фиксированного формата, детерминированный «главный цикл», минимум динамики и максимум последовательных проходов по данным. Тогда экономить память было нужно, чтобы игру в принципе стало возможно создать, сегодня, чтобы игровой сервер был экономически эффективным.

То есть «игровой мир» — это один или несколько массивов записей фиксированной длины; «логика» — один или несколько проходов по этим массивам с понятным порядком шагов. Минимум динамических структур, максимум последовательного доступа к памяти.

Это не «олдскул ради олдскула». Это модель, продиктованная ограничениями: простые структуры, предсказуемые проходы, данные «рядом». Ровно это мы возвращаем — только вместо «одного монолитного массива» даём скелету сущности плотные «горячие» поля, а редкие/большие части выносим в отдельные массивы/пулы. Получается современная версия той же идеи: данные линейны там, где это важно, а код читабелен и предсказуем. Это те самые соображения, которые легли в основу ECS, но воплощённые не в обобщённом шаблонном виде, а простым и эффективным способом, позволяющим не плодить сущности без надобности.

Идея не про «лопату на Си», а про аккуратный, предсказуемый поток данных без лишней инфраструктуры.

Там, где в ECS добавили бы тег/компонент и дождались бы безопасной фазы, в этой модели мы используем обычные очереди для отложенных действий и детерминированный порядок вызовов функций. Например: применяем урон/эффекты заклинаний, изменяя HP и складывая всех персонажей, которые могли умереть, в список для отложенной проверки, потом проверяем смерть и убираем умерших персонажей. Никаких структурных перестроек по ходу.

Такой конвейер и прозрачен, и легко расширяем. Можно смотреть на это как на прадедушку ECS и ООП.

Выделите «скелет» сущности: активность, позиция, базовое здоровье — всё, что нужно почти всегда. Поместите в единый массив структур Unit.

Вынесите опциональные блоки данных (физика, магия, инвентарь) в пулы. В Unit оставьте только указатели. Не плодите «мини‑указатели» без нужды.

Забудьте о "системах". Логика — это простая функция, которая проходит по главному массиву юнитов. Пишите обновление мира в одном месте в виде нескольких проходов, сохраняйте явный порядок вызовов.

Заведите простые очереди для отложенных событий вместо структурных изменений «на лету».

Почему это лучше?

Давайте сравним предложенный подход с альтернативами.

* Против "God-Object": Это не лапша‑код. Это чётко структурированный подход: одна главная структура данных, пулы для опциональных данных и набор независимых функций‑систем. Всё просто, предсказуемо и легко для понимания. Нет выворачивания естественной логики наизнанку, когда мечи считают урон.

* Против ECS: Простой подход позволяет избавиться от всего бойлерплейта. Не нужно создавать файлы, регистрировать типы. Нужно новое поведение? Добавьте параметр или указатель на блок параметров в Unit и допишите код обновления. Всё. Вы сфокусированы на игре, а не на архитектуре.

* А как же производительность? Благодаря пулам, все MagicData лежат в памяти подряд. Когда ваша функция ProcessMagic бежит по юнитам, она обращается к данным, которые находятся рядом в кэше. Да, есть один лишний переход по указателю, но вы избавлены от сложной машинерии ECS по сопоставлению Entity ID с индексами в десятках разных контейнеров. Мы получаем не меньшую производительность, но с на порядок меньшей сложностью.

Не нужно ходить кругами. Пришло время прекратить строить архитектурные соборы и начать писать игры. Этот прямой, прагматичный подход — и есть тот самый здравый смысл, который вы потеряли.

Комментарии (148)


  1. mrhearthstone
    08.08.2025 23:31

    Используйте функциональный - в чем проблема?


    1. Qweker
      08.08.2025 23:31

      Просто "мамкины пирожочки" не знают, что такое даже без типововое лямда - исчисление. Ты им про функциональное программирование ... .


  1. Spyman
    08.08.2025 23:31

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

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

    Проблемы начинаются при реактивных штуках (если противник парировал атаку, даёт +100% к слеюущей) но они и в случае хранения данных в общей куче - точно такие же.

    В своё время при разработке игры ооп очень круто помог масштабировать проект благодаря композиции. Игровые объекты были "актерами", и игровая сцена, содержащая в себе набор взаимодействующих актеров - тоже была актером (по интерфейсу). За счёт этого их можно было комбинировать как угодно, вкладывая одни готовые сценарии в другии в любых масштабах (как матрёшка, только круче).

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


    1. vadimr
      08.08.2025 23:31

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


      1. SAWER
        08.08.2025 23:31

        Логика разная, но интерфейсы одинаковые. Каждый в армии может работать по командам армии, а не, например, игрока. Но при этом действовать как отдельная единица по тем же командам, которые даёт 1 игрок. То же со сражением.

        Не понятно при чём тут композиция вообще. Новую логику так или иначе придётся отдельно реализовать, она само по себе не появится.


      1. RA_ZeroTech
        08.08.2025 23:31

        . Промахнулся


      1. RA_ZeroTech
        08.08.2025 23:31

        Можно это легко сделать шаблоном компановщик. Управлять N юнитами как одним.


        1. vadimr
          08.08.2025 23:31

          Так в том-то и проблема, что N юнитами по уму надо управлять совершенно по-другому, чем одним.


          1. totsamiynixon
            08.08.2025 23:31

            По-другому это как? Юнит может иметь разные навыки в зависимости от того, в отряде он или нет? Он обладает всеми навыками сразу. А вот какие будут использоваться зависит от стратегии - одиночка, малый отряд, батальон.


            1. vadimr
              08.08.2025 23:31

              Если используемые навыки юнита зависят не от него самого, а от способа его применения, то это не очень соответствует принципам ООП. Более того, зачем вообще нужны "навыки юнита" на таком уровне абстракции? Для результата сражения между армиями не важна индивидуальная подготовка конкретных солдат.


              1. Newbilius
                08.08.2025 23:31

                Возможно у вас видимо есть какой-то образ игры с юнитами отрядами, который может не совпадать с теми, кто вас читает. Опишите? Потому что для меня "юнит" умеет просто ходить и атаковать, а если он включается в отряд - просто мы управляем неким объектом, который отправляет команды ходить и атаковать юнитам.


                1. vadimr
                  08.08.2025 23:31

                  Так и происходит в играх с простой механикой, диктуемой как раз принципом декомпозиции. Но в реальной жизни-то это не так. Управление одним солдатом заключается в стрельбе и рукопашном бое, командир батальона управляет тактикой солдат по рации, а генерал, командующий армией, диктует стратегические приказы стенографистке (и при этом минимум 2/3 армии выполняет небоевые задачи). В играх посложнее это пытаются реализовать, с той или иной степенью успешности. Типа, сначала императорский флот захватил планету, потом там по ней AT-AT прошлись танковым строем, а потом за отдельного солдата можно поиграть.

                  Про то и речь, что отряд в виде группы клонированных юнитов - это жуткое упрощение игровой механики.


                  1. Newbilius
                    08.08.2025 23:31

                    Всё равно вас не понимаю, что вы имеете ввиду. Может приведёте пример? Классика RTS типа StarCraft, Warcraft, Homeworld, "Война и мир", Блицкриг и т.п. в таком "жутко упрощённом формате" не спроста стала классикой - видимо этого достаточно для создания увлекательного геймплея) А про какие игры думаете вы, когда описываете свою концепцию?


                    1. vadimr
                      08.08.2025 23:31

                      Ну например Star Wars Battlefront, из классики.

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


  1. vadimr
    08.08.2025 23:31

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


    1. MagisterAlexandr
      08.08.2025 23:31

      Здесь это где?


      1. vadimr
        08.08.2025 23:31

        Что-то не прописалась ссылка, спасибо. https://habr.com/ru/articles/919190/


  1. VitalyZaborov
    08.08.2025 23:31

    Можно пример какой-то игры, написанной по таким принципам? Потому что сейчас кажется, что на бумаге всё хорошо, а на практике такой подход будет работать только для несложной адаптации настолки, где правила изначально были достаточно простые, чтобы их мог понять человек:

    Выделите «скелет» сущности: активность, позиция, базовое здоровье — всё, что нужно почти всегда. Поместите в единый массив структур Unit.

    Это будет работать, если взаимодействовать между собой будут только персонажи. Если же у вас можно нанести урон неодушевлённому объекту (фаербол ломает бочку) и неодушевлённый объект сам может быть источником урона (бочка взрывается), то у вас на сцене вместо десятка юнитов их будут сотни: каждое дерево, сундук и даже стул, на котором сидит персонаж - это теперь всё юниты. И их список постоянно меняется, изменяя наш массив: гоблин кинул в игрока гранату? Плюс юнит, потому что граната - это тоже юнит. И так у нас весь выигрыш в производительности растворяется о тысячи проверок каждый кадр "а нет ли у дерева мозгов и не надо ли обновить ему ИИ", потому что вместо массива компонентов Brain у нас указатели в Unit.

    пора положить код на стол и спросить: «Какие данные и в каком порядке мне реально нужно обрабатывать в каждом кадре?»

    Что если данных - миллион и порядок обработки не детерминирован? У вас, к примеру, диаблоид, где и на персонаже, и на противниках куча предметов с эффектами возврата урона, спавна проджектайлов на атаке и прочего вампиризма. И состав этих эффектов ещё и постоянно меняется в реальном времени. Тогда у вас одна функция расчёта урона растянется на тысячи строк. В таких случаях гораздо удобнее все эффекты атаки / защиты описать в виде классов и хранить их экземпляры в списках для каждого юнита. В кого-то прилетел урон? Проходим по спискам атакующего у цели чтобы посчитать его значение и запустить всю дополнительную логику.

    Минимум динамических структур, максимум последовательного доступа к памяти.

    Упарываться в оптимизацию игровой логики на уровне доступа к памяти в 2025 - ну такое себе. Сколько должно быть юнитов и как они должны себя вести чтобы это давало значимый прирост в сравнении хотя бы затратами на поиск пути для этих всех юнитов?


    1. max-daniels
      08.08.2025 23:31

      Упарываться в оптимизацию игровой логики на уровне доступа к памяти в 2025 - ну такое себе. Сколько должно быть юнитов и как они должны себя вести чтобы это давало значимый прирост в сравнении хотя бы затратами на поиск пути для этих всех юнитов?

      Вон недавно анонсировали Ashes of the Singularity II с тысячами юнитов. Говорят, первая часть утилизировала все ядра процессора. Или всякие UEBS 2 с 1.3 миллионами юнитов. Движения юнитов я так понял там симулируется через физику жидкости(Fast Fluid Dynamics Simulation?), а не через какой-нибудь A*.


  1. CorruptotronicPervulator
    08.08.2025 23:31

     Кто «владеет» действием — меч, рука, аура или персонаж?

    Дамаг наносит не рука, аура и не персонаж. Оружие. Всегда оружие, остальное — модификаторы, которые должно опросить оружие.

    PS: если рука пустая, то оружие — «кулак», дробящее. Проще некуда.


    1. VitalyZaborov
      08.08.2025 23:31

      Маг кастует фаербол. Почему урон должно наносить оружие, которое, в данном случае, на него вообще не влияет? Логичнее оперировать уроном как неким объектом, который могут породить разные источники: оружие, магия, ловушки и т. п. Этот объект хранит все необходимые модификаторы атакующего и при применении к цели опрашивает её модификаторы.


      1. CorruptotronicPervulator
        08.08.2025 23:31

        Логичнее оперировать уроном как неким объектом, который могут породить разные источники: оружие, магия, ловушки и т. п. 

        Не противоречит, просто это ещё более общее определение. Вполне принимается.

        Маг кастует фаербол. Почему урон должно наносить оружие, которое, в данном случае, на него вообще не влияет?

        Я бы сказал, что в данном случае оружие — сам файрболл.


        1. vadimr
          08.08.2025 23:31

          Можно сказать и так, но тогда самому классу "оружие" будет сложно придать конкретное содержание. Между файрболом и мечом очень мало общего.


          1. CorruptotronicPervulator
            08.08.2025 23:31

            Как же там было… а, «оружие, покидающее руку».


          1. CorruptotronicPervulator
            08.08.2025 23:31

            Я, пожалуй, прокомментирую подробнее: тут получается либо а) каст файрболла (и любого заклинания) свободной рукой, что ничем не отличается от метательного ножа; б) каст с посоха/жезла, что ничем не отличается от арбалета; в) магия не с рук (вербальные заклинания, менталистика, амулеты) — реализованная магия становится также оружием, хотя с ограниченным сроком действия, т.е. создаётся, действует положенное время и уничтожается.

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


            1. PrinceKorwin
              08.08.2025 23:31

              Но код должен сидеть именно в объекте оружия.

              Почему же? Возьмем, к примеру, посох. Он не только файерболами умеет кидаться, но и много чего другого имеющие вполне себе уникальные характеристики (понижать здоровье, уменьшать скорость, накладывать дебафы и так далее).

              Вполне логично, что удобнее посох описывать отдельно и отдельно все то, что из него вылетает. И описание того, что из него вылетает вполне удобно хранить и обсчитывать отдельно.

              В противном случае код этого посоха будет уже почти god-object.


        1. Ndochp
          08.08.2025 23:31

          Фаербол (как тут уже писали) сам по себе персонаж порой. Так как маг конечно кастанул, создал объект. Но вполне возможно, что этот объект просто неспешно куда-то полетел. От него могут увернуться, его могут уничтожить и тд и тп. Вот когда он попадет, тогда урон и будем считать, от "кулака" фаербола.
          Если фаербол это только эффект, а удар всегда наносится, то это с точки зрения расчета ничем не отличается от удара мечем/кулаком кроме радиуса действия.


    1. DaneSoul
      08.08.2025 23:31

      Дамаг наносит не рука, аура и не персонаж. Оружие. Всегда оружие, остальное — модификаторы, которые должно опросить оружие.

      А если у нас ДВА оружия в руках? Например меч и кинжал или меч и щит (который тоже в определенных ситуациях используется как оружие)?


      1. CorruptotronicPervulator
        08.08.2025 23:31

        Это ДВА разных оружия в ДВУХ разных руках.


    1. TraX325
      08.08.2025 23:31

      Это может негативно повлиять на разнообразие игрового опыта. Если позволить игроку иметь оружие/щиты как модификаторы к тому, что он делает (т.е. когда урон наносится всё же персонажем), то перед ним откроется бескрайнее поле разных комбинаций снаряжения, которое, на первый взгляд, ему даже не подходит. Зачем магу кинжал и щит, если ему и с фаерболлами хорошо? А ведь если кинжал заставляет горящих противников взрываться при смерти, а щит даёт огненному урону свойства ледяного, то фаерболлами стрелять становится в разы интереснее. И эти предметы всё ещё хороши для того, чтобы кинжалом колоть, а щитом закрываться, если вложиться в огненный урон другим снаряжением или прокачкой. Важная ремарка, что я подразумеваю фаерболл не привязанным к волшебной палочке или перчаткам, персонаж просто "умеет" его делать.

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


  1. winorun
    08.08.2025 23:31

    В ООП-версии мы вдруг пытаемся «наносить урон изнутри меча»: метод Sword.hit(target) лезет за параметрами атакующего, целится в защитника, спрашивает у «брони по которой ударили» модификатор, затем уведомляет target.takeDamage(...). А если урон идёт от «огненной ауры» на перчатках? Кто «владеет» действием — меч, рука, аура или персонаж? Где должен жить код? Как не нарушить инварианты десятка объектов одновременно?

    Damage getDamage(Character * defender, Weapon * weapon){
    ....
    }

    wolf.DownHealth(getDamage(wolf,sword));
    wolf.DownHealth(getDamage(wolf,dager));

    И что здесь не так.


    1. vadimr
      08.08.2025 23:31

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


  1. JordanCpp
    08.08.2025 23:31

    Спасибо за большое количество кода, теперь мне все стало понятно. Текст отлично объясняет проблемы и даёт решение. Сарказм end.

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


  1. JordanCpp
    08.08.2025 23:31

    Описывать код только текстом это знаете моветон. Я что должен предполагать, что вы имели ввиду и по тексту в голове код писать?


    1. Biga
      08.08.2025 23:31

      Теперь ведь нейросети за вас код пишут?


      1. JordanCpp
        08.08.2025 23:31

        Да можно скормить статью ии и пусть сделает примеры кода, что имел автор ввиду. Как вариант.


      1. JordanCpp
        08.08.2025 23:31

        Пока за меня не пишут. Но я их использую для объснения мне определенных нюансов. В этом очень полезна.


  1. CloudlyNosound
    08.08.2025 23:31

    Десятилетиями нам рассказывают, что есть только два пути: громоздкие иерархии ООП или стерильная бюрократия ECS. Нас заставили поверить в то, что создание игр — это выбор между анархией и диктатурой.

    Господи! Да кто с вами всё это сделал? Сколько вас там? А сколько их? Есть понимание, на каком континенте находится бункер, в котором вас держат?

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

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


  1. SergeyGershkovich
    08.08.2025 23:31

    Предложенный метод "Учётной системы" применяется для управления предприятиями. Объекты в справочнике участвуют в различных регистрах - состояниях (моделях). Вычисления в регистрах могут быть взаимосвязаны транзакциями. Такой подход победил ООП и не даёт разбить на микросервисы ERP системы.

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

    Учётная система - комплексный подход к оптимизации связей элеметов. Кажется не сложно продумать заранее:

    • Регистр состояний (перчатка новая, перчатка требует починки);

    • Регистр владения (какие перчатки какому герою принадлежат);

    • Регистр столкновения юнитов (для всех перчаток, мечей, кольчуг проверяется их пересечение в 3д модели);

    • Другие регистры.

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

    ВЫВОД1: Описанная проблема не архитектурная, а организационная. Когда в команде распределяетя работа:

    • Начинающие разобьются на независимые объекты;

    • Опытные уже думают о группах сервисов состояний объектов;

    • Прожаренные смогут возглавить разработку по упрощённым шаблонам, не распыляясь на детализацию.

    ВЫВОД2: Любой шаблон - устаревшая архитектура. Придет однажды новичек с новым объектом, сломает шаблон и все начнется сначала.


  1. BenGunn
    08.08.2025 23:31

    Пример автора использования ООП на примере RPG игры говорит лишь о том, что автор не может в ООП.


    1. mvv-rus
      08.08.2025 23:31

      Нет, он всего лишь неправильно описал модель предметной области - а она там весьма нетривилаьная. Хотел расписать, в чем именно, но к счастью тут в другом комментарии уже привели ссылку на перевод цикла статей от разработчика Roslyn, он там свою предметную область иллюстрировал как раз примером из RPG. Если почитать тот цикл, то выяснится, что проблема имеет весьма нетривиальное решение, и для ее решения нужно не просто мочь в ООП, а владеть им хорошошо.
      Хотя вот мне почему-то решение с внешними объектами-правилами пришло в голову почти сразу. Возможно - это из-за специфического опыта: понимания как выглядит RPG в настольком варианте - там не "меч наносит удар", а "игрок объявляет Dungeon Master'у(ведущему), что он наносит удар мечом", а результат удара рассчитывает уже ведущий.


      1. chaetal
        08.08.2025 23:31

        Он всего лишь неправильно понимает суть ООП. Впрочем, как и миллионы бедолаг, которым втюхивали, что ООП — это про иерархии классов.


  1. Lekret
    08.08.2025 23:31

    Это конечно всё классно, но "Talk is cheap. Show me the code".

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

    Я просто представляю как 50 программистов на проекте, где 500 фичей в которых количество компонентов и систем иногда переваливает за полсотни, будут писать на этой одскульной парадигме.

    Как будет устроена изолированная работа, чтобы к примеру минимизировать конфликты при мёрджах?

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

    Ещё непонятно где данные держать, тот же массив Unit[] или MagicData[]. В глобальной переменной с типом GameData?)))

    но вы избавлены от сложной машинерии ECS по сопоставлению Entity ID с индексами в десятках разных контейнеров. Мы получаем не меньшую производительность, но с на порядок меньшей сложностью.

    Для этого существуют фреймворки, чтобы эту сложную машинерию прятать и заворачивать в общий интерфейс.