Перетяжка, Премия ТПрогер, 13.11
Перетяжка, Премия ТПрогер, 13.11
Перетяжка, Премия ТПрогер, 13.11

Space Invaders «с нуля» — Часть 1, создаём окно

Аватарка пользователя Даровская Маша
для
Логотип компании Tproger
Tproger
Отредактировано

Старт серии по созданию клона Space Invaders на C++: настраиваем окно и контекст OpenGL 3.3 с GLFW и GLEW, собираем проект и запускаем первый «красный» кадр.

258 открытий3К показов
Space Invaders «с нуля» — Часть 1, создаём окно

Это перевод статьи автора Nick Tasios.

Автор написал клон классической аркады Space Invaders на C++, опираясь всего на пару зависимостей. В этой части мы подготовим окно и контекст OpenGL 3.3, используя GLFW и GLEW — это единственные внешние библиотеки, которые понадобятся на старте.

Что такое Space Invaders

Space Invaders «с нуля» — Часть 1, создаём окно 1

Space Invaders — аркадная игра 1978 года. Это 2D-шутер с горизонтальным управлением: игрок двигает пушку вдоль нижней границы экрана и стреляет по строю инопланетян. За каждого сбитого врага начисляются очки. Периодически по верхней части экрана пролетает НЛО — если сбить, получите бонусные очки. По мере уничтожения инопланетян игра ускоряется. Враги тоже стреляют случайно, приближаясь к низу экрана; попадание по игроку отнимает жизнь. Пушку частично прикрывают бункеры, но они постепенно разрушаются как под выстрелами врагов, так и самим игроком. После зачистки волны появляется новая, а бункеры восстанавливаются. Игра заканчивается, если бункеры полностью разрушены, инопланетяне достигли низа экрана или у игрока закончились жизни.

Постановка целей

Важно определить цели до начала проекта. Мы не собираемся дотошно воссоздавать оригинал — сделаем «space-invaders-like» прототип с базовыми элементами. В геймдеве обычно сначала собирают «грубый» прототип, чтобы проверить ядро механик, а «полировать» будем позже.

Space Invaders «с нуля» — Часть 1, создаём окно 2

В прототипе нужны:

  • управляемая игроком пушка;
  • волны инопланетян, которые постепенно движутся к пушке;
  • стрельба у обеих сторон.

НЛО и бункеры на первом этапе опустим (их несложно добавить потом).

Почти любую игру можно разложить на базовые элементы (очень советую доклад Raph Koster). В Space Invaders это движение и стрельба (а значит — детекция столкновений). Даже простой клон поможет прокачать понимание геймлупа, коллизий и правил игры.

Поехали!

Hello Window

Окно можно создать по-разному: нативные API (Cocoa/X11/WinAPI) или кроссплатформенные библиотеки (Qt, GLFW). Нативный путь даёт полный контроль, но ради простоты и кроссплатформенности возьмём GLFW: лёгкая, простая C-API.

Подключим заголовки (стандартный ввод/вывод и GLFW):

			#include 
#include 
Сначала настроим обработчик ошибок. В GLFW события приходят через колбэки; колбэк ошибок задаётся так:
GLFWerrorfun glfwSetErrorCallback(GLFWerrorfun cbfun)
Сигнатура обработчика:
typedef void(*GLFWerrorfun)(int, const char *)
Первый аргумент — код ошибки, второй — описание (UTF-8). Простейший колбэк — печать в stderr:
void error_callback(int error, const char* description)
{
    fprintf(stderr, "Error: %s\n", description);
}
В main() передаём колбэк GLFW:
glfwSetErrorCallback(error_callback);

Инициализируем GLFW библиотеку:
if(!glfwInit())
{
    return -1;
}
Создаём окно:
window = glfwCreateWindow(640, 480, "Space Invaders", NULL, NULL);
if(!window)
{
    glfwTerminate();
    return -1;
}
glfwMakeContextCurrent(window);
		

