Notice: Function _load_textdomain_just_in_time was called incorrectly. Translation loading for the antispam-bee domain was triggered too early. This is usually an indicator for some code in the plugin or theme running too early. Translations should be loaded at the init action or later. Please see Debugging in WordPress for more information. (This message was added in version 6.7.0.) in /home/ruwritingagame/public_html/wp-includes/functions.php on line 6121
Глава 5. Cross-platform, Windows – Игра в Написание Игры

Глава 5. Cross-platform, Windows

В этой главе мы попробуем “препарировать” наш GLFW пример с треугольником. Мы выведем “игровой” код (который готовит и рисует треугольник) в отдельный класс, который потом попробуем использовать и на Android-е. Чтобы код был platform-independent (независимым) мы отделим его от platform-specific вызовов. Весь environment-related код, типа создание окна, инициализация GL-а, и т.д., мы оставим в main.cpp как есть.

  1. Запускаем Visual Studio, открываем C:\CPP\a999hello\p_windows\p_windows.sln solution.

2. Под p_windows проеком добавим новый фильтр.

Right-click на p_windows project -> Add -> New Filter. Name – xTheGame


3. Под xTheGame создадим новый класс. Мы не будем использовать “add Class” поскольку это расположит файлы на свое усмотрение, не там где нам надо. Лучше создадим его по-файлово.

Right-click на xTheGame -> Add -> New Item,

  • Header File (.h)
  • Name – TheGame.h
  • Поменяем location на C:\CPP\a999hello\

Add.

TheGame класс будет состоять из 5-и функций и 3-х переменных, собранных в TheGame.h.

Copy-paste следующий код в TheGame.h:

#pragma once
class TheGame
{
public:
	int screenSize[2];
	float screenRatio;
	bool bExitGame;
public:
	int run();
	int getReady();
	int drawFrame();
	int cleanUp();
	int onScreenResize(int width, int height);
};


4. Теперь – реализация:

Right-click на xTheGame -> Add -> New Item,

  • C++ File (.cpp)
  • Name – TheGame.cpp
  • Location – C:\CPP\a999hello\

Add.

Код:

#include "TheGame.h"
#include "platform.h"
#include "linmath.h"

static const struct
{
    float x, y;
    float r, g, b;
} vertices[3] =
{
    { -0.6f, -0.4f, 1.f, 0.f, 0.f },
    {  0.6f, -0.4f, 0.f, 1.f, 0.f },
    {   0.f,  0.6f, 0.f, 0.f, 1.f }
};

static const char* vertex_shader_text =
"#version 320 es\n"
"precision lowp float;\n"
"uniform mat4 MVP;\n"
"in vec3 vCol;\n"
"in vec2 vPos;\n"
"out vec3 color;\n"
"void main()\n"
"{\n"
"    gl_Position = MVP * vec4(vPos, 0.0, 1.0);\n"
"    color = vCol;\n"
"}\n";

static const char* fragment_shader_text =
"#version 320 es\n"
"precision lowp float;\n"
"in vec3 color;\n"
"out vec4 FragColor;\n"
"void main()\n"
"{\n"
"    FragColor = vec4(color, 1.0);\n"
"}\n";

unsigned int vao_buffer, vertex_buffer, vertex_shader, fragment_shader, program;
int mvp_location, vpos_location, vcol_location;
float angle_z = 0;

int TheGame::getReady() {
    bExitGame = false;

    glGenVertexArrays(1, &vao_buffer);
    glBindVertexArray(vao_buffer);

    glGenBuffers(1, &vertex_buffer);
    glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

    vertex_shader = glCreateShader(GL_VERTEX_SHADER);
    glShaderSource(vertex_shader, 1, &vertex_shader_text, NULL);
    glCompileShader(vertex_shader);

    fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);
    glShaderSource(fragment_shader, 1, &fragment_shader_text, NULL);
    glCompileShader(fragment_shader);

    program = glCreateProgram();
    glAttachShader(program, vertex_shader);
    glAttachShader(program, fragment_shader);
    glLinkProgram(program);

    mvp_location = glGetUniformLocation(program, "MVP");

    vpos_location = glGetAttribLocation(program, "vPos");
    vcol_location = glGetAttribLocation(program, "vCol");

    glEnableVertexAttribArray(vpos_location);
    glVertexAttribPointer(vpos_location, 2, GL_FLOAT, GL_FALSE,
        sizeof(vertices[0]), (void*)0);
    glEnableVertexAttribArray(vcol_location);
    glVertexAttribPointer(vcol_location, 3, GL_FLOAT, GL_FALSE,
        sizeof(vertices[0]), (void*)(sizeof(float) * 2));

    return 1;
}
int TheGame::drawFrame() {
    myPollEvents();

    mat4x4 m, p, mvp;

    glClear(GL_COLOR_BUFFER_BIT);
    angle_z += 0.01f;
    mat4x4_identity(m);
    mat4x4_rotate_Z(m, m, angle_z);
    mat4x4_ortho(p, -screenRatio, screenRatio, -1.f, 1.f, 1.f, -1.f);
    mat4x4_mul(mvp, p, m);

    glUseProgram(program);
    glUniformMatrix4fv(mvp_location, 1, GL_FALSE, (const GLfloat*)mvp);
    glDrawArrays(GL_TRIANGLES, 0, 3);

    mySwapBuffers();
    return 1;
}
int TheGame::cleanUp() {
    return 1;
}
int TheGame::onScreenResize(int width, int height) {
    if (screenSize[0] == width && screenSize[1] == height)
        return 0;
    screenSize[0] = width;
    screenSize[1] = height;
    screenRatio = (float)width / (float)height;
    glViewport(0, 0, width, height);
    mylog(" screen size %d x %d\n", width, height);
    return 1;
}
int TheGame::run() {
    getReady();
    while (!bExitGame) {
        drawFrame();
    }
    cleanUp();
    return 1;
}

  • Заметим, что в коде нет никаких platform-specific ссылок (как на GLAD или GLFW), что означает что его можно использовать и на других платформах, в частности – на Android-е, что мы непременно и попробуем в следующей главе.

