Приветствую, Хабравчане!

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

  1. Мотивация

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

  2. Выбор языка прораммиования, конечно же С++.

    На 2024 год к моему распоряжению доступно 100500 разных языков. Новых, старых, со сборщиком, без сборщика мусора. Но предпочтение отдано С++. В первую очередь его знание, понимание и несколько проф. проектов, разработанных на нём.

  3. Переносимость.

    В 21 веке писать непереносимый код, имея огромное количество кроссплатформенных библиотек, компиляторов под любую платформу, разрабатывать только под Windows, считаю моветоном. Поэтому для данного проекта я выбрал библиотеку SDL. Так же мне нравится идея обеспечить совместимость со старыми версиями Windows 95, 98 и Linux (Debian 3), для этого я буду использовать для старых версий операционных систем библиотеку SDL 1.2, для современных Windows и Linux SDL2. Поэтому я буду использовать стандарт языка С++ 98, (О боже!).

    Это единственный простой путь, который может обеспечить совместимость. В принципе не так уж и плохо. С++ 98, имеет вполне приемлемый набор контейнеров и в любом случае он на порядок лучше С по возможностям и выразительности. У меня особых переживаний на это счёт нет. Буду рад, если в комментариях, кто-то предложит другой вариант, возможность к примеру писать на С++ 11-17 и собирать под Windows 95.

    Главное никакого специфичного для компилятора кода. Проект под все системы для всех архитектур, должен собираться из одной кодо базы и минимального количества ifdef'ов. Поддерживать 32-ух и 64-ех битные сборки.

  4. Инструменты разработки.

    Разрабатывать планирую на Windows 10, Visual Studio 2022, cmake. Для ручной сборки так же имеетсю bat файлы. Для совмсестимости с Windows 95 и 98, использую компилятор Visual C++ 6.0 Для сборки под Debian 3, компилятор gcc 3.

  5. Производительность

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

    Для тестирования производительности буду использовать эмуляторы типа x86box, так и мой ретро ПК с Pentium 4 (с пониженной частотой до 1000 mhz) и Geforce 4.

  6. Архитектура

    На мой взгляд максимально поддерживать простоту, как архитектурно так и частоту кодобазы. Не упариваясь все написать по SOLID, но и не скатываясь в многотысячные портянки функций. Все в меру. Классы простые и маленькие реализующие один функционал. Зависимость между классами передавать через конструктор. Интерфейсы почти не юзаем. Каждый класс, зависит от конкретного класса. Движок разделен по коду на 2 библиотеки: Arcanum как игра, и сам 2D изометрический движок (Pollux). Это позволит в будущем использовать его для других старых игр, сам движок будет по ходу дела расширяться, обобщаться подсистемы к примеру работа с картой и тайлами.

    Движок Pollux содержит единое API над SDL1 и SDL2, что бы движок и игры был написан один раз, не меняя код и не добавляя ifdef'ы.

  7. Общий процесс разработки.

    Разработку веду в едином репозитории. Каждая ветка является уроком в котором я раскрываю тему. Не вижу смысла постить в статье портянки cpp файлов. Ограничусь hpp файлами с коротким описанием простых вещей и более объемным для сложных.

    Для упрощения сборки проекта из коробки, зависимости SDL для Windows лежат прямо в репе в собранном виде, dll и lib. Да я понимаю, что так не делают, но это позволяет не ставить msys, cmake, долго настраивать пути к компилятору, библиотеки и т.д Сделать git clone в Visual Studio и нажать собрать.

  8. Код стайл.

    Прошу прощения, но я по работе пишу на С# из-за этого код стайл сишарповский. Прошу понять и простить:) В будущем втащу в проект какой-нибудь clang формат.

  9. О формате файлов игры.

    Новый движок будет работать только с графическими и звуковыми форматами игры. Остальные форматы такие как диалоги, скрипты, форматы карт, прототипов объектов будут иметь текстовый формат xml. Скрипты будут написаны на С++, что позволяет не отвлекаться на встраивание скриптового языка и его обвязку. Так же движок будет поддерживать современные форматы графики, jpeg, png. Палитровая графика Arcanum, при выводе на экран будет конвертироваться в rgb.

  10. Ссылки на уроки. Уроки находятся в ArcanumTutorial_Habr, переключитесь на ветку ArcanumTutorial_01_Start.

Я думаю вводных достаточно, что бы сделать общие выводы. Теперь поехали писать код.

Первый урок, это минимум кода. Больше опишу об инфраструктуре проекта.

Репа - урок 1.

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

