Приветствую, читатели!
Через данный цикл уроков хочу воплотить мини мечту по разработке движка для игры 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
В следующем уроке, познакомимся с другими форматами и выведем первый спрайт.
Статья больше получилась обзорная и текста больше чем кода, но уже в следующих уроках разгонимся.
Спасибо за внимание. Буду рад критике, советам и предложениям как по коду так и по оформлению статей.
Автор: JordanCpp