5. Также (как обычно) надо оповестить p_windows проект, где искать TheGame.h.

Right-click на p_windows project -> Properties, All Configurations, Win32, Configuration Properties -> C/C++ -> General, открываем Additional Include Directories -> Edit, добавляем новую строку.

ВАЖНО: На этот раз вместо перехода на C:\CPP\a999hello (где TheGame и расположен), вручную добавим

..

Да, это просто 2 точки, что означает 1 уровень вверх (от корневого каталога p_windows проекта).

Ok, Apply, Ok.


6. Как и планировали, расположим platform-specific код вне TheGame класса. В частности, ссылки на GLFW и GLAD, обработку событий и вызов swap screen buffers.

Итак, откроем platform.h и заменим код следующим:

#pragma once
#include <glad/glad.h>

void mylog(const char* _Format, ...);
void mySwapBuffers();
void myPollEvents();

  • Заодно убрем mylog(..) из .h в .cpp

7. Реализация:

Right-click на xPlatform -> Add -> New Item,

  • C++ File (.cpp)
  • Name – platform.cpp
  • Location – C:\CPP\p_windows\

Add.

Код:

#include <stdarg.h>
#include <stdio.h>
#include <GLFW/glfw3.h>
#include "platform.h"
#include "TheGame.h"

extern GLFWwindow* myMainWindow;
extern TheGame theGame;

void mylog(const char* _Format, ...) {
#ifdef _DEBUG
    va_list _ArgList;
    va_start(_ArgList, _Format);
    vprintf(_Format, _ArgList);
    va_end(_ArgList);
#endif
};
void mySwapBuffers() {
    glfwSwapBuffers(myMainWindow);
}
void myPollEvents() {
    glfwPollEvents();
    //check if closing the window
    theGame.bExitGame = glfwWindowShouldClose(myMainWindow);
    //check screen size
    int width, height;
    glfwGetFramebufferSize(myMainWindow, &width, &height);
    theGame.onScreenResize(width, height);
}


8. main.cpp, соответственно, теперь намного короче:

#include <glad/glad.h>
#define GLFW_INCLUDE_NONE
#include <GLFW/glfw3.h>

#include <stdlib.h>

#include "TheGame.h"
#include "platform.h"

static void error_callback(int error, const char* description)
{
    mylog("Error: %s\n", description);
}

static void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods)
{
    if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
        glfwSetWindowShouldClose(window, GLFW_TRUE);
}

TheGame theGame;
GLFWwindow* myMainWindow;

int main(void)
{
    glfwSetErrorCallback(error_callback);

    if (!glfwInit())
        exit(EXIT_FAILURE);

    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);

    myMainWindow = glfwCreateWindow(640, 480, "Simple example", NULL, NULL);
    if (!myMainWindow)
    {
        glfwTerminate();
        exit(EXIT_FAILURE);
    }

    glfwSetKeyCallback(myMainWindow, key_callback);

    glfwMakeContextCurrent(myMainWindow);
    gladLoadGLES2Loader((GLADloadproc)glfwGetProcAddress); //gladLoadGL(glfwGetProcAddress);
    glfwSwapInterval(1);

    theGame.run();

    glfwDestroyWindow(myMainWindow);
    glfwTerminate();
    exit(EXIT_SUCCESS);
}

Заменим код в main.cpp на этот.

  • Заметим, что этот код НЕ содержит никаких game-specific функций, что означает, что мы сможем использовать его как есть и в других наших будущих Windows проектах.

9. Запускаем (зеленая стрелка). Работает!

  • Можно также запустить его в Release конфигурации, тогда будет без окна Консоли.

Наша следующая задача – запустить это на Android-е.


Leave a Reply

Your email address will not be published. Required fields are marked *