- PVSM.RU - https://www.pvsm.ru -
В 2016 году я начал работу над хобби-проектом по реверс-инжинирингу игры Duke Nukem II [1] и воссозданию с нуля его движка. Проект имеет название Rigel Engine и выложен в open source (его страница на GitHub [2]). Сегодня, более чем два с половиной года спустя на моём движке уже можно пройти весь shareware-эпизод оригинальной игры с практически идентичным оригиналу игровым процессом. Вот видео с прохождением первого уровня:
Что же он может делать? Rigel Engine работает как полная замена оригинального двоичного файла DOS (NUKEM2.EXE
). Можно скопировать его в каталог игры и он считает из него все данные, или же указать путь к данным игры как аргумент командной строки. Движок собирается и выполняется под Windows, Mac OS X и Linux. Он основан на SDL [3] и OpenGL 3/OpenGL ES 2, а написан на C++ 17.
Он реализует игровую логику всех врагов и игровых механик из Shareware-эпизода, плюс бОльшую часть системы меню. Кроме того, в него можно импортировать сохранённые игры и таблицу рекордов из оригинальной игры.
Более того, движок имеет преимущества по сравнению с оригиналом:
Пока я не считаю Rigel Engine полностью «готовым». Но это отличный этап в развитии и хорошая возможность снова написать о движке (старые посты опубликованы здесь [4] и здесь [5]). Давайте начнём с того, что взглянем на текущее состояние кода, и узнаем, как я к нему пришёл.
На момент написания статьи RigelEngine состоит из 270 исходных файлов, содержащих более 25 тысяч строк кода (без комментариев/пустых строк). Из них 10 файлов и 2,5 тысяч строк — юнит-тесты. Подробная разбивка с учётом пустых строк и комментариев выложена здесь [6].
Что же есть во всём этом коде? Немного общей инфраструктуры и вспомогательных функций, такие фундаментальные вещи, как рендеринг, и куча небольших кусочков логики. Кроме всего этого самые большие части — это:
Разумеется, весь этот код нужно было написать, и это приводит нас к следующему вопросу.
Хотя с момента начала проекта прошло два с половиной года, я не работал над ним всё это время. Пару месяцев я вообще не занимался проектом, в некоторые другие уделял ему всего несколько часов. Но были и моменты, когда я работал над Rigel Engine достаточно активно. Взглянув на график коммитов на Github, можно получить примерное представление о том, как была распределена по времени моя работа:
По графику мы видим, что в ветку master был сделан 1081 коммит. Однако ещё до создания репозитория я работал над закрытым, в котором было ещё 247 коммитов, что в сумме даёт нам 1328 коммитов. Кроме того, было несколько веток прототипов, которые я использовал для исследований и экспериментов, но никогда не объединял с основной; к тому же перед слиянием я иногда сжимал большие истории коммитов в более краткие.
Нужно также сказать, что написание кода было не единственной частью проекта — ещё одной важной частью был реверс-инжиниринг. Я потратил довольно много часов на изучение дизассемблированного кода оригинального исполняемого файла в Ida Pro [7] (в бесплатной версии), на ведение заметок, запись псевдокода и планирование реализации элементов моей версии. Кроме того, я проводил активное тестирование оригинальной игры, запуская её в DOSBox [8] и на оригинальном оборудовании (разных машинах 386 и 486, купленных на eBay). Я собирал тестовые уровни для отдельного наблюдения за конкретными врагами и изучения игровой механики, записывал видеоклипы с помощью DOSBox, и покадрово просматривал записи, чтобы подтвердить свои выводы, сделанные при изучении ассемблерного кода. После реализации врага или игровой механики я обычно записывал видеоклип из моей версии и покадрово сравнивал её с оригиналом, чтобы подтвердить точность моей реализации.
Вот несколько фотографий моих заметок:
Реверс-инжиниринг кода управления камерой. Большой прямоугольник обозначает экран. Пунктирными линиями показаны зоны, в которые может двигаться игрок без перемещения камеры. Если вам интересно, то сам код управления камерой можно найти здесь [9].
Общие заметки, помогающие в понимании ассемблерного кода. Слева — порядок обновления оригинальной игры на высоком уровне. Справа — заметки о битовых полях, обозначающих состояние некоторых игровых объектов.
Транскрипция ассемблерного кода в псевдокод. Обычно я делаю это достаточно механистично, транскрибирую, не задумываясь о том, что конкретно делает код, а потом использую версию в псевдокоде, чтобы понять лежащую в основе логику. И на основании неё я уже придумываю свою реализацию. Готовый код см. здесь [10].
Псевдокод подчищенной версии логики врага. Заголовки обозначают состояния конечного автомата, код ниже объясняет, что должно происходить в соответствующих состояниях. Он был создан на основе сырого псевдокода, полученного при транскрибировании ассемблерного кода. Готовый код можно найти здесь [11].
В конечном итоге работа над проектом оказалась очень увлекательной, и я многое из него узнал: о реверс-инжиниринге, 16-битном ассемблере x86, низкоуровневом программировании VGA, строгих ограничениях, с которыми приходилось сталкиваться разработчикам игр для PC в начале 90-х; кроме того, я совершил множество открытий о внутренних особенностях оригинальной игры и о том, насколько странно и причудливо некоторые из них были реаизованы — эта тема сама по себе заслуживает отдельной серии постов.
Кроме добавление последних оставшихся функций и доделки поддержки зарегистрированной версии у меня есть несколько идей по улучшению и расширению возможностей движка Rigel Engine, не говоря уже о подчистке и рефакторинге кода — как обычно, наилучший способ создания архитектуры ПО становится очевидным только после завершения создания этого ПО.
Что касается будущих улучшений, то вот некоторые из пунктов, о реализации которых я думал:
У меня нет никакой «дорожной карты» на будущее, поэтому я могу реализовывать эти пункты в любом порядке. Но перед всем этим следующим шагом будет интеграция Dear ImGui для дальнейшей сборки меню опций, которого в игре пока нет; кроме того, оно позволит включать и отключать перечисленные выше улучшения. В конце скажу, что буду благодарен за любой содействие в работе на GitHub [12]!
Автор: PatientZero
Источник [13]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/dos/319736
Ссылки в тексте:
[1] Duke Nukem II: https://en.wikipedia.org/wiki/Duke_Nukem_II
[2] его страница на GitHub: https://github.com/lethal-guitar/RigelEngine
[3] SDL: https://www.libsdl.org/
[4] здесь: https://lethalguitar.wordpress.com/2017/04/02/introducing-the-rigel-engine-dev-blog/
[5] здесь: https://lethalguitar.wordpress.com/2017/10/20/state-of-the-project-pt-1/
[6] здесь: https://gist.github.com/lethal-guitar/6cc2421fe05aff85be49e228d5b20258
[7] Ida Pro: https://www.hex-rays.com/products/ida/support/download_freeware.shtml
[8] DOSBox: https://www.dosbox.com/
[9] здесь: https://github.com/lethal-guitar/RigelEngine/blob/67f1ff7faecb565a93b72d1f803c4424c9428e63/src/game_logic/camera.cpp#L157
[10] здесь: https://github.com/lethal-guitar/RigelEngine/blob/67f1ff7faecb565a93b72d1f803c4424c9428e63/src/game_logic/ai/boss_episode_1.cpp#L79
[11] здесь: https://github.com/lethal-guitar/RigelEngine/blob/67f1ff7faecb565a93b72d1f803c4424c9428e63/src/game_logic/ai/watch_bot.cpp#L284
[12] содействие в работе на GitHub: https://github.com/lethal-guitar/RigelEngine/issues
[13] Источник: https://habr.com/ru/post/454570/?utm_campaign=454570
Нажмите здесь для печати.