Это откроет окно 640×480 с заголовком Space Invaders и контекстом OpenGL. Два последних параметра glfwCreateWindow — монитор для фуллскрина и «шаринг» контекста между окнами. При неудаче вызываем glfwTerminate(). Важно: нужно «привязать» контекст текущему потоку (glfwMakeContextCurrent), чтобы последующие вызовы OpenGL применялись к нему.

По умолчанию версия контекста не гарантируется — попросим минимум 3.3 Core (задать до создания окна):

			glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
		

Почему нужен загрузчик функций OpenGL. OpenGL — это спецификация; реализация зависит от GPU/драйвера/ОС. Множество функций нужно загружать в рантайме. Делать это вручную неудобно, поэтому используют лоадеры. Здесь — GLEW (можно и GLAD, но в статье выбран GLEW).

Важно: подключаем GLEW до glfw3.h:

			#include 
После «активации» контекста инициализируем GLEW:
GLenum err = glewInit();
if(err != GLEW_OK)
{
    fprintf(stderr, "Error initializing GLEW.\n");
    glfwTerminate();
    return -1;
}
Проверим версию OpenGL, которую реально получили:
int glVersion[2] = {-1, 1};
glGetIntegerv(GL_MAJOR_VERSION, &glVersion[0]);
glGetIntegerv(GL_MINOR_VERSION, &glVersion[1]);

printf("Using OpenGL: %d.%d\n", glVersion[0], glVersion[1]);
		

Игровой цикл (Game loop)

Если запустить код сейчас, окно мигнёт и программа завершится. Нужен бесконечный game loop, где мы обрабатываем ввод, обновляем состояние и рисуем кадр:

			glClearColor(1.0, 0.0, 0.0, 1.0);
while (!glfwWindowShouldClose(window))
{
    glClear(GL_COLOR_BUFFER_BIT);

    glfwSwapBuffers(window);

    glfwPollEvents();
}
		
  • Буферы. Современный OpenGL рисует в «задний» буфер, а «передний» отображается на экране. glfwSwapBuffers() меняет их местами.
  • События. glfwPollEvents() вынимает накопившиеся события (клавиатура, мышь, закрытие окна).
  • Выход. glfwWindowShouldClose() станет true, если пользователь нажал «крестик».

В конце корректно освобождаем ресурсы:

			glfwDestroyWindow(window);
glfwTerminate();
		

Компиляция

Ниже — команды из оригинала (C++11), плюс современные примечания.

			Linux (g++):
g++ -std=c++11 -o main -lglfw -lGLEW -lGL main.cpp
macOS (Xcode CLT):
g++ -std=c++11 -o main -lglfw -lglew -framework OpenGL main.cpp
		

Обратите внимание, что позже мы будем использовать некоторые функции C++11, поэтому компилируем с помощью -std=c++11. В обоих случаях убедитесь, что у вас установлен GLFW 3. В Linux, в зависимости от вашего дистрибутива, вы можете использовать менеджер пакетов. Например, в Ubuntu GLFW можно установить с помощью следующей команды:

			sudo apt install glfw3 glew
		

На Mac OS X автор предпочитает использовать Homebrew:

			brew install glfw glew
		

Возможно, эта статья поможет вам настроить проект GLFW в Visual Studio.

Если всё собрано успешно, вы увидите красное окно с заголовком Space Invaders.

Space Invaders «с нуля» — Часть 1, создаём окно 3

Итоги

Даже просто «поднять окно» с контекстом OpenGL на C++ — задача не из быстрых, несмотря на помощь GLFW. Мы пока ничего не рисуем; настройка простейшего рендера в современном OpenGL тоже требует подготовки. Хорошая новость — делать это придётся один раз, а дальше вы переиспользуете базу в следующих частях (шейдеры, VBO/VAO, спрайты, коллизии и т. д.).

Вторая часть статьи — здесь

Следите за новыми постами
Следите за новыми постами по любимым темам
258 открытий3К показов