Как стать автором
Обновить

Пилим движок Arcanum. Урок 01. Начало

Уровень сложностиПростой
Время на прочтение6 мин
Количество просмотров13K

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

Через данный цикл уроков хочу воплотить мини мечту по разработке движка для игры 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

В следующем уроке, познакомимся с другими форматами и выведем первый спрайт.

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

Спасибо за внимание. Буду рад критике, советам и предложениям как по коду так и по оформлению статей.

Теги:
Хабы:
Всего голосов 31: ↑31 и ↓0+33
Комментарии27

Публикации

Работа

Программист C++
96 вакансий
QT разработчик
5 вакансий

Ближайшие события