天选集结号活动中心

天选集结号活动中心

shape
  • Home
  • 展会直击
  • 记录自己从零开发2D游戏引擎的过程(0)

记录自己从零开发2D游戏引擎的过程(0)

  • 2026-02-10 07:17:40
  • admin

1.起因

一直对游戏制作很感兴趣,本来想趁暑假的时间制作一个游戏,但是尝试了几个游戏引擎之后感觉操作很复杂,于是就想自己写一个游戏引擎。因为之前曾经做过类似的努力,然而都半途而废了,所以就开一个博客专门记录自己开发游戏引擎的过程,以此激励自己。

2.开发准备

2.1编译环境

由于大家玩PC游戏主要是在Windows平台游玩的,所以要在Windows上配置编译环境。然而这个过程不如想象中顺利。

由于不想用几十GB的Visual Studio,故选择了MinGW+vscode作为开发工具。但是MinGW安装程序因为网络原因一直卡死,反复试了几次也不成功。所以去安装了msys2,再用msys2安装了MinGW,然后又配置了半天环境变量,终于可以在Windows下编译C++了。

编译器安装好了,然而编辑器又出问题了,vscode的代码提示功能一直用不了,上网查也不知道什么原因。于是放弃了,纯手打代码也不费多少时间。

2.2库

我选择了glad+glfw作为图形渲染的库,box2d作为物理引擎,libpng作为png图片的解析库。分别下载之后编译,因为前期环境配置好了,还是比较顺利的。因为使用这些库的原因,这个项目就暂定为GLBox2D。

3.Hello World

首先复制glfw官网的源码,然后粘贴编译运行,发现运行不了。

上网查发现是因为没有读取GL的函数指针,然而没有找到gladLoadGLLoader和GLADloadproc,凭自己感觉把它们改成了gladLoadGL和GLADloadfunc,居然改对了。然后发现没有加framebuffer size callback,遂加上。

void framebufferSizeCallback(GLFWwindow* window,int w,int h){

glViewport(0,0,w,h);

}

glfwSetFramebufferSizeCallback(window, framebufferSizeCallback);

gladLoadGL((GLADloadfunc)glfwGetProcAddress);

编译运行,窗口正常显示,这是一个好的开始!

4.对glfw的初步封装

为了调用方便,把glfw里的一些东西封装到一个类里面。

这里我就直接贴代码了。目前只封装了暂时用的上的接口,其他的比如纹理渲染等以后再加上。OpenGL初学者,写的很渣,希望不要喷。

gb2graphics.h

#ifndef __GB2_GRAPHICS_H

#define __GB2_GRAPHICS_H

#include

#include

struct GB2VertexArray{

unsigned int type;

unsigned int vao;

unsigned int vbo;

float vertices[4096];

float groups[1024];

unsigned int vertex_count;

unsigned int vertex_increment_count;

unsigned int group_count;

};

class GB2Graphics{

public:

GB2Graphics();

int Init(const char * title);

void CreateVertexArray(GB2VertexArray * p_vertex_array, unsigned int type);

void VAVertex(GB2VertexArray * p_vertex_array, float x, float y);

void VAEnd(GB2VertexArray * p_vertex_array);

void VAClear(GB2VertexArray * p_vertex_array);

void DrawVertexArray(GB2VertexArray * p_vertex_array);

void DeleteVertexArray(GB2VertexArray * p_vertex_array);

void Clear();

void Flush();

bool GetKey(unsigned int key);

bool WillContinue();

private:

unsigned int m_shader;

GLFWwindow* m_window;

};

#endif

gb2graphics.cpp

#include "gb2graphics.h"

void framebufferSizeCallback(GLFWwindow* window,int w,int h){

glViewport(0,0,w,h);

}

GB2Graphics::GB2Graphics(){

}

int GB2Graphics::Init(const char * title){

if(!glfwInit())

return -1;

glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR,3);

glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR,3);

glfwWindowHint(GLFW_OPENGL_PROFILE,GLFW_OPENGL_CORE_PROFILE);

m_window = glfwCreateWindow(1024, 768, title, nullptr, nullptr);

if(!m_window){

glfwTerminate();

return -1;

}

glfwMakeContextCurrent(m_window);

glfwSetFramebufferSizeCallback(m_window, framebufferSizeCallback);

gladLoadGL((GLADloadfunc)glfwGetProcAddress);

m_shader = glCreateProgram();

