Компания
66,02
рейтинг
9 декабря 2014 в 04:21

Разработка → Создание World of Tanks Blitz на базе собственного движка DAVA

image

Пролог


Эта история началась более трех лет назад. Наша небольшая компания DAVA стала частью Wargaming, и мы начали обдумывать, какие проекты делать дальше. Чтобы напомнить, каким был мобайл три года назад, скажу, что тогда не было ни Clash Of Clans, ни Puzzle & Dragons, ни многих очень известных сегодня проектов. Mid-core тогда только-только начинался. Рынок был в разы меньше сегодняшнего.

Изначально всем казалось, что очень хорошей идеей будет сделать несколько мелких игр, которые бы привлекали новых пользователей в большие «танки». После ряда экспериментов оказалось, что это не работает. Несмотря на отличные конверсии в мобильных приложениях, переход от мобильного телефона к PC оказывался пропастью для пользователей.

Тогда в разработке у нас находилось несколько игр. Одна из них носила рабочее название «Sniper». Основной геймплей-идеей была стрельба в снайперском режиме из стоящего в обороне танка, по другим танкам, которыми управлял AI и которые могли атаковать в ответ.

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

С этого все и началось!

Когда мы начинали разработку “Снайпера”, то рассматривали технологии, которые тогда были доступны для мобильных платформ. На тот момент Unity был еще на достаточно ранней стадии своего развития: по сути, необходимых нам технологий еще не было.

Основной вещью, которой нам не хватало, был рендеринг ландшафта c динамической детализацией, что является жизненно необходимым для создания игры с открытыми пространствами. Было несколько сторонних библиотек для Unity, однако их качество оставляло желать лучшего.

Также мы понимали, что на C# мы не сможем выжать максимум из устройств, под которые мы разрабатываем, и всегда будем ограничены.
Unreal Engine 3 тоже не подходил по ряду похожих причин.

В итоге, мы решили дорабатывать свой движок!

Он на тот момент уже использовался в наших предыдущих казуальных проектах. Движок имел достаточно хорошо написанный низкий уровень работы с платформами и поддерживал iOS, PC, Mac, плюс были начаты работы по Android. Было написано много функциональности для создания 2D-игр. То есть, был неплохой UI и много всего для работы с 2D. В нем были первые шаги по 3D-части, так как одна из наших игр была полностью трехмерной.

Что у нас было в 3D-части движка:

  • Простейший граф сцены.
  • Возможность рисования статических мешей.
  • Возможность рисования анимированных мешей со скелетной анимацией.
  • Экспорт объектов и анимаций из Collada-формата.

В общем, если говорить о функциональности серьезного современного движка, в нем было очень мало.

Начало работ


Началось все с доказательства возможности отрисовать ландшафт на мобильных устройствах: тогда это были iPhone 4 и iPad 1.

После нескольких дней работы мы получили вполне функциональный динамический ландшафт, который работал довольно сносно, требовал где-то 8MB памяти и давал 60fps на этих устройствах. После этого мы начали полноценную разработку игры.

Прошло около полугода, и маленький мини-проект превратился в то, чем сейчас является Blitz. Появились совершенно новые требования: MMO, AAA-качество и другие требования, которые движок в его изначальном виде на тот момент уже не мог обеспечить. Но работа кипела полным ходом. Игра работала и работала неплохо. Однако производительность была средней, объектов на картах было мало, и, собственно, было множество других ограничений.

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

Как все работало на тот момент

Вся отрисовка сцен была основана на простой концепции Scene Graph.

Основной концепции являлись два класса:

  • Scene — контейнер сцены, внутри которого происходили все действия
  • над сценой.
  • SceneNode — базовый класс узла сцены, от которого наследовались все классы, которые находились в сцене:
  • MeshInstanceNode — класс для отрисовки мешей.
  • LodNode — класс для переключения лодов.
  • SwitchNode — класс для переключения свитч объектов.
  • еще около 15-ти классов наследников SceneNode.

Класс SceneNode позволял переопределить набор виртуальных методов, для реализации какой-то кастомной функциональности:
Основные функции, которые можно было переопределить, это:

  • Update — функция которая вызывалась для каждого узла, для того чтобы сделать Update-сцены.
  • Draw — функция, которая вызывалась для каждого узла, для того чтобы отрисовать этот узел.

