Содержание

Как написать собственный игровой движок на C++ / Habr

Перевод статьи Джеффа Прешинга (Jeff Preshing) How to Write Your Own C++ Game Engine.


Как написать собственный игровой движок на C++

В последнее время я занят тем, что пишу игровой движок на C++. Я пользуюсь им для создания небольшой мобильной игры Hop Out. Вот ролик, записанный с моего iPhone 6. (Можете включить звук!)


Your browser does not support HTML5 video.

Hop Out — та игра, в которую мне хочется играть самому: ретро-аркада с мультяшной 3D-графикой. Цель игры — перекрасить каждую из платформ, как в Q*Bert.

Hop Out всё ещё в разработке, но движок, который приводит её в действие, начинает принимать зрелые очертания, так что я решил поделиться здесь несколькими советами о разработке движка.

С чего бы кому-то хотеть написать игровой движок? Возможных причин много:


  • Вы — ремесленник. Вам нравится строить системы с нуля и видеть, как они оживают.
  • Вы хотите узнать больше о разработке игр. Я в игровой индустрии 14 лет и всё ещё пытаюсь в ней разобраться. Я даже не был уверен, что смогу написать движок с чистого листа, ведь это так сильно отличается от повседневных рабочих обязанностей программиста в большой студии. Я хотел проверить.
  • Вам нравится ощущение контроля. Организовать код именно так, как вам хочется, и всегда знать, где что находится — это приносит удовольствие.
  • Вас вдохновляют классические игровые движки, такие как AGI (1984), id Tech 1 (1993), Build (1995), и гиганты индустрии вроде Unity и Unreal.
  • Вы верите, что мы, индустрия игр, должны сбросить покров таинственности с процесса разработки движков. Мы пока не очень-то освоили искусство разработки игр — куда там! Чем тщательнее мы рассмотрим этот процесс, тем выше наши шансы усовершенствовать его.

Игровые платформы в 2017-ом — мобильные, консоли и ПК — очень мощные и во многом похожи друг на друга. Разработка игрового движка перестала быть борьбой со слабым и редким железом, как это было в прошлом. По-моему, теперь это скорее борьба со

сложностью вашего собственного произведения. Запросто можно сотворить монстра! Вот почему все советы в этой статье вращаются вокруг того, как сохранить код управляемым. Я объединил их в три группы:


  1. Используйте итеративный подход
  2. Дважды подумайте, прежде чем слишком обобщать
  3. Осознайте, что сериализация — обширная тема.

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




Используйте итеративный подход

Мой первый совет — не задерживаясь заставьте что-нибудь (что угодно!) работать, затем повторите.

По возможности, начните с образца приложения, которое инициализирует устройство и рисует что-нибудь на экране. В данном случае я скачал SDL, открыл Xcode-iOS/Test/TestiPhoneOS.xcodeproj, затем запустил на своём iPhone пример testgles2.

Вуаля! У меня появился замечательный вращающийся кубик, использующий OpenGL ES 2.0.

Моим следующим шагом было скачивание сделанной кем-то 3D-модели Марио. Я быстро написал черновой загрузчик OBJ-файлов — этот формат не так уж сложен — и подправил пример, чтобы он отрисовывал Марио вместо кубика. Ещё я интегрировал SDL_Image, чтобы загружать текстуры.

Затем я реализовал управление двумя стиками, чтобы перемещать Марио. (Поначалу я рассматривал идею создания dual-stick шутера. Впрочем, не с Марио).

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

К тому моменту я отказался от формата OBJ и написал скрипт на Python для экспорта собственных JSON-файлов из Blender. Эти JSON-файлы описывали заскиненный меш, скелет и данные анимации. Я загружал эти файлы в игру с помощью библиотеки C++ JSON.

Как только всё заработало, я вернулся в Blender и создал более проработанного персонажа (Это был первый сделанный и зариганный мной трёхмерный человек. Я им весьма гордился.)

В течение следующих нескольких месяцев я сделал такие шаги:


  • Начал выделять функции работы с векторами и матрицами в собственную библиотеку трёхмерной математики.
  • Заменил .xcodeproj на проект CMake
  • Заставил движок запускаться и на Windows, и на iOS, потому что мне нравится работать в Visual Studio.
  • Начал перемещать код в отдельные библиотеки «engine» и «game». Со временем, я разделил их на ещё более мелкие библиотеки.
  • Написал отдельное приложение, чтобы конвертировать мои JSON-файлы в бинарные данные, которые игра может загружать напрямую.
  • В какой-то момент убрал все библиотеки SDL из iOS-сборки. (Cборка для Windows всё ещё использует SDL.)

Ключевой момент в следующем: я не планировал архитектуру движка до того как начал программировать

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

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

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

.cpp-файла в другой, извлечение определений функций в .h-файлы или другие не менее простые действия. Определить, где что должно лежать — сложная задача, и решить её проще, когда код уже существует.

Готов поспорить, что больше времени тратится при противоположном подходе: пытаться заранее продумать архитектуру, которая будет делать всё, что вам понадобится. Две моих любимых статьи про опасности чрезмерной инженерии — The Vicious Circle of Generalization Томаша Дабровски и Don’t Let Architecture Astronauts Scare You Джоэла Спольски.

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

Итеративный подход дал мне куда более элегантную архитектуру, чем я мог бы вообразить, глядя на чистый лист бумаги. iOS-сборка моего движка сегодня на 100% состоит из оригинального кода, включая собственную математическую библиотеку, шаблоны контейнеров, систему рефлексии/сериализации, фреймворк рендеринга, физику и аудио микшер. У меня были причины писать каждый из этих модулей самостоятельно, но для вас это может быть необязательным. Вместо этого есть множество отличных библиотек с открытым исходным кодом и разрешительной лицензией, которые могут оказаться подходящими вашему движку. GLM, Bullet Physics и STB headers — лишь некоторые из интересных примеров.




Дважды подумайте, прежде чем слишком обобщать

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


Время от времени нарушайте принцип DRY

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


  • Owned<> для динамически выделяемых объектов, имеющих единственного владельца.
  • Reference<>
    использует подсчёт ссылок чтобы позволить объекту иметь несколько владельцев.
  • audio::AppOwned<> используется кодом за пределами аудио микшера. Это позволяет игровым системам владеть объектами, которые аудио микшер использует, такими как голос, который в данный момент воспроизводится.
  • audio::AudioHandle<> использует систему подсчёта ссылок, внутреннюю для аудио микшера.

Может показаться, что некоторые из этих классов дублируют функциональность других, нарушая принцип DRY. В самом деле, в начале разработки я пытался повторно использовать существующий класс Reference<> как можно больше. Однако, я выяснил, что время жизни аудио-объекта подчиняется особым правилам: если объект закончил воспроизведение фрагмента, и игра не владеет указателем на этот объект, его можно сразу же поместить в очередь на удаление. Если игра захватила указатель, тогда аудио-объект не должен быть удалён. А если игра захватила указатель, но владелец указателя уничтожен до того, как воспроизведение закончилось, оно должно быть отменено. Вместо того чтобы усложнять

Reference<>, я решил, что будет практичнее ввести отдельные классы шаблонов.

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


Использовать разные соглашения о вызове — это нормально

Одна из вещей, которая мне не нравится в Java — то, что она заставляет вас определять каждую функцию внутри класса. По-моему, это бессмысленно. Это может придать вашему коду более единообразный вид, но также поощряет переусложнение и не поддерживает итеративный подход, описанный мной ранее.

В моём C++ движке некоторые функции принадлежат классами, а некоторые — нет. Например, каждый противник в игре — это класс, и бо́льшая часть поведения противника реализована в этом классе, как и следовало ожидать. С другой стороны, sphere casts в моём движке выполняются вызовом sphereCast(), функции в пространстве имён physics. sphereCast() не принадлежит какому-либо классу — это просто часть модуля physics. У меня есть система сборки, которая управляет зависимостями между модулями, что сохраняет код достаточно (для меня) хорошо организованным. Заворачивание этой функции в произвольный класс никоим образом не улучшит организацию кода.

А ещё есть динамическая диспетчеризация, которая является формой полиморфизма. Часто нам нужно вызвать функцию объекта, не зная точного типа этого объекта. Первый порыв программиста на C++ — определить абстрактный базовый класс с виртуальными функциями, затем перегрузить эти функции в производном классе. Работает, но это лишь одна из техник. Существуют и другие методы динамической диспетчеризации, которые не привносят так много дополнительного кода, или имеют другие преимущества:


  • С++11 ввел std::function, и это удобный способ хранить функции обратного вызова. Также можно написать собственную версию std::function, не вызывающую столько боли, когда заходишь в неё в отладчике.
  • Многие функции обратного вызова могут быть реализованы с помощью пары указателей: указателя на функцию и непрозрачного аргумента. Требуется только явное приведение внутри функции обратного вызова. Это часто встречается в библиотеках на чистом C.
  • Иногда базовый тип известен во время компиляции, и можно привязать вызов функции вообще без накладных расходов времени выполнения. Turf — библиотека, которой я пользуюсь в своём игровом движке, сильно полагается на этот способ. Взгляните на turf::Mutex для примера. Это просто typedef над платформо-специфичными классами.
  • Иногда самый прямой путь — создать и поддерживать таблицу сырых указателей на функцию своими силами. Я использовал этот подход в своих аудио микшере и системе сериализации. Интерпретатор Python также на полную использует эту технику, как будет показано ниже.
  • Вы можете даже хранить указатели на функцию в хэш-таблице, используя имена функций как ключи. Я пользуюсь этой техникой для диспетчеризации событий ввода, таких как события мультитача. Это часть стратегии по записи ввода игры и воспроизведения его в системе реплеев.

Динамическая диспетчеризация — обширная тема. Я лишь поверхностно рассказал о ней, чтобы показать как много способов реализации существует. Чем больше растяжимого низкоуровневого кода вы пишите — что не редкость для игрового движка — тем чаще обнаруживаете себя за изучением альтернатив. Если вы не привыкли к программированию в таком виде, интерпретатор Python, написанный на C — отличный пример для изучения. Он реализует мощную объектную модель: каждый PyObject указывает на PyTypeObject, а каждый PyTypeObjeсt содержит таблицу указателей на функцию для динамической диспетчеризации. Документ Defining New Types — хорошая начальная точка, если вы хотите сразу погрузиться в детали.




Осознайте, что сериализация — обширная тема

Сериализация — это преобразование объектов времени выполнения в последовательность байтов и обратно. Другими словами, сохранение и загрузка данных.

Для многих, если не большинства, движков игровой контент создаётся в разных редактируемых, таких как .png, .json, .blend или проприетарных форматах, затем в конце концов конвертируется в платформо-специфичные форматы игры, которые движок может быстро загрузить. Последнее приложение в этом процессе часто называют «cooker». Cooker может быть интегрирован в другой инструмент или даже распределяться между несколькими машинами. Обычно, cooker и некоторое количество инструментов разрабатываются и поддерживаются в тандеме с самим игровым движком.

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

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

    void load(InStream& in, u32 fileVersion) {
        // Загрузить ожидаемые переменные-члены
        in >> m_position;
        in >> m_direction;

        // Загрузить более новую переменную только если версия загружаемого файла больше 2.
        if (fileVersion >= 2) {
            in >> m_velocity;
        }
    }

Можно писать более гибкий, менее подверженный ошибкам код сериализации, пользуясь преимуществом рефлексии — а именно, созданием данных времени выполнения, описывающих расположение ваших C++ типов. Чтобы получить краткое представление о том, как рефлексия может помочь с сериализацией, взглянем на то, как это делает Blender, проект с открытым исходным кодом.