const char * str_vertex_shader =

"#version 330 core\n"

"layout (location = 0) in vec4 position;\n"

"void main()\n"

"{\n"

"gl_Position = position;\n"

"}\0";

const char * str_fragment_shader =

"#version 330 core\n"

"in vec4 in_color;\n"

"out vec4 color;\n"

"void main()\n"

"{\n"

"color=vec4(in_color.r*0.4,in_color.g*0.3,in_color.b*0.2,in_color.a);\n"

"}\0";

unsigned int vs = glCreateShader(GL_VERTEX_SHADER);

glShaderSource(vs, 1, &str_vertex_shader, nullptr);

glCompileShader(vs);

unsigned int fs = glCreateShader(GL_FRAGMENT_SHADER);

glShaderSource(fs, 1, &str_fragment_shader, nullptr);

glCompileShader(fs);

glAttachShader(m_shader, vs);

glAttachShader(m_shader, fs);

glLinkProgram(m_shader);

glValidateProgram(m_shader);

glDeleteShader(vs);

glDeleteShader(fs);

return 0;

}

void GB2Graphics::CreateVertexArray(GB2VertexArray * p_vertex_array,unsigned int type){

glGenVertexArrays(1, &p_vertex_array->vao);

glGenBuffers(1, &p_vertex_array->vbo);

glBindVertexArray(p_vertex_array->vao);

glBindBuffer(GL_ARRAY_BUFFER, p_vertex_array->vbo);

glVertexAttribPointer(0, 2, GL_FLOAT, 0, 0, 0);

glEnableVertexAttribArray(0);

VAClear(p_vertex_array);

p_vertex_array->type = type;

}

void GB2Graphics::VAVertex(GB2VertexArray * p_vertex_array, float x, float y){

int w, h;

glfwGetFramebufferSize(m_window, &w, &h);

if(w>h){

x = x*h/w;

}else{

y = y*w/h;

}

p_vertex_array->vertices[p_vertex_array->vertex_count++] = x;

p_vertex_array->vertices[p_vertex_array->vertex_count++] = y;

p_vertex_array->vertex_increment_count ++;

}

void GB2Graphics::VAEnd(GB2VertexArray * p_vertex_array){

p_vertex_array->groups[p_vertex_array->group_count++] = p_vertex_array->vertex_increment_count;

p_vertex_array->vertex_increment_count = 0;

}

void GB2Graphics::VAClear(GB2VertexArray * p_vertex_array){

p_vertex_array->vertex_count = 0;

p_vertex_array->group_count = 0;

p_vertex_array->vertex_increment_count = 0;

}

void GB2Graphics::DrawVertexArray(GB2VertexArray * p_vertex_array){

if(p_vertex_array->group_count == 0) return;

glBindVertexArray(p_vertex_array->vao);

glBindBuffer(GL_ARRAY_BUFFER, p_vertex_array->vbo);

glBufferData(GL_ARRAY_BUFFER,

p_vertex_array->vertex_count*sizeof(float), p_vertex_array->vertices, GL_STATIC_DRAW);

glUseProgram(m_shader);

unsigned int count = 0;

for(int i=0;igroup_count;++i){

glDrawArrays(GL_LINE_LOOP, count, p_vertex_array->groups[i]);

count += p_vertex_array->groups[i];

}

}

void GB2Graphics::DeleteVertexArray(GB2VertexArray * p_vertex_array){

glDeleteVertexArrays(1, &p_vertex_array->vao);

glDeleteBuffers(1, &p_vertex_array->vbo);

}

void GB2Graphics::Clear(){

glClearColor(0.0f, 0.0f, 0.0f, 1.0f);

glClear(GL_COLOR_BUFFER_BIT);

}

void GB2Graphics::Flush(){

glfwSwapBuffers(m_window);

glfwPollEvents();

}

bool GB2Graphics::GetKey(unsigned int key){

return glfwGetKey(m_window, key) == GLFW_PRESS;

}

bool GB2Graphics::WillContinue(){

return !glfwWindowShouldClose(m_window);

}

5.Box2D的使用

Box2D是一个以MIT许可证发行的开源的2D物理引擎,据说被很多游戏引擎使用。

Box2D是用C++写的,包含很多封装好的类,感觉自己再封装一遍的意义不大,所以就直接用它提供的类。

为了测试一下Box2D运行的效果,写了一个测试文件

test.cpp

#include "gb2graphics.h"

#include

#include

#include

