Приветствую, Хабравчане!
Через данный цикл уроков хочу воплотить мини мечту по разработке движка для игры Arcanum. Далее опишу мои вводные. Всё я это делаю по фану. Поэтому некоторые вводные могут быть даже пугающими и просто странными, но хочется не только написать движок, но и поработать с тем старым ретро железом и софтом 20-ти летней давности.
-
Мотивация
В первую очередь это конечно же личное пристрастие к этой прекрасной игре. Много сотен часов, было потрачено на прохождение разными персонажами, открытию новых локаций. Разному типу прохождению, ха мага, технаря, попытки в баланс между магией и технологией.
-
Выбор языка прораммиования, конечно же С++.
На 2024 год к моему распоряжению доступно 100500 разных языков. Новых, старых, со сборщиком, без сборщика мусора. Но предпочтение отдано С++. В первую очередь его знание, понимание и несколько проф. проектов, разработанных на нём.
-
Переносимость.
В 21 веке писать непереносимый код, имея огромное количество кроссплатформенных библиотек, компиляторов под любую платформу, разрабатывать только под Windows, считаю моветоном. Поэтому для данного проекта я выбрал библиотеку SDL. Так же мне нравится идея обеспечить совместимость со старыми версиями Windows 95, 98 и Linux (Debian 3), для этого я буду использовать для старых версий операционных систем библиотеку SDL 1.2, для современных Windows и Linux SDL2. Поэтому я буду использовать стандарт языка С++ 98, (О боже!).
Это единственный простой путь, который может обеспечить совместимость. В принципе не так уж и плохо. С++ 98, имеет вполне приемлемый набор контейнеров и в любом случае он на порядок лучше С по возможностям и выразительности. У меня особых переживаний на это счёт нет. Буду рад, если в комментариях, кто-то предложит другой вариант, возможность к примеру писать на С++ 11-17 и собирать под Windows 95.
Главное никакого специфичного для компилятора кода. Проект под все системы для всех архитектур, должен собираться из одной кодо базы и минимального количества ifdef'ов. Поддерживать 32-ух и 64-ех битные сборки.
-
Инструменты разработки.
Разрабатывать планирую на Windows 10, Visual Studio 2022, cmake. Для ручной сборки так же имеетсю bat файлы. Для совмсестимости с Windows 95 и 98, использую компилятор Visual C++ 6.0 Для сборки под Debian 3, компилятор gcc 3.
-
Производительность
Планирую на всех этапах разработки движка игры, следить за производительностью и оптимизировать код. Arcanum игра начала 2000-ых годов, со смехотворными системными требованиями для текущего времени, поэтому хочу сохранить похожие требования или по крайней мере не увеличить их на порядок.
Для тестирования производительности буду использовать эмуляторы типа x86box, так и мой ретро ПК с Pentium 4 (с пониженной частотой до 1000 mhz) и Geforce 4.
-
Архитектура
На мой взгляд максимально поддерживать простоту, как архитектурно так и частоту кодобазы. Не упариваясь все написать по SOLID, но и не скатываясь в многотысячные портянки функций. Все в меру. Классы простые и маленькие реализующие один функционал. Зависимость между классами передавать через конструктор. Интерфейсы почти не юзаем. Каждый класс, зависит от конкретного класса. Движок разделен по коду на 2 библиотеки: Arcanum как игра, и сам 2D изометрический движок (Pollux). Это позволит в будущем использовать его для других старых игр, сам движок будет по ходу дела расширяться, обобщаться подсистемы к примеру работа с картой и тайлами.
Движок Pollux содержит единое API над SDL1 и SDL2, что бы движок и игры был написан один раз, не меняя код и не добавляя ifdef'ы.
-
Общий процесс разработки.
Разработку веду в едином репозитории. Каждая ветка является уроком в котором я раскрываю тему. Не вижу смысла постить в статье портянки cpp файлов. Ограничусь hpp файлами с коротким описанием простых вещей и более объемным для сложных.
Для упрощения сборки проекта из коробки, зависимости SDL для Windows лежат прямо в репе в собранном виде, dll и lib. Да я понимаю, что так не делают, но это позволяет не ставить msys, cmake, долго настраивать пути к компилятору, библиотеки и т.д Сделать git clone в Visual Studio и нажать собрать.
-
Код стайл.
Прошу прощения, но я по работе пишу на С# из-за этого код стайл сишарповский. Прошу понять и простить:) В будущем втащу в проект какой-нибудь clang формат.
-
О формате файлов игры.
Новый движок будет работать только с графическими и звуковыми форматами игры. Остальные форматы такие как диалоги, скрипты, форматы карт, прототипов объектов будут иметь текстовый формат xml. Скрипты будут написаны на С++, что позволяет не отвлекаться на встраивание скриптового языка и его обвязку. Так же движок будет поддерживать современные форматы графики, jpeg, png. Палитровая графика Arcanum, при выводе на экран будет конвертироваться в rgb.
Ссылки на уроки. Уроки находятся в ArcanumTutorial_Habr, переключитесь на ветку ArcanumTutorial_01_Start.
Я думаю вводных достаточно, что бы сделать общие выводы. Теперь поехали писать код.
Первый урок, это минимум кода. Больше опишу об инфраструктуре проекта.
Сейчас движок умеет толко открывать окно и закрывать. Такой функционала нам хватит, что бы проверить работоспосбность и компиляцию проекта на всех системах. Ниже будут скриншоты.
Каждый функционал лежит в своей подпапке.
Pollux
--- Events - система событий ОС, нажатие, клик мышкой и т.д
EventHandler позволяет конвертировать события SDL_Event в события движка.
bool EventHandler::GetEvent(Event& dstEvent)
{
SDL_Event event = { 0 };
if (_Running)
{
SDL_PollEvent(&event);
if (event.type == SDL_QUIT)
{
dstEvent.Type = IsEventQuit;
}
else if (event.type == SDL_MOUSEMOTION)
{
dstEvent.Type = IsEventMove;
dstEvent.Move.PosX = event.motion.x;
dstEvent.Move.PosY = event.motion.y;
}
}
return _Running;
}
---Grphics - работа с графикой
Пока доступен класс Canvas, который умеет инициализировать окно, в следующих уроках добавлю рисование текстур.
В конструкторе инициализируем окно и рендер SDL. Метод Canvas::Present отвеает за обновление окна.
#include <Pollux/Graphics/Canvas.hpp>
#include <stdexcept>
using namespace Pollux;
Canvas::Canvas(const Point& size) :
_Window(NULL),
_Render(NULL),
_Size(size)
{
if (SDL_Init(SDL_INIT_EVERYTHING) != 0)
throw std::runtime_error(SDL_GetError());
_Window = SDL_CreateWindow("", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, _Size.x, _Size.y, SDL_WINDOW_SHOWN);
if (!_Window)
throw std::runtime_error(SDL_GetError());
_Render = SDL_CreateRenderer(_Window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
if (!_Render)
throw std::runtime_error(SDL_GetError());
}
Canvas::~Canvas()
{
SDL_DestroyRenderer(_Render);
SDL_DestroyWindow(_Window);
SDL_Quit();
}
const Point& Canvas::Size() const
{
return _Size;
}
void Canvas::Present()
{
SDL_RenderPresent(_Render);
}
SDL_Renderer* Canvas::GetRenderImpl()
{
return _Render;
}
Arcanum
---Game - Код относящийся к игре
Минимальный движок игры.
#include <Arcanum/Game/Engine.hpp>
using namespace Arcanum;
using namespace Pollux;
Engine::Engine() :
_Canvas(Point(800, 600))
{
}
Engine::~Engine()
{
}
void Engine::Run()
{
Event report;
while (_EventHandler.GetEvent(report))
{
if (report.Type == IsEventQuit)
{
_EventHandler.StopEvent();
}
_Canvas.Present();
}
}
Инициализируем окно, запускаем обработчик сообщений и ждем пока пользователь нажмет выход.
#include <Arcanum/Game/Engine.hpp>
using namespace Arcanum;
int main(int argc, char* argv[])
{
Engine engine;
engine.Run();
return 0;
}
Так выгляди cmake.
if (MSVC)
add_definitions(-D_CRT_SECURE_NO_WARNINGS)
add_definitions(-D_CRT_NONSTDC_NO_DEPRECATE)
endif()
if (WIN32)
cmake_minimum_required(VERSION 2.9)
set(SDL2_INCLUDE_DIRS "dependencies/SDL2-2.30.3/include")
set(SDL2_LIBRARIES SDL2main SDL2)
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
link_directories("dependencies/SDL2-2.30.3/lib/x64")
elseif(CMAKE_SIZEOF_VOID_P EQUAL 4)
link_directories("dependencies/SDL2-2.30.3/lib/x86")
endif()
else()
find_package(SDL2 REQUIRED)
endif()
include_directories(${SDL2_INCLUDE_DIRS})
include_directories("Pollux/SDL2")
file(GLOB_RECURSE POLLUX_SOURCES "Pollux/SDL2/*.cpp")
include_directories("Pollux/Shared")
file(GLOB_RECURSE SHARED_SOURCES "Pollux/Shared/*.cpp")
include_directories("Arcanum/Shared")
file(GLOB_RECURSE ARCANUM_SOURCES "Arcanum/Shared/*.cpp")
add_executable(Arcanum "main.cpp" ${POLLUX_SOURCES} ${SHARED_SOURCES} ${ARCANUM_SOURCES})
target_link_libraries(Arcanum ${SDL2_LIBRARIES})
Так же в каталоге make, лежат батники для сборки с помощью mingw и visual C++ 6.0
Теперь убедимся, что оно работает.
Windows 98 - SDL1
Lubuntu 22.04
В следующем уроке, познакомимся с другими форматами и выведем первый спрайт.
Статья больше получилась обзорная и текста больше чем кода, но уже в следующих уроках разгонимся.
Спасибо за внимание. Буду рад критике, советам и предложениям как по коду так и по оформлению статей.
Комментарии (19)
codecity
14.08.2024 18:02+3Для совмсестимости с Windows 95 и 98
Но зачем?
JordanCpp Автор
14.08.2024 18:02+1Это фан и возможность пощупать и писать код с поддержкой настолько древнего железа и ос. Старое железо мне интересно в разрезе производительности. Имея слабое и и по современным меркам производительность на уровне погрешности, если сравнивать с современным. Ответственно подойти к проекту и достичь тех же результатов на железе 20-ти летней давности. Имея малое, получить большее.
Такое у меня ретро хобби. Преимущество в том, что я не пишу только под старые системы. Новые системы тоже поддерживаются.
unreal_undead2
14.08.2024 18:02+1Совместимость со старой виндой - это скорее просто fun, а вот достижение сравнимой с оригиналом производительности на том же железе - хорошая идея. А то, скажем, ради Baldur's Gate и Morrowind на старом ноуте приходилось перегружаться в Windows и запускать оригинальные бинарники (для них мощности хватало) - open source движки и того и другого есть, но очень заметно тормозили.
JordanCpp Автор
14.08.2024 18:02Новые движки для старых игр, используют текущие технологии, современный opengl или vulkan. Современный графический движок, который не заточен под железо 20-ти летней давности. Тратить время, что бы добавить поддержку, долго, дорого и бессмысленно.
Поэтому такая картина.
unreal_undead2
14.08.2024 18:02По крайней мере scummvm в этом плане ведёт себя прилично и лишнего не требует.
DanilinS
14.08.2024 18:02И у меня есть аналогичная тема. То-же переписываю понемногу Арканум. Правда на движке GameMaker Studio 2. И то-же пока на начальной стадии. Посмотрим, чей вариант будет лучше?
JordanCpp Автор
14.08.2024 18:02У нас же тут не соц. соревнование. Если хотите можете сравнивать по мере прогресса, у кого лучше, выше, сильнее:)
DanilinS
14.08.2024 18:02Да какое там соревнование ... Старый вялотекущий проект с невысокими шансами завершения.
Но возможно пообщаться по форматам файлов нам будет полезно.
JordanCpp Автор
14.08.2024 18:02Но возможно пообщаться по форматам файлов нам будет полезно.
Да конечно. В следующем уроке я опишу формат dat и art. О форматах карт я информацию не нашел.
MaksimMukharev
14.08.2024 18:02А для Мак не планируется?
JordanCpp Автор
14.08.2024 18:02Да планирую. Насколько я понимаю, нужно будет чуть подправить cmake файл. Но у меня нет мака что бы затестить,, что все собирается. Только если кто то со стороны это сделает и пришлет код.
Вообще планирую собирать под всё, на что портирован SDL1 и SDL2, это на будущее.
Будет использовано минимум зависимостей, SDL, SDL_ttf, SDL_Mixer, zlib и stb_image. Все остальное пишу сам.
JordanCpp Автор
14.08.2024 18:02При разработке я постоянно собираю код, gcc 3, gcc 11, msvc 2022 и Visual C++ 6.0. совместимость на уровне кода отличная. Сейчас собирается без единого предупреждения. Компилятора специфичного кода не использую, все в рамках стандарта С++ 98.
Dagnir
То есть просто движок ради движка? Недавно сам Тим Кейн сел разбирать оригинальный движок — есть вероятность, что готовится какой-то его современный порт.
Видео от Кейна с возможно полезной информацией
Также был проект по созданию многопользовательской игры на базе Arcanum на движке FOnline (опенсорс) — но был заброшен ввиду невообразимо огромного объёма работы (движок сам готов и широко используется в разных играх на базе Fallout, а вот перенос контента Arcanum застопорился). Там уже и поддержка современных форматов изображений, и нативная поддержка родных файлов Arcanum в одном пакете. Возможно, было бы проще начинать с уже готовых наработок? Движок — это только маленькая часть работы, основное — это контент. Попробуйте постучать@Tab10id, если хотите скооперироваться, это один из авторов проекта. Но даже если хотите писать своё, то посмотреть, как сделана уже готовая реализация, точно будет полезным.
Апд.: нашёл ещё основной сайт проекта: www.arcreborn.ru
JordanCpp Автор
Спасибо за информацию и ссылки. Я бэкендер и для меня написание движков, рисование 2D или 3D графики в новинку. И мне интересно не только разобраться в теории, но и разработать движок на практике. Это довольно увлекательное занятие.
Да я понимаю, что есть другие движки, но именно мне интересно разработать с нуля, используя минимум зависимостей.
Да вы правы, движок ради движка. Но конечно я планирую перенести карты и скрипты, конвертировав их. Но без описания их формата, это сделать невозможно. Может я в конце разработки и попробую сам их разобрать, но это довольно ресурсо затратно по степени.
Да за референс, я взял открытый движок freeheroes2. Мне он очень понравился простотой кодовой базы. Арзитектурно прост. Многие архитектурные решения черпаю из него.
Tab10id
Ну всё таки работа была заброшена не столько из-за объёма работы, сколько из-за следующих факторов:
команда работающая без управления просто перестала работать, энтузиазма на такое редко когда хватает в непрофессиональных коллективах;
в движке произошли глобальные изменения, фактически половина функциональности движка была удалена, быстро переписать эту часть на скрипты было невозможно, особенно с учётом того что движок в тот момент ещё не был открыт;
катастрофическая нехватка времени.
Был вариант не переводить игру на новую версию движка и вероятно в этом режиме игра могла бы и существовать на сегодняшний день, но в этом случае можно было бы забыть о запуске игры в браузере, на ios и android.
Я лично хотел довести проект до запускабельного состояния на актуальной версии движка, после чего снова попробовать привлечь энтузиастов к разработке, но до этого, к сожалению, так и не дошло.
Проект всё ещё можно оживить, но сейчас у меня этот проект с таким низким приоритетом в жизни, что фактически руки у меня до него не дойдут примерно никогда.
Но надежда никогда не умирает полностью, доменное имя вот до сих пор оплачиваю=).
Dagnir
О, спасибо за комментарий (и привет). Ну 1 и 3 всё ещё связаны с объёмом. Времени и энтузиазма хватило бы, если бы это был маленький проект. А вот изменения в движке действительно порушили многое. Ничто не убивает энтузиазм как необходимость делать уже проделанную работу заново. Я отвалился как раз на моменте, когда после известного скандала пришлось вырезать части игры и делать что-то на замену.
JordanCpp Автор
Я видел последнее виде по вашей демке. В котором огр на мосту бегал. Очень здорово. Я понимаю, что до завершения переноса, ещё далеко. Но вы сделали большую работу.
Suvitruf
Не готовится. Тим много раз повторял, что он не может распространять исходники =\