Основные проблемы, с которыми мы столкнулись.

Во-первых, производительность:

  • Когда количество нодов в уровне достигло 5000, оказалось что просто пройти по всем пустым функциям Update, занимает около 3ms.
  • Аналогичное время уходило на пустые ноды, которым не требовалось Draw.
  • Огромное количество кэш-миссов, так как работа всегда велась с разнотипными данными.
  • Невозможность распараллелить работу на несколько ядер.

Во-вторых, непредсказуемость:

  • Изменение кода в базовых классах влияло на всю систему целиком, то есть каждое изменение SceneNode::Update могло сломать что угодно и где угодно. Зависимости становились все сложнее и сложнее, и каждое изменение внутри движка почти гарантированно требовало тестирования всей связанной функциональности.
  • Невозможно было сделать локальное изменение, например, в трансформациях, чтобы не задеть остальные части сцены. Очень часто малейшие изменения в LodNode (узел для переключения лодов), ломали что-то в игре.

Первые шаги по улучшению ситуации


Для начала мы решили полечить проблемы с производительностью и сделать это быстро.

Собственно, сделали мы это, введя дополнительный флаг NEED_UPDATE в каждой ноде. Он определял, нужно ли такой ноде вызывать Update. Это действительно повысило производительность, но создало целый ворох проблем. Фактически код функции Update выглядел вот так:

void SceneNode::Update(float timeElapsed)
{
     if (!(flags & NEED_UPDATE))return; 
     
     // the rest of the update function

     // process children
}


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

LodNode, и SwitchNode — ноды, отвечающие, соответственно, за переключение лодов (по расстоянию) и переключение объектов (например, разрушенных и неразрушенных) — начали регулярно ломаться.

Периодически тот, кто пытался исправить поломки, делал следующее: отключал NEED_UPDATE в базовом классе (ведь это было простое решение), и совершенно незаметно FPS опять падал.

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

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

Цели

  • Минимизация зависимости между независимыми подсистемами.
  • Изменения в трансформациях не должны ломать систему лодов, и наоборот
  • Возможность положить код на многоядерность.
  • Чтобы не было функций Update или аналогичных, в которых выполнялся разнородный независимый код. Легкая расширяемость системы новой функциональностью без полного перетестирования старой. Изменения в одних подсистемах не влияет на другие. Максимальная независимость подсистем.
  • Возможность расположить данные линейно в памяти для максимальной производительности.

Основной целью на первом этапе была выбрана переделка архитектуры так, чтобы все эти цели можно было выполнить.

Комбинирование компонентного и data-driven-подхода


Решением этой проблемы стал компонентный подход, комбинированный c data-driven подходом. Дальше по тексту я буду употреблять data-driven-подход, так как не нашел удачного перевода.

Вообще понимание компонентного подхода у многих людей самое разное. То же — и с data-driven.

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

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

Что же такое data-driven. В моем понимании, это подход к проектированию программного обеспечения, когда за основу потока выполнения программы берутся данные, а не логика.

На нашем примере представим следующую иерархию классов:

class SceneNode
{
     // Данные отвечающие за иерархические трансформации
     Matrix4 localTransform;
     Matrix4 worldTransform; 
     
     virtual void Update();
     virtual void Draw();     

     Vector<SceneNode*> children;
}

class LodNode
{
     // Данные cпецифичные для вычисления лодов
     LodDistance lods[4];

     virtual void Update(); // переопределен метод Update, для того чтобы в момент переключения лодов, включать или выключать какие-то из его чайлдов
     virtual void Draw(); // рисуем только текущий активный лод
};

class MeshNode
{
     RenderMesh * mesh; 

     virtual void Draw(); // рисуем меш
};


Код обхода этой иерархии иерархически выглядит так:

Main Loop:
rootNode->Update();
rootNode->Draw(); 

В данной иерархии C++ наследования мы имеем три различных независимых потока данных:

  • Трансформации
  • Лоды
  • Меши

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

Давайте представим, как это должно выглядеть в data-driven подходе. Напишу на псевдокоде, чтобы была понятна идея:

// Transform Data Loop:
for (each localTransform in localTransformArray)
{
     worldTransform = parent->worldTransform * localTransform;
}

// Lod Data Loop:
for (each lod in lodArray)
{
     // calculate lod distance and find nearest lod
     nearestRenderObject = GetNearestRenderObject(lod);
     renderObjectIndex = GetLodObjectRenderObjectIndex(lod);
     renderObjectArray[renderObjectIndex] = renderObject;
}

// Mesh Render Data Loop:
for (each renderObject in renderObjectArray)
{
     RenderMesh(renderObject);
}

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

Данные в data-driven подходе являются ключевым элементом программы. Логика — лишь механизмы обработки данных.

Новая архитектура


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

Читая информацию по этой теме, я наткнулся на блог T-Machine.

Он мне дал множество ответов, на мои вопросы, однако основным ответом было следующее:

• Entity не содержит никакой логики, это просто ID (или указатель).
• Entity знает только ID компоненты, которые ей принадлежат (или указатель).
• Компонент — это только данные, то есть. компонент не содержит никакой логики.
• Система — это код, который умеет обрабатывать определенный набор данных и выдавать на выходе другой набор данных.

Когда я понял это, в процессе дальнейшего изучения различной информации наткнулся на Artemis Framework и увидел хорошую реализацию этого подхода.
Исходники тут, если предыдущий линк не работает: Artemis Original Java Source Code

Если вы разрабатываете на Java, то очень рекомендую посмотреть на него. Очень простой и концептуально правильный Framework. На сегодняшний день он спортирован на кучу языков.

То, чем является Artemis, сегодня называют ECS (Entity Component System). Вариантов организации сцены на базе Entity, компонентов и data-driven достаточно много, однако мы по итогу пришли к архитектуре ECS. Сложно сказать, насколько это общепринятый термин, однако ECS значит, что есть следующие сущности: Entity, Component, System.

Самое главное отличие от других подходов это: Обязательное отсутствие логики поведения в компонентах, и отделение кода в системы.

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

Несмотря на гибкость, заканчивается обычно макаронами.

image

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

ECS — более чистый подход, и решает больше проблем.

Чтобы посмотреть на примере, как это работает в Artemis, можете глянуть вот тут.

Я на примере покажу, как это работает у нас.

Главным классом контейнером является Entity. Это класс, который содержит массив компонентов.

Вторым классом является Component. В нашем случае, это просто данные.

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

    enum eType
    {
        TRANSFORM_COMPONENT = 0,
        RENDER_COMPONENT,
        LOD_COMPONENT,
        DEBUG_RENDER_COMPONENT,
        SWITCH_COMPONENT,
        CAMERA_COMPONENT,
        LIGHT_COMPONENT,
        PARTICLE_EFFECT_COMPONENT,
        BULLET_COMPONENT,
        UPDATABLE_COMPONENT,
        ANIMATION_COMPONENT,
        COLLISION_COMPONENT,    // multiple instances
        PHYSICS_COMPONENT,
        ACTION_COMPONENT,       // actions, something simplier than scripts that can influence logic, can be multiple
        SCRIPT_COMPONENT,       // multiple instances, not now, it will happen much later.
        USER_COMPONENT,
        SOUND_COMPONENT,
        CUSTOM_PROPERTIES_COMPONENT,
        STATIC_OCCLUSION_COMPONENT,
        STATIC_OCCLUSION_DATA_COMPONENT, 
        QUALITY_SETTINGS_COMPONENT,   // type as fastname for detecting type of model
        SPEEDTREE_COMPONENT,
        WIND_COMPONENT,
        WAVE_COMPONENT,
        SKELETON_COMPONENT,

        //debug components - note that everything below won't be serialized
        DEBUG_COMPONENTS,
        STATIC_OCCLUSION_DEBUG_DRAW_COMPONENT,
        COMPONENT_COUNT
    };

Третим классом является SceneSystem:

    /**
        \brief  This function is called when any entity registered to scene.
                It sorts out is entity has all necessary components and we need to call AddEntity.
        \param[in] entity entity we've just added
     */
    virtual void RegisterEntity(Entity * entity);
    /**
        \brief  This function is called when any entity unregistered from scene.
                It sorts out is entity has all necessary components and we need to call RemoveEntity.
        \param[in] entity entity we've just removed
     */
    virtual void UnregisterEntity(Entity * entity);