b2Body * AddBlock(b2World * p_world, float x, float y, float hw, float hh, bool is_dynamic){

b2BodyDef bodyDef;

if(is_dynamic)

bodyDef.type = b2_dynamicBody;

bodyDef.position.Set(x, y);

b2Body * p_body = p_world->CreateBody(&bodyDef);

b2PolygonShape box;

box.SetAsBox(hw, hh);

b2FixtureDef fixtureDef;

fixtureDef.shape = &box;

if(is_dynamic){

fixtureDef.density = 1.0f;

fixtureDef.friction = 0.3f;

}

p_body->CreateFixture(&fixtureDef);

return p_body;

}

void SetVelocity(b2Body * p_body, const b2Vec2& direction, float strength){

b2Vec2 v = p_body->GetLinearVelocity();

b2Vec2 v2 = direction;

v2.Normalize();

float d = strength - b2Dot(v, v2);

v = v + d * v2;

p_body->SetLinearVelocity(v);

}

int main(){

GB2Graphics * p_graphics = new GB2Graphics();

if(p_graphics->Init("Test GlBox2d") == -1){

return -1;

}

GB2VertexArray va;

p_graphics->CreateVertexArray(&va, GL_LINE_LOOP);

b2Vec2 gravity(0.0f, -10.0f);

b2World * p_world = new b2World(gravity);

AddBlock(p_world, 0.0f, -10.0f, 50.0f, 10.0f, false);

b2Body * p_player = AddBlock(p_world, 0.0f, 4.0f, 1.0f, 1.0f, true);

b2Transform camera_transform;

camera_transform.SetIdentity();

float scale = 0.05;

std::chrono::duration frameTime(0.0);

std::chrono::duration sleepAdjust(0.0);

while(p_graphics->WillContinue()){

std::chrono::steady_clock::time_point t1 = std::chrono::steady_clock::now();

p_world->Step(1.0f/60.0f, 6, 2);

p_graphics->VAClear(&va);

for(b2Body * p_body = p_world->GetBodyList();p_body;p_body = p_body->GetNext()){

for(b2Fixture * p_fixture = p_body->GetFixtureList();p_fixture;p_fixture = p_fixture->GetNext()){

if(p_fixture->GetType() == b2Shape::e_polygon){

b2PolygonShape * p_polygon_shape = (b2PolygonShape*)p_fixture->GetShape();

b2Transform xf = p_body->GetTransform();

for(int i=0;im_count;++i){

b2Vec2 v = p_polygon_shape->m_vertices[i];

v = b2Mul(xf, v);

v = scale * b2Mul(camera_transform, v);

p_graphics->VAVertex(&va, v.x, v.y);

}

p_graphics->VAEnd(&va);

}

}

}

b2Vec2 dv = camera_transform.p + p_player->GetPosition();

if(dv.Length()>1.0f)

dv.Normalize();

dv = 0.5 * dv;

camera_transform.p = camera_transform.p - dv;

b2Vec2 up(0.0f, 1.0f);

b2Vec2 right(1.0f, 0.0f);

if(p_graphics->GetKey(GLFW_KEY_UP)){

SetVelocity(p_player, up, 10.0f);

}

if(p_graphics->GetKey(GLFW_KEY_LEFT)){

SetVelocity(p_player, right, -10.0f);

}

if(p_graphics->GetKey(GLFW_KEY_RIGHT)){

SetVelocity(p_player, right, 10.0f);

}

if(p_graphics->GetKey(GLFW_KEY_DOWN)){

SetVelocity(p_player, up, -10.0f);

}

p_graphics->Clear();

p_graphics->DrawVertexArray(&va);

p_graphics->Flush();

std::chrono::steady_clock::time_point t2 = std::chrono::steady_clock::now();

std::chrono::duration target(1.0 / 60.0);

std::chrono::duration timeUsed = t2 - t1;

std::chrono::duration sleepTime = target - timeUsed + sleepAdjust;

if (sleepTime > std::chrono::duration(0))

{

std::this_thread::sleep_for(sleepTime);

}

}

delete p_world;

}

运行,可以看到活动的小方块和静止的大方块,可以控制小方块的移动。

接下来的任务就是加入碰撞侦听的功能,以及关卡文件的设计。

<<<
Previous Post
追平韩日世界杯记录,本届世界杯定位球得分已达56次

Copyright © 2088 天选集结号活动中心 All Rights Reserved.

友情链接