Каждый функционал лежит в своей подпапке.

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)


  1. Dagnir
    14.08.2024 18:02
    +4

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

    Видео от Кейна с возможно полезной информацией

    Также был проект по созданию многопользовательской игры на базе Arcanum на движке FOnline (опенсорс) — но был заброшен ввиду невообразимо огромного объёма работы (движок сам готов и широко используется в разных играх на базе Fallout, а вот перенос контента Arcanum застопорился). Там уже и поддержка современных форматов изображений, и нативная поддержка родных файлов Arcanum в одном пакете. Возможно, было бы проще начинать с уже готовых наработок? Движок — это только маленькая часть работы, основное — это контент. Попробуйте постучать@Tab10id, если хотите скооперироваться, это один из авторов проекта. Но даже если хотите писать своё, то посмотреть, как сделана уже готовая реализация, точно будет полезным.

    Апд.: нашёл ещё основной сайт проекта: www.arcreborn.ru


    1. JordanCpp Автор
      14.08.2024 18:02
      +2

      Спасибо за информацию и ссылки. Я бэкендер и для меня написание движков, рисование 2D или 3D графики в новинку. И мне интересно не только разобраться в теории, но и разработать движок на практике. Это довольно увлекательное занятие.

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

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

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


    1. Tab10id
      14.08.2024 18:02
      +1

      Ну всё таки работа была заброшена не столько из-за объёма работы, сколько из-за следующих факторов:

      • команда работающая без управления просто перестала работать, энтузиазма на такое редко когда хватает в непрофессиональных коллективах;

      • в движке произошли глобальные изменения, фактически половина функциональности движка была удалена, быстро переписать эту часть на скрипты было невозможно, особенно с учётом того что движок в тот момент ещё не был открыт;

      • катастрофическая нехватка времени.

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

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

      Проект всё ещё можно оживить, но сейчас у меня этот проект с таким низким приоритетом в жизни, что фактически руки у меня до него не дойдут примерно никогда.

      Но надежда никогда не умирает полностью, доменное имя вот до сих пор оплачиваю=).


      1. Dagnir
        14.08.2024 18:02

        О, спасибо за комментарий (и привет). Ну 1 и 3 всё ещё связаны с объёмом. Времени и энтузиазма хватило бы, если бы это был маленький проект. А вот изменения в движке действительно порушили многое. Ничто не убивает энтузиазм как необходимость делать уже проделанную работу заново. Я отвалился как раз на моменте, когда после известного скандала пришлось вырезать части игры и делать что-то на замену.


      1. JordanCpp Автор
        14.08.2024 18:02

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


    1. Suvitruf
      14.08.2024 18:02

      Тим Кейн сел разбирать оригинальный движок — есть вероятность, что готовится какой-то его современный порт

      Не готовится. Тим много раз повторял, что он не может распространять исходники =\


  1. codecity
    14.08.2024 18:02
    +3

    Для совмсестимости с Windows 95 и 98

    Но зачем?


    1. JordanCpp Автор
      14.08.2024 18:02
      +1

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

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


      1. unreal_undead2
        14.08.2024 18:02
        +1

        Совместимость со старой виндой - это скорее просто fun, а вот достижение сравнимой с оригиналом производительности на том же железе - хорошая идея. А то, скажем, ради Baldur's Gate и Morrowind на старом ноуте приходилось перегружаться в Windows и запускать оригинальные бинарники (для них мощности хватало) - open source движки и того и другого есть, но очень заметно тормозили.


        1. JordanCpp Автор
          14.08.2024 18:02

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

          Поэтому такая картина.


          1. unreal_undead2
            14.08.2024 18:02

            По крайней мере scummvm в этом плане ведёт себя прилично и лишнего не требует.


  1. DanilinS
    14.08.2024 18:02

    И у меня есть аналогичная тема. То-же переписываю понемногу Арканум. Правда на движке GameMaker Studio 2. И то-же пока на начальной стадии. Посмотрим, чей вариант будет лучше?


    1. JordanCpp Автор
      14.08.2024 18:02

      У нас же тут не соц. соревнование. Если хотите можете сравнивать по мере прогресса, у кого лучше, выше, сильнее:)


      1. DanilinS
        14.08.2024 18:02

        Да какое там соревнование ... Старый вялотекущий проект с невысокими шансами завершения.

        Но возможно пообщаться по форматам файлов нам будет полезно.


        1. JordanCpp Автор
          14.08.2024 18:02

          Но возможно пообщаться по форматам файлов нам будет полезно.

          Да конечно. В следующем уроке я опишу формат dat и art. О форматах карт я информацию не нашел.


  1. MaksimMukharev
    14.08.2024 18:02

    А для Мак не планируется?


    1. JordanCpp Автор
      14.08.2024 18:02

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

      Вообще планирую собирать под всё, на что портирован SDL1 и SDL2, это на будущее.

      Будет использовано минимум зависимостей, SDL, SDL_ttf, SDL_Mixer, zlib и stb_image. Все остальное пишу сам.


    1. JordanCpp Автор
      14.08.2024 18:02

      При разработке я постоянно собираю код, gcc 3, gcc 11, msvc 2022 и Visual C++ 6.0. совместимость на уровне кода отличная. Сейчас собирается без единого предупреждения. Компилятора специфичного кода не использую, все в рамках стандарта С++ 98.


  1. JordanCpp Автор
    14.08.2024 18:02
    +1

    Я уже работаю над вторым уроком. Скоро оформлю в статью.