Когда вы собираете Blender из исходников, выполняется много шагов. Во-первых, компилируется и запускается собственная утилита makesdna. Эта утилита парсит набор заголовочных файлов C в дереве исходников Blender, а затем выводит краткую сводку со всеми определёнными типами в собственном формате, известном как SDNA. Эти SDNA-данные служат данными рефлексии. SDNA затем компонуется с самим Blender, и сохраняется с каждым .blend-файлом, который Blender записывает. С этого момента, каждый раз когда Blender загружает .blend-файл, он сравнивает SDNA .blend-файла cо SDNA, скомпонованной с текущей версией во время исполнения и использует общий код сериализации для обработки всех различий. Эта стратегия даёт Blender впечатляющий диапазон обратной и прямой совместимости. Вы всё ещё можете загрузить файлы версии 1.0 в последней версии Blender, а новые .blend-файлы могут быть загружены в старых версиях.

Как и Blender, многие игровые движки — и связанные с ними инструменты — создают и используют собственные данные рефлексии. Есть много способов делать это: вы можете разбирать собственный исходный код на C/C++, чтобы извлечь информацию о типах, как это делает Blender. Можете создать отдельный язык описания данных и написать инструмент для генерации описаний типов и данных рефлексии C++ из этого языка. Можете использовать макросы препроцессора и шаблоны C++ для генерации данных рефлексии во время выполнения. И как только у вас под рукой появятся данные рефлексии, открываются бесчисленные способы написать общий сериализатор поверх всего этого.

Несомненно, я упускаю множество деталей. В этой статье я хотел только показать, что есть много способов сериализовать данные, некоторые из которых очень сложны. Программисты просто не обсуждают сериализацию столько же, сколько другие системы движка, даже несмотря на то, что большинство других систем зависят от неё. Например, из 96 программистских докладов GDC 2017, я насчитал 31 доклад о графике, 11 об онлайне, 10 об инструментах, 3 о физике, 2 об аудио — и только один, касающийся непосредственно сериализации.

Как минимум, постарайтесь представить, насколько сложными будут ваши требования. Если вы делаете маленькую игру вроде Flappy Bird, с несколькими ассетами, вам скорее всего не придётся много думать о сериализации. Вероятно, вы можете загружать текстуры напрямую из PNG и этого будет достаточно. Если вам нужен компактный бинарный формат с обратной совместимостью, но вы не хотите разрабатывать свой — взгляните на сторонние библиотеки, такие как Cereal или Boost.Serialization. Не думаю, что Google Protocol Buffers идеально подходят для сериализации игровых ресурсов, но они всё равно стоят изучения.

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

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

habr.com

Как создать движок для сайта

Вы здесь: Главная — PHP — PHP Основы — Как создать движок для сайта

У меня часто спрашивают: «Как создать свой собственный движок для сайта, например, как Joomla, DLE, WordPress и другие«. Многие, конечно, спросят: «А зачем, ведь есть, например, Joomla?«. На этот вопрос я уже отвечал в этой статье: joomla vs сайт с нуля. И если Вы приняли решение о создании своего движка для сайта, то читайте дальше.

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

  1. Сверстайте все страницы сайта. Здесь совсем необязательно рисовать какой-то дизайн — это можно сделать потом. Вы должны просто чётко нарисовать структуру внешнего вида: где будет лого, где меню, сколько колонок у Вас будет, что будет в подвале и так далее. Это всё можно будет потом поменять, но начальном этапе Вам просто нужно отлаживать движок, поэтому самый элементарный внешний вид всё равно потребуется.
  2. Создайте все таблицы для базы данных. В этом пункте необходимо понять, какие таблицы Вам потребуется и какие там будут поля. Например, таблица со статьями, с пользователями, с разделами сайта и так далее. Ничего страшного не будет, если Вы что-то забудете. Но этот пункт надо выполнить максимально хорошо, потому что после этого внутренняя структура движка станет намного понятнее.
  3. Теперь Вам необходимо создать набор классов (да, советую использовать объектно-ориентированное программирование, поверьте — так будет намного проще), каждый из которых отвечает за определённую часть. Например, класс по работе с базой данных, класс по управлению пользователями и так далее. Обратите внимание, что один класс не должен реализовывать задачи, которые по смыслу относятся к другим. Например, не надо соединяться с базой данных в классе пользователя. А надо создать объект базы данных в классе пользователя и им манипулировать. В каждом классе Вы должны написать все методы, которые Вам потребуются. Например, метод получения логина пользователя по его id или метод изменения e-mail пользователя и так далее. Также наверняка Вам потребуется какой-нибудь глобальный абстрактный класс, в котором будут содержаться методы, общие для всех других. Например, очень полезный метод — это получения значения некоего поля, по известному имени и значению другого поля. Например, мы знаем e-mail пользователя, и хотим узнать его логин. Такой метод легко данную задачу решит.
  4. Разбейте Ваш шаблон на составные части и отправьте их в отдельные файлы с расширением tpl. Делается это так: копируете какую-то отдельную часть (например, форму авторизации, часть, где выводятся статьи, меню и другие) и вставляете в отдельный файл tpl. В результате, у Вас появится большое количество файлов-шаблонизаторов. В этих файлах-шаблонизаторах поставьте {элемент_шаблона}. Например, там где должно быть имя пользователя поставьте {username}. Там, где должен быть заголовок статьи, поставьте {article_title}. Аналогичным образом Вам надо исправить каждый файл tpl.
  5. Дальше создайте класс, который будет работать с файлами-шаблонами. В задачу этого класса будет входить работа с основными другими классами (например, классом пользователей), а также подстановка соответствующих данных в файлы-шаблоны вместо {элемент}. Например, вместо {username} этот класс должен будет подставить имя пользователя. Разумеется, здесь будет очень много методов, каждый из которых выполняет свою функцию.
  6. Выводите все элементы на страницах сайта, используя класс созданный в предыдущем пункте, собирая тем самым страницы по кирпичикам.
  7. И, наконец, внешний вид сайта — он же дизайн. Исправляйте файлы tpl (оставляя {элементы}), добавляйте CSS-файлы и JavaScript. А функциональность трогать уже не надо (если Вы всё сделали правильно, конечно).

Всё, движок для сайта создан! Я понимаю, что многие из Вас сейчас сидят в шоке, не понимая, как это всё реализовать. Это действительно сложно для начинающего, поэтому сначала Вам надо набраться опыта в простых вещах. А вот дальше уже реализовать подобные вещи. Если остались какие-то вопросы, то Вы можете задать их в комментариях. А в следующей статье я расскажу, как создаётся Admin-панель для сайта, так что подписывайтесь, чтобы не пропустить.

Пример создания реального движка с нуля можно посмотреть в этом курсе: http://srs.myrusakov.ru/php

  • Создано 28.05.2011 14:44:36
  • Михаил Русаков
Предыдущая статья Следующая статья

