利用OpenGL设计贪吃蛇游戏
文章目录
任务介绍
- 贪吃蛇游戏:玩家控制贪吃蛇在游戏区域里驰骋,避免碰到自己或障碍物,尽可能地吃更多的食物以生长!
游戏玩法
- WASD控制蛇的移动
- 游戏开始,会在地图空闲位置刷新一个食物,蛇触碰到食物后食物消失,食物会重新刷新,分数增加,蛇会增加一个单位的长度
- 当蛇触碰到自己,则游戏失败
- 当蛇接触到地图边界,蛇会在地图另一端重新进入地图
开发环境
- OpenGL3
- GLFW
- IMGUI
游戏实现
贪吃蛇游戏的框架搭建
主程序
对GLFW进行初始化,创建游戏对象,创建GUI,对用户的输入进行传递。主要是在一个 while 循环中,进行对游戏对象的更新与渲染。
//渲染
while (!glfwWindowShouldClose(window))
{
// 开启深度测试
glEnable(GL_DEPTH_TEST);
// 清空深度缓存
glClear(GL_DEPTH_BUFFER_BIT);
//处理用户输入
processInput(window);
// Start the Dear ImGui frame
ImGui_ImplOpenGL3_NewFrame();
ImGui_ImplGlfw_NewFrame();
ImGui::NewFrame();
// 计算每一次渲染相差的时间
GLfloat currentFrame = glfwGetTime();
deltaTime = currentFrame - lastFrame;
lastFrame = currentFrame;
std::string score = "Score : " + std::to_string(Snake.score);
const char *scorechar = score.c_str();
// 创建gui
ImGui::Begin("Gluttonous Snake");
// 显示分数或者游戏结束
if (Snake.State == GAME_WIN)
{
ImGui::Text("Game Over!!!!!!");
ImGui::Text(scorechar);
}
else
{
ImGui::Text(scorechar);
}
ImGui::End();
// Rendering
ImGui::Render();
// 游戏处理用户输入
Snake.ProcessInput(deltaTime);
// 更新游戏的状态
Snake.Update(deltaTime);
//设置清空屏幕所用的颜色
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
//清除颜色缓冲
glClear(GL_COLOR_BUFFER_BIT);
// 渲染游戏
Snake.Render((float)glfwGetTime());
//渲染gui
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
//交换颜色缓冲
glfwSwapBuffers(window);
//检查IO事件
glfwPollEvents();
}
游戏类
维护游戏状态,提供更新状态的接口供主程序调用,进行碰撞检测等
// 游戏的状态
enum GameState {
GAME_ACTIVE,
GAME_MENU,
GAME_WIN
};
class Game
{
public:
// 游戏状态
GameState State;
GLuint Width, Height;
// 游戏分数
int score;
Game(GLuint width, GLuint height);
~Game();
// 初始化游戏加载shader等
void Init();
void ProcessInput(GLfloat dt);
void Update(GLfloat dt);
void Render(float rotateRad);
void ProcessMouseMovement(float xoffset, float yoffset, bool constrainPitch);
// 键盘状态
bool pressW;
bool pressS;
bool pressA;
bool pressD;
void ResetPress();
// 移动蛇
void MoveTheSnack();
// 碰撞检测
void DoCollisions();
};
游戏对象类
维护游戏对象的状态,在本次游戏中就是蛇头与蛇身、以及生成的食物
class GameObject
{
public:
// 状态
glm::vec3 Position, Size;
GLfloat Rotation;
GameObject();
// 设置位置
void setPosition(glm::vec3 pos);
private:
GLuint gameobjectVAO;
};
工具类
管理着色器、纹理的加载,本次游戏中没有用到纹理于是只有着色器的加载
class ResourceManager
{
public:
// Resource storage
static std::map<std::string, Shader*> Shaders;
// Loads (and generates) a shader program from file loading vertex, fragment (and geometry) shader's source code. If gShaderFile is not nullptr, it also loads a geometry shader
static Shader * LoadShader(const GLchar *vShaderFile, const GLchar *fShaderFile, const GLchar *gShaderFile, std::string name);
// Retrieves a stored sader
static Shader * GetShader(std::string name);
// Properly de-allocates all loaded resources
static void Clear();
private:
// Private constructor, that is we do not want any actual resource manager objects. Its members and functions should be publicly available (static).
ResourceManager() {
}
// Loads and generates a shader from file
static Shader * loadShaderFromFile(const GLchar *vShaderFile, const GLchar *fShaderFile, const GLchar *gShaderFile = nullptr);
};
着色器类
用于对单个的着色器进行初始化,并修改里面的值
class Shader
{
public:
// State
GLuint ID;
// Constructor
Shader() {
}
// Sets the current shader as active
Shader &Use();
// Compiles the shader from given source code
void Compile(const GLchar *vertexSource, const GLchar *fragmentSource, const GLchar *geometrySource = nullptr)