Функции RegisterEntity, UnregisterEntity вызываются для всех систем в сцене тогда, когда мы добавляем или удаляем Entity из сцены.

    /**
        \brief  This function is called when any component is registered to scene.
                It sorts out is entity has all necessary components and we need to call AddEntity.
        \param[in] entity entity we added component to.
        \param[in] component component we've just added to entity.
     */
    virtual void RegisterComponent(Entity * entity, Component * component);

    /**
        \brief  This function is called when any component is unregistered from scene.
                It sorts out is entity has all necessary components and we need to call RemoveEntity.
        \param[in] entity entity we removed component from.
        \param[in] component component we've just removed from entity.
     */
    virtual void UnregisterComponent(Entity * entity, Component * component);

Функции RegisterComponent, UnregisterComponent вызываются для всех систем в сцене, тогда, когда мы добавляем или удаляем Component в Entity в сцене.
Также для удобства есть еще две функции:

    /**
        \brief This function is called only when entity has all required components.
        \param[in] entity entity we want to add.
     */
    virtual void AddEntity(Entity * entity);
    
    /**
        \brief This function is called only when entity had all required components, and don't have them anymore.
        \param[in] entity entity we want to remove.
     */
    virtual void RemoveEntity(Entity * entity);

Эти функции вызываются, когда уже создан заказанный набор компонентов с помощью функции SetRequiredComponents.

Например, мы можем заказать получение только тех Entities, у которых есть ACTION_COMPONENT и SOUND_COMPONENT. Передаю это в SetRequiredComponents и — вуаля.

Чтобы понять, как это работает, распишу на примерах, какие у нас есть системы:

  • TransformSystem — система которая отвечает за иерархию трансформаций.
  • SwitchSystem — система которая отвечает за переключения объектов, которые могут быть в нескольких состояниях, как например разрушенное и неразрушенное.
  • LodSystem — система которая отвечает за переключение лодов по расстоянию.
  • ParticleEffectSystem — система которая обновляет эффекты частиц.
  • RenderUpdateSystem — система которая обновляет рендер-объекты из графа сцены.
  • LightUpdateSystem — система которая обновляет источники света из графа сцены.
  • ActionUpdateSystem — система которая обновляет actions (действия).
  • SoundUpdateSystem — система которая обновляет звуки, их позицию и ориентацию.
  • UpdateSystem — система которая вызывает кастомные пользовательские апдейты.
  • StaticOcclusionSystem — система применения статического окклюжена.
  • StaticOcclusionBuildSystem — система построения статического окклюжена.
  • SpeedTreeUpdateSystem — система апдейта деревьев Speed Tree.
  • WindSystem — система расчета ветра.
  • WaveSystem — система расчета колебаний от взырвов.
  • FolliageSystem — система расчета растительности над ландшафтом.

Самый главный результат, которого мы добились, — высокая декомпозиция кода, отвечающего за разнородные вещи. Сейчас в функции TransformSystem::Process четко локализирован весь код, который касается трансформаций. Он очень прост. Его легко разложить на несколько ядер. И самое главное, сложно сломать что-то в другой системе, сделав логическое изменение в системе трансформаций.

В практически любой системе код выглядит следующим образом:

for (определенного набора объектов) 
{
  // получить необходимые компоненты 
  // выполнить действия над этими объектам
  // записать данные в компоненты
}

Системы можно классифицировать по тому как они обрабатывают объекты:

  • Требуется обработка всех объектов, которые находятся в системе:
    • Физика
    • Коллизии

  • Требуется обработка только помеченных объектов:
    • Система трансформаций
    • Система actions (действий)
    • Система обработки звуков
    • Система обработки частиц

  • Работа со своей специально оптимизированной структурой данных:
    • Static Occlusion System

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