Копирование материалов разрешается только с указанием автора (Михаил Русаков) и индексируемой прямой ссылкой на сайт (http://myrusakov.ru)!

Добавляйтесь ко мне в друзья ВКонтакте: http://vk.com/myrusakov.
Если Вы хотите дать оценку мне и моей работе, то напишите её в моей группе: http://vk.com/rusakovmy.

Если Вы не хотите пропустить новые материалы на сайте,
то Вы можете подписаться на обновления: Подписаться на обновления

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

Порекомендуйте эту статью друзьям:

Если Вам понравился сайт, то разместите ссылку на него (у себя на сайте, на форуме, в контакте):

  1. Кнопка:
    <a href=»https://myrusakov.ru» target=»_blank»><img src=»https://myrusakov.ru//images/button.gif» alt=»Как создать свой сайт» /></a>

    Она выглядит вот так:

  2. Текстовая ссылка:
    <a href=»https://myrusakov.ru» target=»_blank»>Как создать свой сайт</a>

    Она выглядит вот так: Как создать свой сайт

  3. BB-код ссылки для форумов (например, можете поставить её в подписи):
    [URL=»https://myrusakov.ru»]Как создать свой сайт[/URL]

myrusakov.ru

Зачем писать свой игровой движок? / Social Quantum corporate blog / Habr

В декабре прошлого года, на конференции Games Gathering 2017, мы сделали доклад, в котором рассказали о том, надо ли компаниям, работающим в игровой индустрии, писать собственные движки.



Unity в условиях многообразия игровых движков



Зачем в современном мире, в котором существуют такие гиганты, как Unity, делать что-то своё, писать собственные игровые движки?

Перед вами слайд с данными компании Unity Technologies по использованию различных игровых движков в 2017 году. В следующем году ситуация, как вы понимаете, поменяется. Нас интересует то, чему тут отведён 41%, то есть — движки собственной разработки. Если взглянуть на 5 самых больших компаний, представленных на конференции, то у каждой из них будет свой движок. Получается, что в большей части компаний, представляющих игровую индустрию, используются какие-то внутренние разработки. Далеко не всегда это — самое разумное в мире решение, но это так.

Из каких базовых технологий могут выбирать компании, задумывающиеся о разработке игрового проекта?

Армия семи наций



Пирамиду, которую вы видите на слайде, можно продолжить и вверх, и вниз. Она абсолютно чётко вас ограничивает. Снизу — операционная система, ещё ниже — какие-то базовые технологии. Сверху — аддоны над движками, готовый инструментарий, который поставляют с играми, вроде инструментов Neverwinter Nights. Что бы вы ни делали, вы, так или иначе, оказываетесь внутри этой штуки.

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

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

Назад, к основам



Если вся показанная на слайде круговая диаграмма — это игра, то движок — это её синий сектор. В идеальном мире, разрабатывая игру, вы можете вынуть этот синий сектор и поставить туда красный, или желтый, или зеленый. Можно одно большое «U» поменять на другое большое «U», или взять некое маленькое «x», и то, что вы выбрали, там тут же заработает. В реальности это не так, но мы должны к этому стремиться. Подобная картина, если речь идёт о реальных проектах, наблюдается в игровой индустрии постоянно. Бывает и так, что вся эта диаграмма занята движком, но это справедливо лишь для каких-то демонстрационных продуктов.

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

Мифический человеко-месяц



Предположим, вы решили создать собственный движок и сообщили об этом человеку, принимающему решения. В ответ вы услышите примерно то же самое, что можно видеть на слайде: «Делать ты это будешь долго, это очень рискованно, это нерационально, это не поможет нам достигнуть наших целей». Что можно на это ответить? Дело тут в том, что все эти возражения, кроме последнего, не выдерживают никакой критики.

Долгая разработка движка? Но если разработка с собственным движком пойдёт быстрее, то это — не аргумент. О том, что такое «рационально», пожалуй, вообще никто не знает. Поэтому все эти возражения очень субъективны.

Последний пункт, касающийся того, способствует ли свой движок достижению целей — это очень важно. Если ваша цель — заработать благодаря получению гранта от Unreal 4, то, наверное, свой движок делать не надо, потому что он не ведёт к этой цели. Если у вас есть цель, в рамках которой вам надо что-то делать на определенных технологиях, то не надо писать свой движок — надо брать то, что есть. Но эффективно использовать готовые движки можно далеко не всегда. Зачем же, всё таки, писать свой движок?

13 причин почему



Когда может понадобиться собственный движок? Разберём этот слайд по пунктам:
  • Time to Market — срок вывода продукта на рынок. Это — по-настоящему серьёзно. Половина из тех движков, которые сейчас используются в больших компаниях, разработаны именно потому что в тот момент, когда этой компании надо было быстро быстро занимать нишу, разработать своё было быстрее, чем взять что-то чужое и осваивать это.

Вот вам история. В одной компании на сайте было задание для программистов, выглядящее примерно так: «Ребята, если хотите у нас работать, напишите пожалуйста «Астероиды», которые должны запускаться на платформе Android без использования внешних библиотек. Мы считаем, что на это вам достаточно 8 часов. Время пошло». Потом время увеличили до 12 часов. Может быть, выглядит это смешно. Сначала я наблюдал за этим, снаружи, потом заглянул в эту компанию и попросил рассказать мне о том, что прислали в виде отклика на это задание. Как оказалось, отбор прошли больше двухсот программ, то есть — эти программы запускались и работали. Это значит, что если вы вдруг захотите издать «Астероиды» для Android, то найдется как минимум 200 человек, которые могут это сделать за 8 часов. Я не говорю, что это можно продавать. Но рассказал я эту историю к тому, что очень часто, собственно, движок — это и есть Time to Market. Скажем, просто потому, что у вас такие маленькие потребности, что вы дольше будете изучать документацию на тот же Unreal, чем просто взять и сделать своё.
  • Lord of the Platform — властелин платформы. Существуют платформы, для которых нет готовых инструментов. Например, STB, set-top box (ресивер цифрового телевидения) — коробочка для кабельного телевидения, которая стоит на столе у каждого третьего американца.

У Warner имеется 120 миллионов пользователей этой услуги. Если вы пишете туда софт, и игры в том числе, то вы имеете доллар с коробки. Это 120 миллионов долларов в год. При этом кроме вас писать для этой штуки не может больше никто. Потому что там нет DirectX, там вообще ничего нет. Если вы умеете писать программы для STB — то вы властелин платформы, вы имеете эти сто двадцать миллионов долларов в год. Чем не повод писать свой движок?

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

Можно говорить, что нас интересуют телефоны, но речь идёт о миллионах долларов. Почему бы не писать для других устройств? В результате, есть абсолютно четкие причины это делать.
Или, например, перед нами новейшие смарт-часы. SDK к ним ещё не вышел, никто не понимает, что с ними делать, а если вы сможете, своими силами, написать для них качественный продукт, то вы, скажем, заработаете по доллару с каждого такого устройства. Если их будет продано два миллиона — то вам достанется два миллиона долларов. Написать это несложно, но для этого надо делать свой движок, потому что чужих нет, а компании-производители таких устройств не будут делать общедоступные движки для разработки под них.

  • Weak but proud devices — малые, но гордые устройства. Если вы делаете игры для мобильных телефонов, собираете хоть какую-то статистику, то вы знаете, что с аппаратным обеспечением у устройств от Apple всё более-менее нормально, а вот с платформой Android — просто беда.

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

При этом в случае с iPhone можно делать хоть какие-то ставки на прогресс. Например, ожидается, что Unreal будет работать под iPhone 10, хотя пока всё это доведут до ума, будет уже какой-нибудь iPhone 12, 15 или 17. А вот в случае с миром вообще в краткосрочной перспективе на прогресс ставить тяжелее. Потому что апгрейд устройств происходит очень медленно.

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

  • Own language for own ideas — собственные языки для собственных идей. Когда вы сами делаете движок — вы можете задействовать эту концепцию. Дело в том, что основная проблема нашей индустрии в том, что домен геймдизайнера — это филология. Он мыслит на обычном языке. Он ничего другого не умеет. А программист мыслит в домене программирования и между ними пропасть. Как результат, некая итерация, которую приходится непрерывно повторять, стоит, например, два доллара. И вы постоянно тратите эти деньги.

Стандартные движки пытаются охватить всех. На самом деле мы видим, как они пытаются делать естественные доменные преобразования из языка в язык и из пространства в пространство. Но для всех. А у вас есть собственные идеи, и вы можете реализовывать их напрямую, делая собственный набор инструментов. Понятно, что всё это, в виде плагинов, можно делать и поверх существующих движков, но свой движок открывает совсем другие возможности.
  • Unique Mechanics = Unique engines — уникальные механики = уникальные движки. Мои знакомые писали Minecraft в пятнадцатом году с использованием Unity. Был ли смысл всё это делать — вопрос открытый. Но движок они выбрали и принялись за работу. Однако движок им, очевидно, очень мешал. Им было тяжело. У них были очень долгие итерации. После того, как мы их проконсультировали, они написали, буквально за три дня, собственный рендер. Причём весь остальной код, ответственный, скажем, за построение мира, естественно, никуда не делся. Просто всё это перестало быть в C#, перестало быть в Unity. И работа закипела. Не знаю, удалось ли им на этом заработать, но главный вывод из этой истории заключается в том, что им изначально не надо было использовать Unity.

То есть, существует большое количество механик, для которых стандартные, большие, универсальные движки не подходят просто потому, что они предназначены для всего. Поэтому если вы завтра придумаете что-то особенное, какие-то сложные воксельные механизмы, то работать со стандартными движками вам будет неудобно. То есть, существуют механики, для которых стандартные движки не подходят, и которые достаточно просто реализовать самостоятельно.
  • The game is not a render — the game is everything else — игра это не рендер, игра — это всё остальное. Мы об этом уже говорили. Если у вас проблема только в том, чтобы нарисовать что-то, или, скажем, использовать звук, делая многоплатформенную игру, то в рассмотренной ранее пирамиде можно было видеть подобные истории. Если вы говорите: «Я хочу проигрывать звук на трёх платформах», то вам для этого не нужна большая «U» — маленькой «c» будет вполне достаточно.

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

Преимущества разработки своего движка



Рассмотрим преимуществах разработки своего движка, опираясь на основные идеи, вынесенные на слайд:
  • Buying is often better than mortgage — покупка часто предпочтительнее ипотеки. Игровая разработка — это, так или иначе, деньги. Есть способы монетизации, при использовании которых покупка не просто лучше, чем ипотека, а это просто единственно возможный вариант.

Если кто-то работал на мобильных технологиях, то вы всё понимаете. Если на коробке движка написано: «10 процентов роялти», то это категорически неприемлемо, вы не заработаете столько. У вас может быть прибыль сто процентов, но вы работаете из 2-х. То есть, если у вас есть роялти, то это чисто экономическая причина отказа от движка. А надо сказать что три, точнее — два из самых популярных на сей момент движков — это как раз вопрос отчислений. То есть такой вариант сразу отпадает.
  • Specificity is better than universality in the long term — на длинной дистанции специализированные инструменты всегда лучше универсальных. Очевидно, что универсальность всегда медленнее, она менее производительна и менее специфична, чем специализированные вещи. Движок, написанный под конкретную задачу, справится с ней лучше и быстрее, чем универсальное средство. На длинной дистанции специальные инструменты гораздо выгоднее, чем универсальные.
  • Tools and pipelines are developed within — пайплайны и средства разработки, созданные внутри компании. Любой движок, придуманный людьми вне вашей компании, ориентируется на несколько вещей. Первая — это best practices. То есть ориентируется движок другой компании не на то, как ваш художник рисует, а на то, как рисуют художники, идеальные с их точки зрения. Вполне возможно, что ваши художники рисуют по-другому. У них свой пайплайн и у них это получается.

У вас есть 2 варианта действий: либо их переучивать так, как положено с точки зрения best practices, либо использовать своё. Есть простые примеры. Предположим, вы говорите: «Мы импортируем 3D-модели». Вы не знаете — что там с той стороны. Поэтому вам нужен промежуточный формат. Промежуточный формат пускай будет FBX. Огрехи FBX все, кто этим занимаются, знают. А вам некуда деваться, потому что вы не знаете, что там будет делаться, вы не будете писать плагины под 450 средств 3D-моделирования.

Когда вы работаете внутри своей компании, то вы можете реализовать тот самый пайплайн, который у вас уже существует и надеть на него сверху то, чем вы занимаетесь. На самом деле, это очень важно. Дело в том, что всё это имеет отношение ко времени разработки и, как следствие, к стоимости. Поэтому, когда вы разрабатываете у себя, вы можете утилизировать тот пайплайн, который уже есть. Иначе у вас документ, который называется «Правило выгрузки 3D-моделей и создания материалов для художников» будет больше, чем дизайн документ, а это неправильно.

  • Reaction time — время реакции. Речь идёт о времени реакции производителя движка на ваши обращения, о возможности оснащения движка новым функционалом, и об оперативном исследовании новых технологий.

Скажем, в соседнем офисе сидят люди, которые делают движок. Каждый, кто пытался исправить баг в универсальном движке, то есть написать в багтрекер Unity или Epic, знает, что лучше даже не начинать. А если разработчики сидят в соседнем офисе, то вы можете к ним обратиться и решить вопрос за 15 минут.

То же касается и предложения нового функционала, если у вас есть на это право. Исследование новых технологий так же упрощается при использовании собственных движков.
Предположим, ваш программист съездил на конференцию, послушал там лекцию о чём-то новом. Он тут же это попробовал, вы получили представление об этом новом и знаете, нужно оно вам или нет. Вы можете прямо-таки реактивно пробовать что-то интересное из мира науки. А это, кстати, значит, что в компании может быть человек, который будет называться «исследователь». При этом исследованиями можно заниматься и на том же Unreal, поскольку он поставляется в исходниках.

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

Разработка собственного движка — это не только преимущества. Это ещё и риски. Рассмотрим их.

Риски, связанные с разработкой своего движка



Рассмотрим риски сопровождающие разработку и использование собственных движков:
  • Development time — время разработки. Это понятие пересекается с тем, что мы говорили о времени выхода на рынок. Разработка движка может быть и очень долгой, и достаточно быстрой. Но время разработки движка, в любом случае, вносит вклад в общее время разработки проекта. Поэтому это тоже риск. Например, мне известны коллективы, у которых время разработки движка стремится к бесконечности.
  • Terminal cases of vendor-lock — терминальные случаи привязки к поставщику. Это относится не только к большим компаниям, но и к маленьким. Скажем, вы наняли Васю, он написал движок, потом влюбился, от вас уволился, и в том, что он написал, не понимает никто. В результате у вас vendor-lock похлеще Google. Потому что в Google еще можно письмо написать, хотя они и не ответят, а здесь с уходом программиста всё закончилось. В результате — потерянное время на разработку и другие неприятные последствия. Надо уметь избегать этих рисков.
  • Reinvent the wheel — изобретение колеса. Тут дело в том, что мы живём в мире, в котором вы всё равно изобретаете велосипеды. При разработке движка получается перенос велосипедного завода из кода игры в код движка, хотя там им и не место.
  • Closed ecosystem — закрытая экосистема. Всё, что делается внутри компании, принадлежит этой компании. Я знаю кучу компаний, у которых есть что-то вроде своего скриптового языка. Это может быть какой-нибудь XScript, который работает только в рамках их решения.

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

Главный вопрос жизни, вселенной и всего такого



Рассмотрим очень важный вопрос. Какой ресурс требуется в первую очередь для разработки собственного движка? Ресурс, без которого бессмысленно задумываться о том, надо или не надо делать собственный движок. Ответ на него, конечно, не 42. Вопрос в том — что вообще нужно, чтобы просто хотя бы иметь возможность сказать: «Да, у нас хоть что-то есть, мы можем начать что-то делать». Ответ на этот вопрос заключается в том, что для разработки собственного движка нужны программисты.

Программисты



Для того чтобы создать собственный движок, нужны программисты. Если не знаете — погуглите разницу между словами «developer» (разработчик) и «programmer» (программист). Это очень важно. Девелоперы — это основная группа. Игровая индустрия так устроена, что большинство людей в ней программистами назвать нельзя. Извините, но они разработчики. Разработчики не способны грамотно сделать движок. Опять же, если взглянуть на разницу между первыми и вторыми, то разработчики делают игры, а программисты делают инструменты. Разработчики делают продукт, компания зарабатывает за счёт продукта, но инструменты должны быть сделаны программистами, иначе они просто не будут работать.

С одной стороны, сейчас очень открытый мир. Я, например, знаю код Unreal 4 и CryEngine, он открыт. Все, кто хотят знать, могут узнать код Unity, есть огромное количество соответствующих материалов. Это значит, что этим способен заниматься тот, кто является программистом и читает по-английски. Там нет какого-то rocket science. Но с другой стороны, как выяснилось, программистов, которые читают по-английски, найти очень непросто. Поэтому вы должны знать, где они водятся, должны уметь их набирать, использовать, поощрять. Если вы это умеете, то можно уже и о своём движке задуматься. Если у вас таких людей нет, то у вас всё равно ничего не получится. Примеров тому — тьма. Дело не в том, что есть плохие и хорошие решения. Просто есть такие вещи, которые не могут работать изначально.

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

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

Техническая эпидемия



Сейчас уместно вспомнить ещё об одном аспекте поиска сотрудников, который касается, преимущественно, больших компаний. У таких компаний есть несколько подходов к подбору персонала.

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

Во-вторых, есть подход, который мы, в принципе, и исповедуем. Как работает кефир? Он всё вокруг превращает в себя. Берёте молоко, кидаете туда кефир — и нет молока, всё превратилось в кефир. У нас это выглядит так: «Ребята, давайте возьмём 5 сильных программистов, это будет внутренний технологический центр». И я всем говорю, что если вы можете себе позволить сделать RnD-отдел, если вы — большая компания — то сделайте. Пускай они даже ничего, с точки зрения денег, полезного не делают. Если компания укрепилась на рынке и перед ней возникает вопрос о том, куда расширяться, то ответом на этот вопрос может стать создание RnD-отдела. Когда компания уже богата, для неё это не потеря денег, потому что она столько денег теряет уже на том КПД, на котором сейчас работает наша игровая индустрия, что расходов на исследования просто не заметит.

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

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

Если компания уже настолько большая, что может себе позволить делать что-то интересное внутри себя, то это окупается даже с точки зрения денег. Поэтому если можете — то пробуйте. Выглядеть это может как угодно — скажем, делается своя ветка Unreal и там перерабатываем всё так, как хочется вам. Я, например, в одной из компаний делал браузер, который помещается в 2.5 мегабайт памяти. И он работал. Зачем — я не знаю, но это было бесконечно интересно.

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

Два мира



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

В игровой индустрии соседствуют два мира. Люди либо ориентированы на решение технологических задач, либо на нарратив. А посередине — кустарщина какая-то. То есть, инструментария практически нет. Слова, слова, слова — бах — код. Опять слова — и опять код. Мы считаем, что требуются инструменты, которые позволяют подключать к тому, что получится в результате работы над игрой, геймдизайнера, менеджера, других сотрудников, не являющихся программистами.

На слайде можно видеть behavior tree, дерево поведения. В принципе, это штука, просто взятая из википедии, но перед нами её оттуда взял Unreal. Ничего страшного в этом нет. Итак, документация к этому лежит на сайте Unreal, нам было несложно сделать подходящий интерфейс, совместимый с тем, что делается в Unreal. То есть, можно взять любой пример с анриловского сайта экшнов, пример самого поведения, так как формат вполне совпадает, вот так вот переписать, и оно заработает. Это значит, что я облегчил себе жизнь, я не пишу документацию. И таких штук здесь много.

В примере со слайда что-то происходит, бегает краб, кого-то ловит, в общем-то, это неважно. Внутри программист решает задачи, которые выглядят как «пойди в…», «стрельни в…», «посчитай расстояние» — и всё. Всё остальное поведение написано в этом редакторе человеком, который не имеет абсолютно никакого отношения к программированию. И это работает, в отличие от перевода текста в код. При этом если говорить, скажем, о балансе. Что такое баланс? Это — 15 коэффициентов, которые можно менять. А здесь описано поведение, а не коэффициенты.

То есть, например, поведение «патрулирование» описано геймдизайнером, а не программистом. Это значит, что мы сделали тот шаг, которого большая часть людей не делает. Они просто пишут в дизайн-документе: «патрулирует». А программист это может перевести 50 разными способами. Что такое вообще патрулирование? А здесь геймдизайнер пишет именно то, что он имеет в виду. И это победа, друзья мои. Именно для этого вам нужны собственные инструменты. Для того чтобы сглаживать переход от вербального определения вашего визионера, который видит игру, так сказать, внутри головы, до программистов. А иначе они перестанут быть программистами, станут девелоперами и всю жизнь будут рассаживать травку.

Итоги



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

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

В результате, если вы имеете поводы писать свой движок, и у вас есть ресурсы — берите и пишите.

Вопросы и ответы

Вопрос


Какова стоимость вашего движка, если оценивать её в деньгах и в трудозатратах?
→ Ответ

Сколько это стоило в деньгах, я, наверное, сейчас не могу сказать. А если говорить о трудозатратах, то это девять месяцев команды из шести человек. Но надо понимать, что это наша последняя разработка, и тут мы целимся достаточно высоко. Я не рассказываю о нашем наборе инструментов, о том, что мы делаем с Lua, или о том, что мы делаем с JavaScript такого, что не делает вообще никто. Мы планируем выпустить в опенсорс пару модулей, которые, если и не перевернут индустрию, то, как минимум, помогут людям осознать — что такое скриптовые языки. Наш предыдущий движок делали два человека четыре месяца. Он — тоже 3D, но попроще, телефонный. Можно быстрее, я уже рассказывал, как «Астероиды» пишут за 8 часов, но это, конечно, далеко от реальности.

Вопрос


Сколько времени потребовалось на реализацию дерева поведения?
Ответ

Это я могу точно сказать, делалось это совсем недавно. Два человека занимались этим месяц. Но проблема тут шире, чем создание дерева поведения. Дело в том, что экшны у нас пишутся в Lua, занимает это, наверное, дня четыре, а написать редактор на Qt — месяц.

Вопрос


Насколько я понимаю, вы используете Lua, у вас есть экшн-система, которую вы изначально написали, а дерево поведения просто дёргает эти экшны?
Ответ

Да, так и есть. На самом деле, мы работаем над тем, чтобы вывести это в опенсорс, пишем документацию, систему сборки, примеры.

Вопрос


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

У нас сейчас два движка, предыдущая редакция и новая. То есть это не рефакторинг. Это полностью новый движок. Если говорить о трудозатратах, то можно сказать, что компания у нас большая, работает около 500 человек, программистов около 250, 5 офисов. Проектная команда работает над играми. Проект — это некая игра, и ей занимается некоторое количество людей. Команда разработки движка — это отдельный коллектив. Это — тот самый кефир, о котором я говорил, элитные подразделения, там немножко другие деньги и подходы к формированию команд. Сейчас мы немного обгоняем разработку. Две новые игры запущены на новом движке. Это достаточно мучительно, так как разработчикам игры не очень комфортно, потому что они работают в ситуации, когда у них что-то может буквально из-под рук взять и взорваться. И у нас команда движка — это 6 человек. Команды продуктов — это, в среднем, человека по четыре программиста, они не пересекаются.

Вопрос


Под движком вы подразумеваете и инструменты тоже?
Ответ

Да, у нас есть отдельная команда по разработке инструментов. У нас был очень неудачный пример. Очень плохо разрабатываются GUI-инструменты. Потому что любой нормальный программист считает, что это очень просто. Мы попытались это аутсорсить. Потому что понятно — ты отдаёшь полный интерфейс, у тебя всё есть, ты говоришь: «Создай окно, нарисуй кнопки — и всё». Но эта затея провалилась, поэтому сами делаем, мучительно работаем с Qt, потому что нам важно, чтобы это работало на всех трёх настольных платформах. Поэтому сами делаем. И у нас 6 человек делают и то, и другое, и третье. Но мы всё равно находимся чуть впереди запросов продукта.

Вопрос


Реально ли сейчас продать свой движок?
Ответ

Нет. Сейчас нельзя продать свой движок. Можно продать экосистему. То есть, работать по схеме «дай мне денег, а я тебе дам движок» нельзя. Обратите внимание на то, сколько у нас компаний имеет собственный движок, и сколько из них движки продаёт. На самом деле — никто из них движки не продаёт. Для начала — это достаточно большая головная боль с точки зрения того, что это надо превратить в продукт. То, что у вас работает внутри компании, никак нельзя продать. Вы должны, как минимум, написать документацию, которую поймут окружающие. Вы должны нанять просто какую-то армию волонтёров, которые будут это дело евангелизировать. И не вполне понятно, что вы с этого получите. А если сделать мобильную игру на этом движке, то вполне можно проснуться миллионером. Поэтому, чтобы такие вещи делать, надо быть фанатом, надо быть уверенным в том, что делаете. Я рассказывал о причинах, которые могут привести к разработке своего движка, а у вас тут — ещё одна причина. Скажем, вы думаете, что сделаете движок лучше, чем Unreal. Если это так — идите на рынок. Но я не думаю, что сделаю лучше, чем Unreal.

Вопрос


Я так понимаю, что ваш новый движок — это С++ и Lua?
Ответ

Да, C++, Lua и ещё JavaScript.

Вопрос


А почему C++? Были ли варианты, или вы чётко знали, что именно возьмёте?
Ответ

Смотрите, существует такая мода. Каждый второй, кого встречаешь, говорит тебе: «Golang», или говорит тебе: «Rust». И если бы это было сейчас, я бы в принципе задумался. Но когда ты приходишь в компанию руководителем процесса разработки движка, а было это год назад, тебе надо строить какие-то планы, а так придётся включать в эти планы пункт «Почитать о Go». Тут ведь важна производительность, а на C++ мы достаточно давно работаем, умеем им пользоваться.

А почему мы используем Lua? Потому что это недооценённый язык, он отлично подходит для встраивания. Почему JavaScript? Потому что так получилось. Потому что на рынке кроме V8 и Webkit ничего нет. А это монстроиды. Как я уже говорил, мы делали браузер, который занимает 2.5 мегабайт памяти, там есть JavaScript-движок, который проходит все тесты. У нас это есть, и вот поэтому — JavaScript. В результате, например, можно брать тех, кто знает JS и писал сайты на React.

Вопрос


Скажите, вы дерево поведения используете только для управления поведением, или применяете его ещё для управления игровыми механиками и для продвижения игрового прогресса?
Ответ

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

Вопрос


Насколько трудно прогнозировать развитие технологий на будущее? То есть, как сложно предвидеть появление каких-то подводных камней и тому подобных вещей?
Ответ

На данный момент я вижу одну проблему. Сейчас интересно выглядит технология WebAssembly. Flash умер. Мы хотим, естественно, издаваться ещё где-то на вебе. Портировать игру, скажем, с Unity на WebGL — это задача, которая не решается нажатием на одну кнопку. То есть сейчас мы смотрим на WebAssembly и пока неясно — будет ли это стандартом, или нет, начинать сейчас с этим работать, или подождать. В мобильных технологиях тоже ничего особенного не происходит. Нет пока каких-то сингулярных взрывов, но если будут — будем готовиться.

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

habr.com

Уроки, полученные при создании первой игры, и почему я хочу написать свой движок / Habr


Недавно я выпустил свою первую игру BYTEPATH и мне показалось, что будет полезно записать свои мысли о том, чему я научился в процессе её создания. Я разделю эти уроки на «мягкие» и «жёсткие»: под мягкими я подразумеваю идеи, связанные с разработкой ПО, жёсткие — это более технические аспекты программирования. Кроме того, я расскажу о том, почему хочу написать собственный движок.
Сообщу ради контекста, что начал делать собственные игры примерно 5-6 лет назад и у меня есть 3 «серьёзных» проекта, над которыми я работал до выпуска первой игры. Два из эти проектов мертвы и полностью провалились, а последний я временно приостановил, чтобы поработать над BYTEPATH.Вот gif-анимации из этих проектов
Первые два проекта провалились по разным причинам, но с точки зрения программирования они провалились (по крайней мере, как мне это видится) потому, что я слишком часто пытался быть слишком умным и заранее обобщал слишком многое. Большинство мягких уроков связано с этим провалом, поэтому важно было сказать об этом.

Преждевременное обобщение


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

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

Рассмотрим пример сущности, которая выполняет действия ABC. Поначалу мы кодируем ABC непосредственно в сущности, потому что нет никаких причин делать иначе. Но когда дело доходит до другой сущности, выполняющей ABD, мы анализируем всё и думаем «давай-ка возьмём AB из этих двух сущностней, и тогда каждая из них будет обрабатывать самостоятельно только C и D», что кажется вполне логичным, потому что мы абстрагируем AB и сможем повторно использовать их в других местах. Если новые сущности используют AB таким же образом, каким они определены, то это не проблема. Допустим, у нас есть ABE, ABF и так далее…


Но рано или поздно (и обычно это происходит раньше) появляется сущность, которой требуется AB*, почти похожее на AB, но с маленькой и несовместимой разницей. Тогда мы можем или изменить AB с учётом AB*, или создать совершенно новую часть, в которой будет содержаться поведение AB*. Если мы повторим такое упражнение несколько раз, то в первом случае мы придём к очень сложному AB со всевозможными переключателями и флагами для различных поведений, а во втором случае мы вернёмся на первую клетку поля, потому что все немного отличающиеся версии AB всё равно будут содержать кучу повторяющегося кода.
В основе этой проблемы лежит тот факт, что каждый раз, когда мы добавляем что-то новое или изменяем поведение чего-то старого, мы должны делать это с учётом существующих структур. Чтобы изменить что-то, мы должны всегда задумываться «это будет находиться в AB или в AB*?», и этот кажущийся простым вопрос является источником всех проблем. Так происходит потому, что мы пытаемся вставить что-то в существующую структуру, а не просто добавить новое и заставить это работать. Невозможно переоценить разницу в том, чтобы просто делать то, что нужно и тем, что приходится учитывать имеющийся код.

Поэтому я осознал, что поначалу гораздо проще по умолчанию выбирать копипастинг кода. В показанном выше примере у нас есть ABC, и чтобы добавить ABD мы просто скопипастим ABC и удалим часть C, заменив её на D. То же относится и к ABE с ABF, и когда нам нужно добавить AB*, мы просто снова копипастим AB и заменяем его на AB*. Когда мы добавляем в эту схему что-то новое, нам достаточно просто скопировать код оттуда, где уже выполняется похожее действие, и изменить его, не беспокоясь о том, как оно встроится в уже имеющийся код. Оказалось, что такой способ гораздо лучше в реализации и ведёт к меньшему количеству проблем, хотя и выглядит контринтуитивным.


Большинство советов не подходит разработчикам-одиночкам


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

Например, я могу использовать глобальные значения, потому что очень часто они полезны, и пока я могу удерживать их в голове, они не представляют проблемы (подробнее об этом можно прочитать в части 10 туториала по BYTEPATH). Также я могу не слишком сильно комментировать свой код, потому что бОльшую его часть я держу в голове, ведь кодовая база не очень велика. Я могу создавать скрипты, которые работают только на моей машине, потому что никому не понадобится выполнять сборку игры, то есть сложность этого шага можно сильно снизить и для выполнения работы мне не понадобятся специальные инструменты. У меня могут быть огромные функции и классы, и поскольку я создаю их с нуля и точно знаю, как они работают, их огромный объём не представляет проблемы. И всё это я могу делать потому, что, как оказывается, большинство связанных с ними проблем проявляется только в коллективах, работающих над ПО с длительным сроком жизни.

Из работы над этим проектом я научился тому, что не произошло ничего особо плохого, когда я делал все эти «плохие» вещи. Где-то на границе сознания я всегда помнил, что для создания инди-игр мне не нужен суперкачественный код, учитывая тот факт, что многие разработчики создали отличные игры, используя очень плохие практики написания кода:


casenpai: Меня приводит в ужас то, что у тебя, похоже, в коде 864 конструкций case.

Тоби Фокс (автор Undertale): Я не умею программировать, лол.

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

ECS


Паттерн Entity Component System — хороший реальный пример противоречия всему, сказанному в предыдущих двух разделах. После прочтения большинства статей становится понятно, что инди-разработчики считают наследование плохой практикой, и что мы можем использовать компоненты, создавая сущности как из конструктора Lego, и что благодаря им мы можем гораздо проще использовать многократно используемое поведение, а буквально всё в создании игры становится легче.

По своему определению стремление программистов к ECS говорит о преждевременном обобщении, потому что если мы рассматриваем вещи как кирпичики Lego и думаем о том, как из них можно собирать новые вещи, то мы думаем в рамках многократно применяемых фрагментов, которые можно объединить каким-то полезным образом. И по причинам, перечисленным мной в разделе о предварительном обобщении, я считаю, что это СОВЕРШЕННО НЕВЕРНО! Хорошо объясняет мою позицию этот точный научный график:


Как вы видите, защищаемый мной принцип «yolo-кодинга» сначала намного проще и постепенно становится сложнее: сложность проекта увеличивается и yolo-техники начинают демонстрировать свои проблемы. С другой стороны, ECS поначалу гораздо сложнее — вам приходится создавать компоненты, а это по определению более трудно, чем создавать просто работающие элементы. Но со временем полезность ECS становится всё более и более очевидной и в какой-то момент он побеждает yolo-кодинг. Моя точка зрения заключается в том, что в контексте БОЛЬШИНСТВА инди-игр момент, в который ECS становится лучшим вложением средств, никогда не наступает.

К слову о несоответствии контекста: если эта статья наберёт популярность, то в комментариях обязательно появится какой-нибудь AAA-разработчик и скажет что-то вроде «Я уже 20 лет работаю в этой индустрии, и этот несмышлёныш несёт полную ЧУШЬ!!! ECS очень полезен, я выпустил уже несколько AAA-игр, заработавших миллионы долларов, в которые играли миллиарды людей по всему миру!!! Прекратите нести этот бред!!!»

И хотя этот AAA-разработчик будет прав в том, что ECS полезен для него, это не всегда верно для других инди-разработчиков: из-за несовпадения контекстов эти две группы решают очень разные задачи.

Как бы то ни было, мне кажется, я, как мог, донёс свою точку зрения. Значит ли это, что использующие ECS глупы или тупы? Нет. Я думаю, что если вы уже привыкли к использованию ECS и он для вас работает, то можете без размышлений использовать его. Но я считаю, что инди-разработчики в целом должны более критически относиться к таким решениям и их недостаткам. Думаю, здесь очень подходит мысль Джонатана Блоу (я ни в коем случае не считаю, что он согласился бы со мной относительно ECS):


Избегайте разделения поведения на несколько объектов


Один из паттернов, которого, похоже, мне не удалось избежать — это разбиение одного поведения на несколько объектов. В BYTEPATH это в основном проявилось тем, как я создавал комнату «Console», но в Frogfaller (игре, которую я делал ранее) это более очевидно:


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

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

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


Их контекст заключается в том, что я писал свою игру на Lua и с помощью LÖVE. Я написал 0 строк кода на C и C++, всё писалось на Lua. Поэтому многие из этих уроков связаны с самим Lua, хотя большинство из них применимы и к другим языкам.

nil


90% багов, получаемых от игроков, связаны с доступом к переменным nil. Я не отслеживал статистику того, какие типы доступа более/менее часты, но чаще всего они связаны со смертью объекта, когда другой объект хранит ссылку на этот умерший объект и пытается с ним что-нибудь сделать. Думаю, это относится к категории проблем «срока жизни».

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

if self.other_object then
    doThing(self.other_object)
end

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

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

local other_object = getObjectByID(self.other_id)
if other_object then
    doThing(other_object)
end

Преимущество такого подхода в том, что он заставляет меня получать объект каждый раз, когда я хочу с ним что-нибудь сделать. Кроме того, я никогда не сделаю ничего подобного:
self.other_object = getObjectByID(self.other_id)

Это значит, что я никогда не храню постоянную ссылку на другой объект в текущем, то есть не может произойти ошибок из-за смерти другого объекта. Мне это не кажется очень желательным решением, потому что каждый раз, когда я хочу что-то сделать, оно добавляет много излишнего. Языки наподобие MoonScript немного помогают в этом, потому что там можно сделать нечто подобное:
if object = getObjectByID(self.other_id) 
    doThing(object)

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

Больший контроль над размещением памяти


Хотя я и не буду утверждать, что сборка мусора плоха, особенно с учётом того, что я собираюсь использовать Lua для моих следующих игр, мне всё равно очень не нравятся некоторые её аспекты. В похожих на C языках возникновение утечки раздражает, но в них мы обычно можем приблизительно понять, где она происходит. Однако в языках наподобие Lua сборщик мусора похож на «чёрный ящик». Можно заглянуть в него, чтобы получить намёки о происходящем, но это неидеальный способ работы. Когда у вас происходит утечка в Lua, то она оказывается гораздо большей проблемой, чем в C. Это дополняется тем, что я использую кодовую базу C++, которой не владею, а именно кодовую базу LÖVE. Я не знаю, как разработчики настроили размещение памяти со своей стороны, поэтому со стороны Lua мне гораздо сложнее добиться предсказуемого поведения памяти.

Стоит заметить, что с точки зрения скорости проблем со сборщиком мусора Lua у меня нет. Можно управлять им так, чтобы он работал с определёнными ограничениями (например, чтобы не запускался в течение n мс), поэтому в этом проблем нет. Проблема только в том, что можно сказать ему не выполняться более n мс, а он не сможет собрать весь мусор, который вы сгенерировали за кадр. Поэтому желателен максимальный контроль над количеством размещённой памяти. Есть очень хорошая статья по этой теме: http://bitsquid.blogspot.com.br/2011/08/fixing-memory-issues-in-lua.html, и я расскажу о ней подробнее, когда доберусь в этой статье до движка.

Таймеры, ввод и камера


Это три области, в которых я очень доволен получившимися у меня решениями. Для этих общих задач я написал три библиотеки:
У всех них есть API, которые кажутся мне очень интуитивно понятными и очень облегчают мою жизнь. Пока самым полезным для меня оказывалась Timer, потому что она позволяет мне реализовывать всевозможные решения простым образом:
timer:after(2, function() self.dead = true end)

Этот код убивает текущий объект (self) через 2 секунды. Также эта библиотека позволяет очень удобно реализовывать переходы tween:
timer:tween(2, self, {alpha = 0}, 'in-out-cubic', function() self.dead = true end)

Эта строка позволяет плавно изменять (tween) атрибут alpha объекта до 0 в течение 2 секунд с помощью режима tween in-out-cubic, а затем уничтожать объект. Это позволяет создать эффект постепенного растворения и исчезания. Также его можно использовать, чтобы заставить объекты мерцать при ударе:
timer:every(0.05, function() self.visible = not self.visible, 10)

Этот код 10 раз каждые 0,05 секунды переключает значение self.visible между true и false. Это значит, что он создаёт эффект мерцания на 0,5 секунды. Как вы видите, библиотеку можно использовать практически безгранично. Это стало возможным благодрая тому, как Lua работает со своими анонимными функциями.

Другие библиотеки имеют столь же тривиальный API, являющийся мощным и полезным. Библиотека камеры — единственная, которая оказалась слишком низкоуровневой, но это можно улучшить в будущем. Смысл её заключается в том, чтобы иметь возможность реализовать нечто похожее на то, что показано в этом видео:


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

Комнаты и области


Для меня очень подходящим способом работы с объектами оказалась концепция комнат (Room) и областей (Area). Комнаты — это аналог «уровня» или «сцены». В них происходит всё действие, их может быть множество и вы можете переключаться между ними. Область — это тип менеджера объектов, который может находиться внутри комнат. Некоторые называют такие объекты Area «пространствами» (spaces). Area и Room работают вместе примерно таким образом (в реальной версии этих классов будет намного больше функций, например, у Area будут addGameObject, queryGameObjectsInCircle, и т.д.):
Area = Class()

function Area:new()
    self.game_objects = {}
end

function Area:update(dt)
    -- update all game objects
end
Room = Class()

function Room:new()
    self.area = Area()
end

function Room:update(dt)
    self.area:update(dt)
end

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

Однако одно из преимуществ такого подхода заключается в том, что легко смешать локальную логику управления объектами с логикой управления объектами Area, если в комнате есть они обе. Это очень легко может запутать и при разработке BYTEPATH это стало серьёзным источником ошибок. Поэтому в будущем я постараюсь сделать так, чтобы в Room использовались строго или Area, или её собственная процедура управления объектами, но никогда обе одновременно.

snake_case и camelCase


Сейчас я использую snake_case для имён переменных и camelCase для названий функций. В будущем я собираюсь использовать snake_case везде, кроме имён классов/модулей, которые по-прежнему останутся CamelCase. Причина этого очень проста: в camelCase очень сложно читать длинные названия функций. Возможность перепутать имена переменных и функций в snake_case обычно не является проблемой благодаря контексту использования имени, поэтому всё будет в порядке.
Основной причиной того, что я хочу написать после завершения этой игры собственный движок, является контроль. LÖVE — это отличный фреймворк, но когда дело доходит до выпуска игры, он становится слишком грубоват. Такие вещи, как поддержка Steamworks, поддержка HTTPS, тестирование других физических движков наподобие Chipmunk, использование библиотек C/C++, упаковка своей игры для распространения на Linux и куча других вещей, которые я вскоре упомяну, оказываются слишком сложными.

Это не значит, что задача нерешаема, но для её решения мне пришлось бы спуститься до уровня C/C++ и работать там. Я программирую на C, поэтому с этим у меня нет никаких проблем, но изначально я решил использовать фреймворк потому, что хотел пользоваться Lua и не беспокоиться ни о чём другом, а такая работа противоречит моим стремлениям. Поэтому если мне в любом случае придётся работать на низком уровне, то я лучше буду владеть этой частью кодовой базы, написав её самостоятельно.

Однако здесь я хочу изложить более общую точку зрения на движки, и для этого мне придётся вместо LÖVE начать ругать Unity. Есть игра, которая мне нравится и в которую я довольно долго играл — Throne of Lies:


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

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

В число багов входят постоянные тормоза, проблемы с Async, добавленная и заброшенная поддержка Vulkan, они полностью сломали FB standalone для всей версии и притворились, что ничего не было, перейдя к новым фичам (нам пришлось убрать логин через FB и т.п.), глитчи UI наподобие искажённого текста, баг с окрашиванием всего в розовый цвет, баг с чёрным экраном, баг с исчезающим текстом, Scroll Rect’ы содержат больше багов, чем я могу перечислить (даже улучшающие их плагины терпят неудачу, потому что ставятся поверх Unity).

Например, скроллеры… В случайные моменты времени они могут начать смещаться влево, хотя ты ничего не делал. Если изменить их положение, то иногда элемент UI сворачивается и становится отрицательным. Приходится нажимать на Play, а потом Stop, чтобы увидеть это снова, если повезёт, или перезагружать систему.

Ещё баги… У их фичи совместной работы (collab) были потрясающие разработчики, но снова та же беда — высшее руководство, принимающее плохие решения, выпустило её в почти в состоянии альфы, просто чтобы заработать денег. Потом и эту фичу забросили. Мы отказались от collab в пользу gitlab CE и половина наших проблем просто исчезла. Всё настолько дико. Один из самых крупных багов — через каждые 2-3 запуска кнопка запуска блокировалась (патч так и не вышел, отчёт отправлен в ПРОШЛОМ ГОДУ), и запуск Unity блокирует все потоки на две минуты. Комбо-баг… Блокируется кнопка Play, а перезагрузка затормаживает всё на 2 секунды. Через каждые 2-3 запуска игры. Теперь представьте работу в таком режиме в течение 10 часов.

Ещё баги… Unity крашится, если вы выполняете выход, когда предварительно загружается новая сцена — выглядит непрофессионально. Всё, что делаешь с фичами буфера обмена, блокирует весь буфер обмена ЗА ПРЕДЕЛАМИ Unity, пока не перезагрузишь компьютер.

Ещё нарушенные обещания… UNET? Это тоже отдельная история. Они заявляли, что это будет фича корпоративного уровня. В результате оказалась только для двух людей, со сломанной архитектурой, по-прежнему не работает уже в течение полутора лет, нет ни документации, ни туториала, ни поддержки. Похоже, её полностью отдали на аутсорс, потому что никто ничего не знает. Мы снова купились на маркетинг и потеряли три месяца, а потом перешли на Photon и то, что заняло у меня три месяца, я сделал за три дня. Даже модераторы на их собственном форуме говорят, что она совершенно не работает. Я делал уморительные скриншоты. Приходилось смеяться, чтобы не заплакать… Сколько времени было потрачено… Так много нарушенных обещаний. Снова тот же шаблон: реализуем, выпускаем в состоянии альфы, забываем навечно.

И такого было очень много. Вот, что я могу сказать: если делаете 3D, то переходите на Unreal. Я даже не могу начать описывать своё разочарование. Раньше я был гордым Unity-разработчиком, пока не увидел за маской ужасную правду. Нам было так стыдно, что мы даже убрали со своего веб-сайта логотип Unity. Так много вложено, а я даже не могу рекомендовать Unity другим разработчикам.


То есть этот человек, создавший очень понравившуюся мне игру, рассказывал ужасные вещи о Unity, о том, что он очень нестабилен, что разработчики постоянно стремятся к новым функциям и никогда не реализуют их правильно, и так далее. Меня очень удивило, что кому-то настолько не нравится Unity, что он пишет такое. Поэтому я решил немного подтолкнуть его, чтобы узнать, что ещё он может сказать о Unity:
А потом он сказал такое:
И такое:
Я никогда не пользовался Unity, поэтому не знаю, правду ли он говорит, но он написал на нём готовую игру и я не вижу причин, по которым он мог бы лгать. Его точка зрения во всех этих постах примерно одинакова: Unity сосредоточен на добавлении новых функций вместо усовершенствования имеющихся и у Unity есть проблемы с поддержанием стабильности множества имеющихся функций между версиями.

По моему мнению, одним из самых убедительных аргументов в его постах является то, что применимо и другим движкам, а не только к Unity: разработчики движка сами не делают на нём игры. По крайней мере, с LÖVE я заметил одну важную вещь — многие проблем фреймворка могли бы быть решены, если бы разработчики активно делали на нём инди-игры. Потому что в таком случае все эти проблемы стали бы для них очевидными, получили бы высочайший приоритет и быстро были исправлены. xblade724 выяснил, что то же самое справедливо и для Unity. А многие другие знакомые мне люди обнаружили подобное и для других движков.

Есть очень малое количество фреймворков/движков, на которых сами разработчики активно делают игры. Первые, которые приходят мне в голову: Unreal, потому что Epic создала кучу суперуспешных игр на собственном движке, последняя из них Fortnite; Monogame, потому что основные разработчики портируют с его помощью игры на разные платформы; и GameMaker, потому что YoYo Games делает мобильные игры на своём движке.

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

И всё это означает, что если я заинтересован в создании игр надёжным и проверенным способом, не сталкиваясь с кучей неожиданных проблем ближе к завершению игры, то я не будут использовать движок, который усложнит мою жизнь, поэтому я не буду использовать никакой другой движок, кроме перечисленных выше трёх. В моём конкретном случае Unreal не подходит, потому что меня в основном интересуют 2D-игры, а Unreal для них — это перебор, Monogame не работает, потому что я ненавижу C#, а GameMaker не работает, потому что мне не нравится идея визуального кодинга или кодинга на основе интерфейса. То есть у меня остаётся единственный вариант — создать свой собственный движок.

Итак, разобравшись со всеми этими рассуждениями, давайте перейдём к конкретным задачам:

Взаимодействие C/Lua и память


Привязка C/Lua может осуществляться двумя фундаментальными способами (по крайней мере, исходя из моего ограниченного опыта): с полными пользовательскими данными и с ограниченными пользовательскими данными. При использовании полных пользовательских данных когда код на Lua запрашивает размещение чего-то в C, например, физического объекта, мы создаём ссылку на этот объект в Lua и используем её. Таким образом мы можем создать полный объект с метатаблицами и всевозможными параметрами, надёжно описывающими объект C. Одна из проблем такого подхода заключается в том, что это создаёт кучу мусора со стороны Lua, а как я упоминал в предыдущих разделах, я стремлюсь как можно сильнее избегать размещения памяти, или, по крайней мере, хочу иметь полный контроль над ним, когда оно происходит.

Поэтому логичнее мне будет использовать подход с ограниченными пользовательскими данными. Ограниченные пользовательские данные — это просто обычный указатель C. Это означает, что мы не можем получить много информации об объекте, на который указываем, но этот вариант обеспечивает наибольший контроль со стороны Lua. В этой схеме создание и уничтожение объектов должно выполняться вручную и всё не собирается волшебным образом, и именно это мне и нужно. Этой теме посвящён очень интересный доклад разработчика Stingray Engine:


Прочитав документацию, можно увидеть, как описываемое им происходит в движке.

Смысл написания своего собственного движка в том, что у меня будет полный контроль над тем, как происходит привязка C/Lua и над выбором компромиссов, которые при этом должны возникать. Если я использую чей-то чужой движок на Lua, то выбор сделан за меня и этим выбором я могу и не быть полностью довольным, например, как это случилось с LÖVE. Поэтому это основной способ, которым я могу получить больше контроля над памятью и создавать быстрые и надёжные игры.

Внешняя интеграция


Такие вещи, как Steamworks, Twitch, Discord и другие сайты, имеют собственные API, которые нужно интегрировать, чтобы пользоваться их удобными возможностями, а если не владеть кодовой базой на C/C++, то это задача будет намного сложнее. Разумеется, можно выполнить работу для интегрирования их в LÖVE, например, но при этом потребуется больше труда, чем при интеграции в собственный движок.

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

И снова владение частью кодовой базы на C/C++ делает такие интеграции гораздо проще, потому что вы можете просто реализовать только нужное вам и это точно будет работать.

Другие платформы


Это одно из преимуществ, которые я вижу в движках наподобие Unity или Unreal по сравнению с написанием собственного движка: они поддерживают все наиболее распространённые платформы. Я не знаю, хорошо ли реализована, но меня впечатляет то, что они на это способны, и в одиночку сделать это будет сложно. Хотя я и не супернерд, живущий и дышащий ассемблером, я не думаю, что у меня возникнет куча проблем с портированием моего будущего движка на консоли или другие платформы, но не могу рекомендовать каждому идти таким путём, потому что скорее всего это приведёт к куче трудозатрат.


Одна из платформ, которую я очень хочу поддерживать с самого начала — это веб. Однажды я играл в браузере в игру Bloons TD5, и спустя какое-то время игра предложила мне перейти в Steam и купить её за 10 долларов. Так я и поступил. Поэтому я считаю, что поддержка браузерной версии игры с меньшим количеством функций и предложение перейти в Steam — это хорошая стратегия, которую я тоже хочу реализовать. Предварительно я изучил вопрос о том, что нужно для создания движка на C, и, похоже, удобным для работы SDL будет Emscripten, с помощью которого я смогу рисовать на экране в браузере.

Реплеи, трейлеры


Создание трейлера для этой игры оказалось очень плохим опытом. Мне он совсем не понравился. Я хорошо могу продумывать в голове фильмы/трейлеры/истории (по какой-то причине я постоянно это делаю, когда слушаю музыку), поэтому у меня была очень хорошая идея для трейлера, который я хотел создать для игры. Но результат получился совершенно не тем, потому что я не знал, как использовать необходимые инструменты (например, редактор видео) и не имел особого контроля над записью.


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

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

И причина, по которой мне нужен свой движок для создания системы реплеев, заключается в том, что мне совершенно точно нужно работать на уровне байтов, чтобы реплеи работали управляемым образом и занимали меньше места. Я уже собирал систему реплеев в на Lua в этой статье, но всего 10 секунд реплея создают файл объёмом 10 МБ. В неё можно внести и дополнительные оптимизации, но в конце концов Lua имеет свои пределы, и гораздо удобнее оптимизировать подобные вещи на C.

Целостность конструкции


И последняя причина, по которой я хочу создать собственный движок — это целостность конструкции. Один из принципов, которые я люблю/ненавижу в LÖVE, Lua (то же самое относится и к философии Linux) — это их децентрализованность. В Lua и LÖVE нет стандартных способов реализации, люди делают то, что им кажется правильным, и если вы хотите написать библиотеку, которой будут пользоваться другие люди, то не стоит делать слишком много допущений. Этой идее следовали все библиотеки, созданные мной для LÖVE (их можно найти в моём репозитории), потому что в противном случае ими бы никто не пользовался.

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

Поэтому одна из вещей, которую я очень хочу сделать в своём движке — это способность централизовать всё точно так, как я хочу и возможность делать множество допущений, что увеличит темп работы (а также, я надеюсь, и повысит продуктивность)!

habr.com

Как создать игровой движок 🚩 движок игры 🚩 Компьютерные игры

Автор КакПросто!

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

Статьи по теме:

Инструкция

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

Используйте язык программирования Delphi, особенно если вы еще делаете первые шаги в данной области. Delphi – это среда разработки Object Pascal, отличающегося простотой и достаточной гибкостью, для возможности создания на нем полноценной компьютерной игры современного уровня, причем любого жанра, с полноценной трехмерностью и современным уровнем графики. Собственно говоря, среда разработки – дело вкуса каждого конкретного программиста. Например, MSVC++ , в отличие от Delphi , генерирует более быстрый код, но скорость компиляции Delphi в десятки, а может даже в сотни раз быстрее. Плюс удобный интерфейс и точное указывание строки кода, содержащей ошибку.

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

Полезный совет

В процессе разработки можно экономить время за счет повторного использования одного какого-то игрового движка для создания множества разных игр.

Совет полезен?

Статьи по теме:

www.kakprosto.ru

Зачем в 2017 году писать свой движок для мобильных игр? / Habr

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

Игровой движок для разработчика — это инструмент. Как молоток для плотника или гоночный автомобиль для пилота, инструмент влияет на успех. Конечно, не всегда. Бывает что на слабых технологиях делают шедевры, а на hight-end движках делают откровенно провальные проекты. Но «в среднем по больнице» выбор инструмента довольно значительно влияет на успех. Чтобы понять насколько, нужно понять зачем и как делаются игры.

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

Прибыль = Доход — Расход

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


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

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

Оплата труда = (Стоимость специалиста 1 + Стоимость специалиста 2 + …)*Время разработки

Выглядит просто. Исходя из формулы видно что есть фактически две величины, на которые можно повлиять: стоимость специалистов и время разработки. Уменьшаем стоимость специалиста, уменьшаем время разработки — и наш проект подешевел! Но как сделать специалиста дешевле? Как заставить его работать быстрее? Ответ — правильный выбор технологии и правильная постановка работы.

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

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

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

Далее, время разработки. Предлагаю разложить его на две составляющие: время произведения операции и количество повторений. Рассмотрим на примере интерфейсов. Если у вас есть специальный редактор интерфейса, то верстка в нем будет гораздо быстрее, чем в текстовом файле или в коде. Соответственно время на операцию верстки будет меньше. Если есть возможность работать с шаблонами, то количество однотипных операций будет меньше. Проще говоря, чем проще, удобнее и продуманнее инструмент, тем быстрее будет проходить разработка. И это уже следующая метрика — удобство инструментария или usiability, как это чаще называют. Здесь многие могут заметить, что во многих инди-командах нет специализированных людей и все равно все делает программист. Которому удобнее писать код. Тема довольно спорная, но я видел кучу примеров, когда те же программисты делают контент гораздо лучше и быстрее в специальных редакторах, чем с помощью кода.

Естественно, даже если снять максимум лишних задач с программиста, ему еще нужно писать код. По опыту разработки игр могу сказать, что 80% всего времени — это скучная рутина, которую нужно делать быстро и особо не заботясь о производительности. Диалоги, клиент-серверные взаимодействия, сценарии и все в таком духе. Здесь можно выделить еще одну метрику — сложность разработки игровой логики. Она описывает насколько сложна будет разработка большей части кода, те самые 80% рутины. Обычно для этого лучше всего подходят скрипты. А остальные 20% — это как раз те узкие места, в которых необходима гибкая оптимизация и настройка. Что, собственно, тоже относится к метрике гибкости.

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

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


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

Доход = Средний доход с пользователя*Количество пользователей

Снова две величины, на которые можно повлиять. Итак, как влияет движок на средний доход с пользователя? Здесь довольно косвенная связь, и зачастую ее вообще нет. Но мы ведь рассматриваем «среднее по больнице», поэтому отбрасывать не станем.

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

Одним из факторов, влияющих на успех и доходность игры, является ее качество. Красивый арт, плавные анимации, хорошие эффекты, интересный геймплей — все это влияет на мнение игрока, на его желание остаться в игре. А на это уже непосредственно влияет движок. Мы уже рассмотрели, что инструментарий необходим для эффективной разработки, но так же необходим для разработки качественного контента. Красивые анимации, интерфейсы, эффекты, сцены: все это сложно сделать без хороших инструментов. Плохой инструмент — это как велосипед с овальными колесами, на котором уехать куда-то довольно сложно. Однако, довольно часто разработчики ездят на таких велосипедах: программист лепит несуразные анимации на своих любимых синусах и косинусах, дизайнер верстает уровень в текстовом файле и так далее. Все это не приводит к качественному результату. Поэтому инструментарий важен.

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

Так же производительность влияет на удержание игрока. В среднем новый игрок устанавливает игру и принимает решение об ее удалении в течение 20-30 секунд. Заставлять его ждать загрузку игры в это время не самое хорошее решение. Так же размер игры влияет на порог вхождения. Мало кто захочет скачать игру размером 200МБ на мобильном интернете. Энергозатраты в игре так же влияют. Многие игроки запускают игру на телефоне в дороге по пути домой, и если уровень заряда батареи низок, а ваша игра сильно ее тратит, игрок скорее предпочтет остаться на связи, чем играть в вашу игру. Собственно и время, которое он предпочтет провести в игре уменьшится, если он знает, что батарея устройства неминуемо сядет. Вывод из этого один — нужно стараться экономить ресурсы.

Перейдем к следующей части формулы — охват аудитории. Здесь метрики вполне понятны. Это производительность и портируемость. Чем больше платформ, тем больше устройств охвачено, а соответственно пользователей. Чем лучше производительность, тем более слабые устройства поддерживают игру и больше игроков могут запустить вашу игру. Можно понадеяться на то, что у ваших игроков в кармане новенький продукт Apple, но статистика показывает что новых устройств мало, все ходят с устаревающими «середнячками». Примерно такая же ситуация и на Android.


Теперь у нас есть примерное представление какие метрики у движков есть и как они влияют на успех игры:
  • Входной порог
  • Инструментарий
  • Usability
  • Сложность разработки игровой логики
  • Гибкость
  • Портируемость
  • Производительность

Исходя из этого списка оценим существующие движки. Предлагаю рассмотреть лишь самые значимые, опираясь на статистику, собранную Unity3D, с несколькими моими дополнениями.
Мнение ниже не следует рассматривать как 100% истину, оно составлено мной и может не отражать действительность в полной мере
Возможно пока что мало людей распробовали этот движок, но он развивается и становится лучше. В нем есть неплохой редактор all-in-one, в котором вы найдете все что нужно. Игровой код пишется на внутреннем скриптовом языке, похожим на Python. Разработчики утверждают что есть возможность оптимизировать части игры на С++, или делать дополнения для редактора.
  • Входной порог
    несмотря на простоту движка он довольно высок. Во-первых, в нем собственный скриптовый движок. Во-вторых мало документации и примеров
  • Инструментарий
    есть все что необходимо, а так же можно делать свои
  • Usability
    здесь так же сказывается плохая документация и небольшое количество примеров. Но и в целом инструментарий оставляет впечатление непродуманности и неудобства
  • Сложность разработки игровой логики
    скрипты похожи на Python, а значит писать их просто, но тем не менее язык свой, поэтому вряд ли будет просто найти какое-то решение в сети
  • Гибкость
    игровая логика пишется на скриптах, исходный нативный код открыт. В теории можно сделать все что угодно, но на практике, насколько я слышал, их сложно совместить
  • Портируемость
    портируется на все нужные платформы
  • Производительность
    учитывая скрипты, рассчитывать на высокую производительность не стоит


Внутренний корпоративный движок от King, который они решили вывести в свет. Появился в общем доступе относительно недавно, быстро развивается сообщество. Под капотом у него, по заверению разработчиков, все максимально оптимизировано. Игровая логика пишется на LUA скриптах. Есть all-in-one редактор, но довольно ограниченный по функционалу.
  • Входной порог
    довольно низкий за счет LUA скриптов и быстроразвивающегося сообщества
  • Инструментарий
    есть единый редактор, но много не хватает, например анимаций, полноценной компонентной системы
  • Usability
    этот движок вырос как корпоративный движок студии King, соответственно он не был изначально нацелен на широкую аудиторию. Поэтому инструменты несколько специфичны и ограничены
  • Сложность разработки игровой логики
    LUA один из самых лучших скриптовых языков, весьма распространен и легко изучаем. Однако, по моему опыту, большие проекты на чистых LUA-скриптах писать затруднительно
  • Гибкость
    движок довольно ограничен парадигмами, заданными создателями. Одна из них — игровая логика пишется на LUA. Хотя и есть возможность включить нативный код, упор именно на то, что игра пишется на LUA, а этот язык не позволяет делать по истине быстрые решения. Другая парадигма — вечная полная обратная совместимость. Безусловно это хорошо, что проект на первой версии движка без проблем заработает на последней. Однако таким образом необходимо поддерживать старые решения, что замедляет введение новых, а соответственно замедляет развитие
  • Портируемость
    отлично портируется на все платформы. Причем даже на Web
  • Производительность
    LUA очень быстрый скриптовый язык, но, тем не менее, остается скриптовым, что напрямую сказывается на производительности игрового кода. Однако остальные компоненты движка, по заверению создателей, сделаны максимально эффективно


Профессиональный игровой движок на пике технологий. Отличная картинка, производительность, инструмент для настоящих профессионалов. Однако в мобильной разработке применять его сложно. Все же он больше рассчитан на трехмерные AAA игры. Делать мобильные 2D игры на нем — это как из пушки по воробьям.
  • Входной порог
    очень высок. Вам потребуются настоящие специалисты в своем деле, как со стороны игрового контента, так и со стороны программистов
  • Инструментарий
    набор инструментов поистине впечатляет, от редактора частиц до анимирования персонажей
  • Usability
    уверен для профессионалов он супероптимален. Однако в руках непрофессионалов результат получается не очень качественным и долгим
  • Сложность разработки игровой логики
    в Unreal игровая логика пишется либо на С++, либо на Blueprints. На самом деле всю игру на Blueprint’ах не напишешь, уж очень он объемный получится и запутанный, а про С++, думаю, не стоит рассказывать. По словам разработчиков все работает так: С++ программисты пишут ядро и «блоки» для Blueprint’ов, а дизайнеры из этих блоков составляют игровые механики. Уверен это отличный подход для 3D шутера, но вряд ли будет просто поместить кучу логики, например, на интерфейсы. А порой большая часть игры — это интерфейсы
  • Гибкость
    архитектура и С++ дают безграничные возможности в разработке, однако опять же уклон в трехмерные проекты. Двумерные игры для создателей никогда не были в приоритете
  • Портируемость
    сейчас Unreal запускается на всех мобильных платформах, однако ходят слухи о многочисленных проблемах, таких как размер приложения
  • Производительность
    одни из лучших инженеров игровой индустрии разработали движок, нативный, все это говорит о том что из железа можно выжать все до последней капли


Полностью бесплатный и с открытым исходным кодом движок. Есть all-in-one редактор, документация и куча примеров. Игровая логика пишется на С++, либо на JavaScript. Очень много проектов на нем выпущено, отличительная особенность этих проектов — хорошая оптимизация. Однако при более детальном рассмотрении редактор оказывается не таким удобным как кажется на первый взгляд, находится куча недочетов в исходниках и приходится много чего доделывать.
  • Входной порог
    довольно высок, ведь для качественной разработки нужно знание С++ и довольно хорошее понимание как делаются игры
  • Инструментарий
    есть почти все необходимое
  • Usability
    весьма сомнительное, на мой взгляд. Очень много скопировано из других технологий, но без особых раздумий почему и зачем. В конечном итоге весьма специфично и немного оторвано от реальных задач. Куча консольных утилит так же не ассоциируется с удобством
  • Сложность разработки игровой логики
    в основном используются С++, но так же есть возможность использовать JavaScript. Однако использовать их вместе несколько затруднительно
  • Гибкость
    открытый исходный код и С++ позволяют сделать все что угодно
  • Портируемость
    все мобильные платформы
  • Производительность
    из-за своей простоты и гибкости позволяет достичь очень хороших результатов

Game maker/Construct и похожие


Движки такого рода создавались для того, чтобы любой человек мог сделать свою игру, без каких-либо знаний о разработке. Все максимально просто и понятно, с этим эти движки справляются великолепно. Однако для крупных или даже средних проектов они плохо подходят.
  • Входной порог
    минимален
  • Инструментарий
    есть все необходимое для создания небольших или простых игр. Удобнее всего на них делать игры в стиле pixel-art
  • Usability
    стоит посмотреть редактор Game Maker 2! Пожалуй лучшее решение для движка данной категории. Очень продумано и рассчитано на целевую аудиторию
  • Сложность разработки игровой логики
    либо элементарный скрипт, либо вообще отсутствие скриптов. Вместо скриптов применяется система реакция-действие, в которой описывается все игровое взаимодействие
  • Гибкость
    очень ограниченный функционал, но для простых игр хватает
  • Портируемость
    все мобильные платформы
  • Производительность
    естественно парадигма разработки таких простых проектов требует много дополнительных ресурсов. Как правило разработчики уходят с таких движков в более низкоуровневые именно столкнувшись с плохой производительностью


Самый распространенный в наши дни движок. И этому есть причина — он очень удобный. Великолепный редактор, практически неограниченные возможности, 2D, 3D графика, все что захочется. И конечно же C#, с которым разработка идет довольно легко. Однако у него тоже есть скелеты в шкафу. Зачастую производительность оставляет желать лучшего. Согласен, если приложить усилия, то можно сделать многое, но цена за это высока. Кроме этого порой непонятны планы разработчиков на развитие движка. Самые нужные вещи остаются на дне roadmap, но в новых и новых версиях появляется то, что отлично звучит на продающей странице, но в разработке далеко не всем нужно.
  • Входной порог
    очень низкий. Вашу первую игру вы сделаете через пару-тройку туториалов. Огромное сообщество, куча доступных решений
  • Инструментарий
    есть абсолютно все, некоторое даже излишне на большинстве проектов. А если нет нужного в движке, наверняка найдется в магазине
  • Usability
    это самый большой плюс движка. Отличные инструменты ускоряют разработку в разы. Однако, при более глубоком использовании всплывают недостатки, например: местами не очевидная логика работы UI, не очень хорошо продуманные анимации. Некоторые вещи очень долго просят пользователи, но разработчики все никак не сделают. В целом, для разработки мобильных игр — лучшее что можно найти
  • Сложность разработки игровой логики
    C#, хоть и несколько специфичный в Unity, очень удобный язык. Очень часто слышу от С++ программистов мнение, что это «глоток свежего воздуха». По личному опыту хочу отметить, что разработка на C# в разы быстрее чем на C++
  • Гибкость
    очень гибкие инструменты и архитектура движка в целом. Однако, во многих вещах остается недоступен для изменения. Закрытый код дает некий эффект черного ящика, на который зачастую невозможно повлиять. Есть свобода для оптимизаций, но тонкие оптимизации не получится сделать в следствие своей закрытости.
  • Портируемость
    портируется на все что возможно, ходят слухи скоро на пылесосах можно будет запустить
  • Производительность
    в целом движок очень хорошо оптимизирован, и на нем при должном усердии можно сделать производительную игру. Однако, C# довольно сильно ограничивает. Порой над C# кодом думаешь гораздо больше о его производительности, чем над С++. И как правило в игре находится место, где производительность проседает. В таких случаях очень много ресурсов уходит на оптимизацию. Так же почти всегда возникают проблемы с производительностью, связанные с UI. Сложные диалоги, длинные списки без оптимизаций довольно сильно нагружают систему.


Собственные движки, С++


Выбор либо крупных студий, либо безумцев. Для этого нужны крутые специалисты, а их мало и стоят они дорого. А в итоге получается франкинштейн из технологий, который то и дело разваливается. Получается очень неправильно поставленная разработка, чаще всего именно те дорогие программисты делают все. Но зато получается самый идеальный продукт. Все что захотели дизайнеры — реализовано, работает максимально плавно и запускается на древних устройствах. Но цена за такую технологию велика.
  • Входной порог
    очень высок, документации нет, единственный способ узнать как все работает через коллег
  • Инструментарий
    как правило в таких движках инструментов либо нет, либо есть элементарные редакторы, удовлетворяющие каким-то специфичным нуждам. Именно на таких движках как правило все делают программисты. Ведь обычному дизайнеру крайне сложно понять все эти технические дебри
  • Usability
    даже если и есть инструменты, то они работают откровенно плохо. Оно и понятно, ведь такие движки пишут чтобы сделать игру, а игру нужно сделать побыстрее, отсюда низкое качество инструментов, куча багов, консольных утилит и вот всего этого. Но даже если есть какие-то инструменты, они написаны программистами без учета того, что ими будут пользоваться не программисты, в итоге редактор эффектов превращается в панель запуска космического шаттла, а не в красивый интерфейс с тремя кнопочками.
  • Сложность разработки игровой логики
    начинать с такими движками очень тяжело, ведь сначала нужно понять его устройство, все его хаки и грабли. Но затем это играет лишь на руку. Как правило такие движки делаются с уклоном под игру, на которых они делаются, поэтому со временем писать код все легче и легче. Особенно если прикручен какой-нибудь скриптовый язык.
  • Гибкость
    собственно самое главное преимущество таких движков. Разработчик ограничен лишь самим собой, он может делать все что угодно.
  • Портируемость
    как правило такие движки портированы лишь на те платформы, на которых выходит игра. Оно и понятно, зачем тратить время на поддержку платформы, если не планируется на ней выпускать игру. Однако с опытом мною замечено, что в таких движках операции с платформами очень неповоротливы, сборки очень сложны и длительны
  • Производительность
    очевидно те, кто в состоянии написать свой движок, довольно хорошо разбираются в своей теме. При этом никаких ограничений нет. Соответственно можно сделать все что угодно в угоду производительности и достичь идеального результата.

Так все же, зачем нужен еще один?


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

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

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

Верхний уровень то же должен быть достаточно продуманным, чтобы хорошо выполнять свою функцию. Многие разработчики In-house движков здесь делают одну и ту же ошибку, они собирают разные технологии вместе и строят связи между ними. Однако, как показывает практика, это ведет к тому что они плохо взаимодействуют. Более конкретный пример: импорт анимаций из стороннего редактора и эффекты частиц из другого. Как только становится необходимым использовать эффект в анимации, начинаются проблемы. В редакторе анимаций частицы не проигрываются и наоборот. Соответственно конечный результат достигается дизайнером довольно тяжело. Поэтому все должно быть в одной системе, чтобы дизайнер мог анимировать, накладывать звук, эффекты и все сразу, в одном месте.

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

  • Входной порог
    как можно более низкий. Система, где «верхний» уровень держится на «нижнем», полностью дает возможность снизить входной порог, при этом не ограничивая в гибкости
  • Инструментарий
    должно быть все самое необходимое: работа с ресурсами, графикой, анимациями, интерфейсами, эффектами, физикой, грамотная компоновка сцены. В идеале это все должно быть в одной системе, чтобы каждый из компонентов идеально подходил друг к другу
  • Usability
    каждый инструмент должен быть продуман со стороны реального использования. Недостаточно просто сделать редактор частиц, интерфейсов или анимаций. Все это должно быть понятно и удобно именно тем, кто специализируется на этом
  • Сложность разработки игровой логики
    как и описано выше, высокоуровневые компоненты должны базироваться на низкоуровневых. Например, все затратные и низкоуровневые вещи делаются нативным кодом, те самые 20% сложных процентов. Остальные 80% можно переложить на скрипты, которые гораздо быстрее писать, но при этом их производительность не критична. Благо в наши дни встраиваемых скриптов много
  • Гибкость
    движок должен быть полностью изменяемым и дополняемым. Единственный правильный путь к этому — это хорошее API и открытый исходный код
  • Портируемость
    так как речь идет в основном о мобильной разработке, приложения на движке должны без труда портироваться на все мобильные платформы. Кроме мобильных платформ сейчас становится все более привлекательна web-платформа. С развитием WebAssembly и графики в браузерах становится все реальнее и реальнее запускать нативные игры в браузере и на мобильных платформах
  • Производительность
    сама технология должна предоставлять максимально оптимизированные компоненты — интерфейс, анимации, эффекты и так далее. Чтобы при невдумчивом наполнении игры контентом все было более-менее приемлимо в плане оптимальности. Но так же стоит оставить возможность для тонкой оптимизации, которая понадобится для доведения продукта до идеала

Итог


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

habr.com

Как написать движок для сайта

Вам понадобится

  • — доступ в интернет;
  • — современный браузер.

Инструкция

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

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

Определите технологии, на основе которых будет реализован основной функционал продукта. Выберите язык программирования. Стандартом де-факто для разработки CMS сейчас является PHP. Хотя можно рассмотреть использование ASP .NET, Java, Python. Выберите технологии хранения данных. Как правило, современные CMS используют базы данных в качестве основного места хранения информации. Но в отдельных случаях можно положиться на файловую систему. Выделите другие технологии, которые потребуются для работы движка (библиотеки обработки графики, проверки орфографии, криптографические модули, и т.д.).

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

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

Реализуйте часть проекта в соответствии с ранее разработанным техническим заданием.

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

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

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

Обратите внимание

Реализуя движок сайта «с нуля», будьте особо внимательны при обработке принимаемых извне данных. Недостаточная фильтрация может быть причиной уязвимости, несущей угрозу безопасности.

Полезный совет

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

www.kakprosto.ru