用 opengl 写一个小游戏 (1)

本文介绍了使用OpenGL开发小游戏的环境搭建过程,包括freetype、SOIL和glm库的集成,以及注意事项。接着,阐述了游戏开发中的基本组件——着色器(shader)和纹理(texture)的封装,并提供了资源加载类的设计。最后,提到了简单的渲染器和主游戏类的概要,为后续的游戏开发奠定基础。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

用 opengl 写一个小游戏 (1)

本节代码 github

环境搭建

基本的环境搭建可以参考我之前的文章在 Eclipse或CLion 中集成 opengl 环境 (windows+mingw)
在这里我们还需要另外两个包,freetype 和 soil。

freetype

FreeType 库是一个完全免费(开源)的、高质量的且可移植的字体引擎,它提供统一的接口来访问多种字体格式文件。我们可以借助它实现文字渲染。可在此网站下载源代码或直接下载二进制文件。

soil

SOIL是简易OpenGL图像库(Simple OpenGL Image Library)的缩写,它支持大多数流行的图像格式,使用起来也很简单。它可以帮助我们在openGL项目中加载图片以实现纹理。这是SOIL库的主页

glm

由于C/C++标准库中没有几何数学库,这样造成在开发一个三维系统之初往往都需要自行实现一个实用的几何数学库,这样太费时费力了。GLM的出现可以很好的解决这个问题。GLM设计上遵照OpenGL Shading Language风格,使用开放的MIT授权协议。会GLSL的人可以很快上手。因采用了数据结构与函数方法分离的方式,可以很容易扩充函数方法而不改变原文件(增加新的头文件即可,不过得在不同的头文件中找函数方法比较费力)。
下载地址
注意这个库不需要 link。只要要包含头文件即可

注意事项

注意 link 时的顺序,有些包的前后顺序不能倒置。我的顺序是

target_link_libraries(game glew32s  glfw3 gdi32 freetype soil opengl32)

game 是游戏程序。后面是需要用到的包。

基本组件

在写游戏的过程中我们需要频繁的使用两个部分:shader 和 texture,即着色器和纹理。我们可以将其封装为两个类。

shader

.h 文件

#ifndef GAME_SHADER_H
#define GAME_SHADER_H

#include <string>

#define GLEW_STATIC
#include <GL/glew.h>
#include <glm/glm.hpp>
#include <glm/gtc/type_ptr.hpp>

class Shader{
public:
    GLuint id;

    //构造函数
    Shader(){};

    //使用此程序
    Shader &use();

    //编译 shader 代码, 可选的 geometry shader
    void    Compile(const GLchar *vertexSource, const GLchar *fragmentSource, const GLchar *geometrySource = nullptr);

    //设置参数
    void    SetFloat    (const GLchar *name, GLfloat value, GLboolean useShader = false);
    void    SetInteger  (const GLchar *name, GLint value, GLboolean useShader = false);
    void    SetVector2f (const GLchar *name, GLfloat x, GLfloat y, GLboolean useShader = false);
    void    SetVector2f (const GLchar *name, const glm::vec2 &value, GLboolean useShader = false);
    void    SetVector3f (const GLchar *name, GLfloat x, GLfloat y, GLfloat z, GLboolean useShader = false);
    void    SetVector3f (const GLchar *name, const glm::vec3 &value, GLboolean useShader = false);
    void    SetVector4f (const GLchar *name, GLfloat x, GLfloat y, GLfloat z, GLfloat w, GLboolean useShader = false);
    void    SetVector4f (const GLchar *name, const glm::vec4 &value, GLboolean useShader = false);
    void    SetMatrix4  (const GLchar *name, const glm::mat4 &matrix, GLboolean useShader = false);
private:
    // 检查编译的错误
    void    checkCompileErrors(GLuint object, std::string type);
};
#endif //GAME_SHADER_H

.cpp 文件

#include "shader.h"

#include <iostream>

Shader &Shader::use()
{
    glewExperimental = GL_TRUE;
    glewInit();
    glUseProgram(this->id);
    return *this;
}

void Shader::Compile(const GLchar* vertexSource, const GLchar* fragmentSource, const GLchar* geometrySource)
{
    GLuint sVertex, sFragment, gShader;
    // Vertex Shader
    sVertex = glCreateShader(GL_VERTEX_SHADER);
    glShaderSource(sVertex, 1, &vertexSource, NULL);
    glCompileShader(sVertex);
    checkCompileErrors(sVertex, "VERTEX");
    // Fragment Shader
    sFragment = glCreateShader(GL_FRAGMENT_SHADER);
    glShaderSource(sFragment, 1, &fragmentSource, NULL);
    glCompileShader(sFragment);
    checkCompileErrors(sFragment, "FRAGMENT");
    // Geometry Shader (可选)
    if (geometrySource != nullptr)
    {
        gShader = glCreateShader(GL_GEOMETRY_SHADER);
        glShaderSource(gShader, 1, &geometrySource, NULL);
        glCompileShader(gShader);
        checkCompileErrors(gShader, "GEOMETRY");
    }
    // Shader Program
    this->id = glCreateProgram();
    glAttachShader(this->id, sVertex);
    glAttachShader(this->id, sFragment);
    if (geometrySource != nullptr)
        glAttachShader(this->id, gShader);
    glLinkProgram(this->id);
    checkCompileErrors(this->id, "PROGRAM");
    // 删除 shader 当 link 成功时
    glDeleteShader(sVertex);
    glDeleteShader(sFragment);
    if (geometrySource != nullptr)
        glDeleteShader(gShader);
}