Итог


  • Мы сильно повысили FPS, так как с компонентным подходом вещи стали более независимы и мы смогли их по отдельности развязать и оптимизировать.
  • Архитектура стала более простой и понятной.
  • Стало легко расширять движок, почти не ломая соседние системы.
  • Стало меньше багов из серии «сделав что-то c лодами, сломали свитчи», и наоборот
  • Появилась возможность это все распараллеливать на несколько ядер.
  • На текущий момент, уже работаем над тем, чтобы все системы запускать на всех доступных ядрах.

Код нашего движка находится в Open Source. Движок в том виде, в котором он используется в World of Tanks Blitz, полностью доступен в сети на github.

Соответственно, если есть желание можете заходить и смотреть на нашу имплементацию в деталях.

Учитывайте тот факт, что все писалось в реальном проекте, и, конечно, это не академическая реализация.

Планы на будущее:


  • Более эффективный менеджмент данных компонетов, то есть разложить данные компоненты линейно в памяти, для минимизации кэш-миссов
  • Переход на многозадачность во всех системах.

Все полезные ссылки из текста напоследок:

Автор: @binaryzebra

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

  • +8
    Спасибо, почерпнул для себя недостатки сценографа, взял на карандаш ECS-подход.

    Отдельное спасибо за открытый исходный код, думаю, многим пригодится.
  • +1
    Но ведь фактически вы переизобрели unity с ее компонентным подходом (ну или Ogre). Если вы изучали ее когда делали анализ готовых решений — неужели это было не очевидно? И почему «Также мы понимали, что на C# мы не сможем выжать максимум из устройств» если вы изначально выходили на iOS, а там MSIL прогоняется через AOT и получается нейтивный код? Что будет если вы захотите выйти на другие платформы? Например, на тот же SmartTV как один из необычных и интересных вариантов.
    • 0
      Unity, как раз таки тот вариант когда компоненты могут быть полиморфны. Тоже хороший подход, однако, мы пошли дальше. ECS это больше возможностей для производительности на современном железе.

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

      Производительность Unity, это отдельный вопрос. Не исследовал его детально, но из того что слышал, от коллег по цеху, люди сталкиваются с проблемами особенно на Android.
    • 0
      К сожалению, Unity далеко не панацея, и не серебряная пуля в геймдеве. Как верно заметил binaryzebra, некоторые исправления приходится ждать месяцами, даже когда речь идет о пустяках. Для меня это было особенно остро, когда они внедрили нативное 2D. Халтурная имплементация Box2D в версии 4.3, которую поправили спустя много месяцев.

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

      Естественно, если квалификация программистов все это позволяет.
      • 0
        Ну мне видится основной смысл статьи так — «мы посмотрели сторонние ассеты для террейна, они нам показались сырыми и поэтому мы решили написать свое двигло полностью». Немного несоизмеримые вещи. То же самое и с остальными готовыми решениями типа ue. Когда говорят «вот были бы исходники, все бы быстро поправили», то обычно забывают о том, насколько большой зоопарк устройств юнити поддерживает в плане костылей вокруг багов в дровах и железе. Например, вся 4 мажорная версия в каждом апдейте содержала workaround-ы для самсунгов и тегр, а так же интегрированных решений от интела. Обеспечить такое же количество гарантированно работающих девайсов с учетом их косяков — это практически нереально в случае своего двигла. Понятно, что для варгейминга это мелочи, но это скорее исключение чем правило.
        • +1
          Выбор технологии это был серьезный вопрос, который поднимался несколько раз в процессе разработки. Подходили к нему тоже достаточно серьезно. Просто не формат статьи детально про это писать, ситуация 3 года назад, это не ситуация сейчас, и конечно сейчас надо смотреть на выбор технологии по новому.

          Но если по ситуации на 3 года назад то было вот так:

          — 3 года назад, Unity для мобильных был очень сырой. Там фактически не было ничего.
          Сейчас конечно надо оценивать заново, однако надо понимать, что когда движок пишется специально под какую-то задачу, он как правило лучше выполняет эту задачу.

          — UE3 — не был доступен публично для лицензирования, он появился где-то уже через год нашей разработки. Цена UE3 для приватного лицензирования, была приличная. Так же как и UE4 сегодня, без 5% от gross revenue.

          — Ни в одном из этих движков тогда не было нормального UI, а для нашей игры, это было критично.

          Сейчас ситуация другая, однако все равно на текущий момент Unity и UE4 обладают рядом недостатков, которые люди решают в своих движках. Есть вполне резонное обоснование.
          • 0
            3 года назад, Unity для мобильных был очень сырой.

            3 года назад юнити фактически отличалась только отсутствием mecanim и штатной 2d-рендера/физики, что в случае танков как мертвому припарка. Ну да, не было теней на мобилках, как и железа, способного тянуть такое. Все остальное до сих пор замерло в развитии примерно на уровне 3.х версии (как раз актуальной 3 года назад) и поменяется только в 5-ке. Относительно вменяемый гуй тоже уже был — Ngui (тогда 2.х ветка, сейчас 3.х), который и сейчас дает прикурить штатному гую, запиленному на его основе.
            Не хочу спорить, но фраза выглядит черезчур голословной.
  • +1
    Спасибо за интересную статью, я уже наверное с месяц поиграл в Блитз, но не играю в основном потому что Блитз нереально жрёт батарейку, даже стоя на зарядке в 1А телефон умудряется садится достаточно быстро. Планируется ли что-нибудь в этом направлении?
    • +1
      Да, постоянно оптимизируем. Большой проект очень для мобильных платформ, количество задач и проблем огромно :-) работаем над всем, по приоритетам. Одна из основных это производительность, и батарея.
  • 0
    А что у вас во фреймворке делает Box2D?
    • 0
      Ну движок то не только 3Д, но в детстве и 2Д, как я понял из статьи. Так же на гитхабе есть Scene2D, что как бы намекает. Надеюсь автор поста разъяснит.
    • 0
      Когда-то давно, делали казуалочки. Для них и использовалось.
      • 0
        То есть у вас там не всё, что в движке — необходимо? Будете вырезать?
        • 0
          Пока не планируем. Код который не используется, автоматически вырезается на линковке. Если будут желающие сделать казуалочку, им пригодиться.
          Раньше поддержка 2D была в 2-х видах, UI и Scene2D. Сейчас активно будем поддерживать UI, Scene2D пока нет необходимости.
          • 0
            У вас там столько всего, что страшно становится. Внешние зависимости я так понимаю являются частью проекта.
            Откуда начинать собирать вообще неясно.
            Если инструция какая — нить может?
            • +1
              Инструкции выложим в ближайшее время, сейчас активно переходим на cmake, чтобы стало проще с зависимостями. Сейчас можете пробовать собрать TemplateProject. Для смелых ResourceEditor. Это редактор сцен.
  • 0
    Жаль что ссылки на Artemis не работают. Надеюсь что временно.
    Подход ECS необычен и очень симпатичен.
  • 0
    Этот подход более известен как data-oriented design. Data-driven было про то как меняя данные можно менять всю игру не трогая код.
    Обязательная ссылка на то где все описанные проблемы расписаны: research.scee.net/files/presentations/gcapaustralia09/Pitfalls_of_Object_Oriented_Programming_GCAP_09.pdf
    • 0
      Очень хорошая статья, там также более детально расписано то что я лишь вкратце осветил, по оптимизации.
  • +2
    Кстати, в 2d я пришёл приблизительно к той же схеме. Никакой прямой реакции на действия пользователя — только изменение модели данных, а уже за ней изменение отображения.
  • +1
    Порадовал тонкий троллинг на первой картинке. Понимаю вашу боль. :)
  • +1
    Вообще чем больше open source движков от крупных компаний тем лучше, ну уж точно не хуже ;) А писаки про священную корову на c# уже достали, ну норм она, никто не спорит.

    Но для open source нету самого главного! Простого и вменяемого «get started» и маломальского мануала. Без этого исходники эти похожи на кучу кода, которые в чьих-то руках могут превратиться в очень хорошую игру, но другие с ними ничего не сделают.

    И еще странно… Почему зависимости лежат в проекте? Они тоже изменялись?
    • 0
      Может для статистической линковки?
    • 0
      Get Started — будет. Не обещаю что скоро :-) но обещаю что будет.
      С зависимостями сейчас думаем как это сделать лучше. После перехода на cmake, думаю станет проще.
  • 0
    Комментарий удалён.

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.

Самое читаемое Разработка