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

в 15:21, , рубрики: С++
Пилим движок Arcanum. Урок 01. Начало - 1

Приветствую, читатели!

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

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

Lubuntu 22.04

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

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

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

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

Автор: JordanCpp

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js