void Shader::SetFloat(const GLchar *name, GLfloat value, GLboolean useShader)
{
    if (useShader)
        this->use();
    glUniform1f(glGetUniformLocation(this->id, name), value);
}
void Shader::SetInteger(const GLchar *name, GLint value, GLboolean useShader)
{
    if (useShader)
        this->use();
    glUniform1i(glGetUniformLocation(this->id, name), value);
}
void Shader::SetVector2f(
作者对游戏的说明: 首先,您应当以一种批判的眼光来看待本程序。这个游戏是我制作 的第一部RPG游戏,无任何经验可谈,完全按照自己对游戏的理解进 行设计的。当我参照了《圣剑英雄2》的源码之后,才体会到专业游 戏引擎的博大精深。 该程序的内核大约有2000余行,能够处理人物的行走、对话、战斗, 等等。由于该程序的结构并不适于这种规模的程序,故不推荐您详 细研究该程序。所附地图编辑器的源程序我已经添加了详细的注释, 其程序结构也比较合理,可以作为初学VC的例子。 该程序在VC的程序向导所生成的SDI框架的基础上修改而成。它没有 使用任何关于VC底层的东西。程序的绝大部分都是在CgameView类中 制作的,只有修改窗口特征的一段代码在CMainFrm类中。其他的类 统统没有用到。另外添加的一个类是CEnemy类。 整个游戏的故事情节分成8段,分别由Para1.h ~ Para8.h八个文件 实现。由于程序仅仅能够被动的处理各种各样的消息,所以情节的 实现也只能根据系统的一些参数来判断当前应当什么。在程序中 使用了冗长的if……else if……结构来实现这种判断。 当然,在我的记录本上,详细的记录了每个事件的判断条件。这种 笨拙的设计当然是不可取的。成都金点所作《圣剑英雄II》采用了 剧本解读的方式,这才是正统的法。但这也需要更多的编程经验 和熟练的code功夫。 下面列举的是程序编制过程中总结出来的经验和教训。 第一,对话方式应该采用《圣剑英雄II》的剧本方式。 现在的方式把一个段落中所有的对话都混在一个文件中,然后给每 句话一个号码相对应。这样虽然降低了引擎的难度,却导致剧情的 编极其繁琐。 第二,运动和显示应当完全分开。 现在的程序中,运动和显示是完全同步的。即:在定时器中调用所有 敌人的运动函数,然后将主角的动画向前推一帧,接着绘制地图,调 用所有敌人的显示函数、重绘主角。这样的好处是不会掉帧,但带来 的问题是,如果要提高敌人的运动速度,那么帧数也跟着上去了。所 以当DEMO版反馈说速度太慢的时候,我修改起来非常困难。而这个问 题到最后也仅仅是将4步一格该成了2步一格。 第三,VC中数组存在上限。如果用“int aaa[1000000000]”定义一个 数组,编译器肯定不会给分配那么大的内存空间。而在这个程序中, 地图矩阵、NPC矩阵都超过了VC中数组的上限。但这一点知道的太晚了。 在1.0版本中已经发现地图最右端缺少了几行,但不知道是什么原因 造成的。(地图编辑器中未出现此问题,因为地图编辑器是用“序列 化”的方式存盘读盘的。)解决这个问题的方法是用“new”来分配 内存空间。 第四,由于不知道应该如何使用“new”和“delete”,几乎所有的DC 都使用了全局变量。这是完全没有必要的。程序运行期大约会耗用20 多M的内存空间,相当于一个大型游戏所使用的内存空间了。 另外,在游戏的剧情、美工方面也有许多问题,总之一个词“业余”。 我就不总结了。下一部作品,我将争取在程序上有一个质的飞跃。
### 回答1: 下面是一个使用OpenGL小游戏的基本代码:#include <GL/glut.h>int main(int argc, char* argv[]) { glutInit(&argc, argv); glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB); glutInitWindowSize(500, 500); glutCreateWindow("Game"); glutDisplayFunc(draw); glutKeyboardFunc(keyboard); glutMainLoop(); return 0; }void draw() { glClear(GL_COLOR_BUFFER_BIT) // 绘制3D场景 glFlush(); }void keyboard(unsigned char key, int x, int y) { switch (key) { case 'a': // 一些事情 break; case 'b': // 一些事情 break; } } ### 回答2: OpenGL一个开放源代码的图形库,用于渲染2D和3D图形。使用OpenGL小游戏代码需要掌握OpenGL的基本知识和一些游戏开发的概念。 以下是一个OpenGL的简单小游戏代码的示例: ```c++ #include <GL/glut.h> // 引入GLUT库 void display() { glClear(GL_COLOR_BUFFER_BIT); // 清空颜色缓存区 // 绘制场景 glBegin(GL_QUADS); // 绘制四边形 glColor3f(1.0, 0.0, 0.0); // 设置颜色为红色 glVertex2f(-0.5, -0.5); // 设置顶点坐标 glVertex2f(0.5, -0.5); glVertex2f(0.5, 0.5); glVertex2f(-0.5, 0.5); glEnd(); // 结束绘制 glFlush(); // 刷新显示 } int main(int argc, char** argv) { glutInit(&argc, argv); // 初始化GLUT库 glutCreateWindow("OpenGL游戏"); // 创建窗口,设置标题 glutDisplayFunc(display); // 注册绘制函数 glutMainLoop(); // 进入GLUT事件处理循环 return 0; } ``` 这个示例代码创建了一个窗口,并在窗口中绘制一个红色的四边形。`display`函数负责绘制场景,`glClear`函数清空颜色缓存区,`glBegin`和`glEnd`函数用于指定绘制的图形形状,`glVertex2f`函数设置顶点坐标,`glColor3f`函数设置颜色,`glFlush`函数刷新显示。`main`函数中调用了一系列GLUT函数来初始化窗口和事件处理循环。 这只是一个非常简单的例子,实际编小游戏代码时可能需要更加复杂的逻辑和绘制函数。在实际开发中,还需要学习和掌握OpenGL的其他功能,如纹理映射、光照、投影变换等,以实现更丰富的游戏效果。 ### 回答3: OpenGL是一款开源的图形库,可以方便地进行图形渲染和交互式应用程序的开发。下面是一个使用OpenGL的简单小游戏的代码示例: 首先,需要包含OpenGL的头文件和一些其他必要的头文件: ```c++ #include <GL/glut.h> #include <math.h> ``` 然后,定义一些全局变量: ```c++ float angle = 0.0f; // 控制旋转角度 float x = 0.0f; // 控制矩形位置 float y = 0.0f; float delta = 0.01f; // 矩形运动速度 ``` 接下来,编绘制场景的函数: ```c++ void drawScene() { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // 清除颜色缓冲区和深度缓冲区 glLoadIdentity(); // 重置变换矩阵 glTranslatef(x, y, -5.0f); // 平移矩形到指定位置 glRotatef(angle, 0.0f, 0.0f, 1.0f); // 绕z轴旋转矩形 glColor3f(1.0f, 0.0f, 0.0f); // 设置矩形颜色为红色 glBegin(GL_QUADS); // 绘制四边形 glVertex2f(-0.5f, -0.5f); glVertex2f(0.5f, -0.5f); glVertex2f(0.5f, 0.5f); glVertex2f(-0.5f, 0.5f); glEnd(); glutSwapBuffers(); // 交换前后缓冲区 } ``` 然后,编更新函数,控制矩形的运动和旋转角度的改变: ```c++ void update(int value) { // 更新矩形位置 x += delta; if (x > 1.0f || x < -1.0f) { delta = -delta; } // 更新旋转角度 angle += 1.0f; if (angle > 360.0f) { angle -= 360.0f; } glutPostRedisplay(); // 重新绘制场景 glutTimerFunc(16, update, 0); // 固定时间间隔更新 } ``` 最后,编主函数,进行初始化和注册回调函数: ```c++ int main(int argc, char** argv) { glutInit(&argc, argv); // 初始化GLUT glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH); // 设置显示模式 glutInitWindowSize(800, 600); // 设置窗口大小 glutInitWindowPosition(100, 100); // 设置窗口位置 glutCreateWindow("OpenGL小游戏"); // 创建窗口,窗口标题为"OpenGL小游戏" glEnable(GL_DEPTH_TEST); // 启用深度测试 glutDisplayFunc(drawScene); // 注册绘制函数 glutTimerFunc(16, update, 0); // 注册更新函数 glutMainLoop(); // 进入事件循环 return 0; } ``` 这个示例代码实现了一个简单的小游戏,场景中有一个可以旋转和移动的红色矩形,矩形会在窗口边界反弹。通过定时器,更新函数会按固定时间间隔更新矩形的位置和旋转角度,并重新绘制场景。这样就完成了一个基本的使用OpenGL小游戏
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值