【OpenGL】OpenGL游戏案例(二)

文章目录

    • 特殊效果
      • 数据结构
      • 生成逻辑
      • 更新逻辑
    • 文本渲染
      • 类结构
      • 构造函数
      • 加载函数
      • 渲染函数

特殊效果

为提高游戏的趣味性,在游戏中提供了六种特殊效果。

数据结构

PowerUp

类只存储存活数据,实际逻辑在游戏代码中通过Type字段来区分执行

class PowerUp : public GameObject
{
public:// powerup statestd::string Type;float       Duration;bool        Activated;// constructorPowerUp(std::string type, glm::vec3 color, float duration, glm::vec2 position, Texture2D texture): GameObject(position, POWERUP_SIZE, texture, color, VELOCITY), Type(type), Duration(duration), Activated() { }
};

Game中存在容器存储目前存活的PowerUp对象

std::vector<PowerUp>  PowerUps;

生成逻辑

当玩家消灭一个方块后,按固定概率随机生成或不生成一个随机能力的砖块。

//Game::DoCollision()// destroy block if not solid
if (!box.IsSolid)
{box.Destroyed = true;this->SpawnPowerUps(box);
}

生成策略

bool ShouldSpawn(unsigned int chance)
{unsigned int random = rand() % chance;return random == 0;
}
void Game::SpawnPowerUps(GameObject& block)
{if (ShouldSpawn(75)) // 1 in 75 chancethis->PowerUps.push_back(PowerUp("speed", glm::vec3(0.5f, 0.5f, 1.0f), 0.0f, block.Position, ResourceManager::GetTexture("powerup_speed")));if (ShouldSpawn(75))this->PowerUps.push_back(PowerUp("sticky", glm::vec3(1.0f, 0.5f, 1.0f), 20.0f, block.Position, ResourceManager::GetTexture("powerup_sticky")));if (ShouldSpawn(75))this->PowerUps.push_back(PowerUp("pass-through", glm::vec3(0.5f, 1.0f, 0.5f), 10.0f, block.Position, ResourceManager::GetTexture("powerup_passthrough")));if (ShouldSpawn(75))this->PowerUps.push_back(PowerUp("pad-size-increase", glm::vec3(1.0f, 0.6f, 0.4), 0.0f, block.Position, ResourceManager::GetTexture("powerup_increase")));if (ShouldSpawn(15)) // Negative powerups should spawn more oftenthis->PowerUps.push_back(PowerUp("confuse", glm::vec3(1.0f, 0.3f, 0.3f), 15.0f, block.Position, ResourceManager::GetTexture("powerup_confuse")));if (ShouldSpawn(15))this->PowerUps.push_back(PowerUp("chaos", glm::vec3(0.9f, 0.25f, 0.25f), 15.0f, block.Position, ResourceManager::GetTexture("powerup_chaos")));
}

更新逻辑

生成完后会在Update中调用以下函数下落更新,并每帧检查是否与玩家发生碰撞

不同状态下能力内部标志位变化:

DestoryedActivated
下落状态falsefalse
与玩家碰撞后truetrue
倒计时结束后truefalse

Destroyed决定是否要渲染其图像,Activated决定是否要启用其计时器,最后的状态则决定了是否要将其移除出列表

刚碰撞时根据Type字段执行附加能力的逻辑,倒计时结束后执行取消附加能力的逻辑。流程完毕后移除出数组

//绘制
for (PowerUp& powerUp : this->PowerUps)if (!powerUp.Destroyed)powerUp.Draw(*Renderer);//检查碰撞
for (PowerUp& powerUp : this->PowerUps)
{if (!powerUp.Destroyed){if (powerUp.Position.y >= this->Height)powerUp.Destroyed = true;if (CheckCollision(*Player, powerUp)){	// collided with player, now activate powerupActivatePowerUp(powerUp);powerUp.Destroyed = true;powerUp.Activated = true;}}
}//启用能力
void ActivatePowerUp(PowerUp& powerUp)
{if (powerUp.Type == "speed"){Ball->Velocity *= 1.2;}else if (powerUp.Type == "sticky"){Ball->Sticky = true;Player->Color = glm::vec3(1.0f, 0.5f, 1.0f);}else if (powerUp.Type == "pass-through"){Ball->PassThrough = true;Ball->Color = glm::vec3(1.0f, 0.5f, 0.5f);}else if (powerUp.Type == "pad-size-increase"){Player->Size.x += 50;}else if (powerUp.Type == "confuse"){if (!Effects->Chaos)Effects->Confuse = true; // only activate if chaos wasn't already active}else if (powerUp.Type == "chaos"){if (!Effects->Confuse)Effects->Chaos = true;}
}//每帧更新,轮询何时撤销能力
void Game::UpdatePowerUps(float dt)
{for (PowerUp& powerUp : this->PowerUps){powerUp.Position += powerUp.Velocity * dt;if (powerUp.Activated){powerUp.Duration -= dt;if (powerUp.Duration <= 0.0f){// remove powerup from list (will later be removed)powerUp.Activated = false;// deactivate effectsif (powerUp.Type == "sticky"){if (!IsOtherPowerUpActive(this->PowerUps, "sticky")){	// only reset if no other PowerUp of type sticky is activeBall->Sticky = false;Player->Color = glm::vec3(1.0f);}}else if (powerUp.Type == "pass-through"){if (!IsOtherPowerUpActive(this->PowerUps, "pass-through")){	// only reset if no other PowerUp of type pass-through is activeBall->PassThrough = false;Ball->Color = glm::vec3(1.0f);}}else if (powerUp.Type == "confuse"){if (!IsOtherPowerUpActive(this->PowerUps, "confuse")){	// only reset if no other PowerUp of type confuse is activeEffects->Confuse = false;}}else if (powerUp.Type == "chaos"){if (!IsOtherPowerUpActive(this->PowerUps, "chaos")){	// only reset if no other PowerUp of type chaos is activeEffects->Chaos = false;}}}}}// Remove all PowerUps from vector that are destroyed AND !activated (thus either off the map or finished)// Note we use a lambda expression to remove each PowerUp which is destroyed and not activatedthis->PowerUps.erase(std::remove_if(this->PowerUps.begin(), this->PowerUps.end(),[](const PowerUp& powerUp) { return powerUp.Destroyed && !powerUp.Activated; }), this->PowerUps.end());
}

文本渲染

文本渲染部分依赖于一个库FreeType,在项目中只拿来读取ttf生成像素数据,然后记录在自定义的Character结构中。

// 宽度、高度、左上角偏移、水平距离
struct Character
{unsigned int TextureID;glm::ivec2    Size;glm::ivec2    Bearing;unsigned int  Advance;
};

创建文字渲染器类TextRenderer来负责文字的渲染

类结构

class TextRenderer
{
public:// 预处理记录的需要的字符内容(借助FreeType)std::map<char, Character> Characters;// 文字渲染Shader,就是渲染一个四边形,然后采样纹理并渲染上去Shader TextShader;TextRenderer(unsigned int width, unsigned int height);void Load(std::string font, unsigned int fontSize);void RenderText(std::string text, float x, float y, float scale, glm::vec3 color = glm::vec3(1.0f));private:unsigned int VAO, VBO;
};

构造函数

构造函数中负责初始化shader参数,初始化VAO和VBO,但VBO的具体顶点数据会在绘制时动态计算出来

TextRenderer::TextRenderer(unsigned int width, unsigned int height)
{// load and configure shaderthis->TextShader = ResourceManager::LoadShader("res/shaders/text.vs", "res/shaders/text.frag", nullptr, "text");this->TextShader.SetMatrix4("projection", glm::ortho(0.0f, static_cast<float>(width), static_cast<float>(height), 0.0f), true);this->TextShader.SetInteger("text", 0);// configure VAO/VBO for texture quadsglGenVertexArrays(1, &this->VAO);glGenBuffers(1, &this->VBO);//绑定VAO,VBOglBindVertexArray(this->VAO);glBindBuffer(GL_ARRAY_BUFFER, this->VBO);//填充VBO数据glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 6 * 4, NULL, GL_DYNAMIC_DRAW);//规划VBO数据布局glEnableVertexAttribArray(0);glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 4 * sizeof(float), 0);//解绑glBindBuffer(GL_ARRAY_BUFFER, 0);glBindVertexArray(0);
}

加载函数

外部提供字体文件(.ttf)路径和字体大小,内部利用FreeType将前128字符转换为使用Characters存储

void TextRenderer::Load(std::string font, unsigned int fontSize)
{// 如果之前已经Load过,清除this->Characters.clear();// 初始化并加载 FreeType libraryFT_Library ft;if (FT_Init_FreeType(&ft))std::cout << "ERROR::FREETYPE: Could not init FreeType Library" << std::endl;// 将字符加载为faceFT_Face face;if (FT_New_Face(ft, font.c_str(), 0, &face))std::cout << "ERROR::FREETYPE: Failed to load font" << std::endl;// 设置加载字体的大小,这里是设置为 fontSize 参数指定的大小。第一个参数为 0,表示不限制宽度,第二个参数是目标字体大小FT_Set_Pixel_Sizes(face, 0, fontSize);// 关闭了 OpenGL 中的字节对齐限制,使得字节数据可以按 1 字节对齐,这样可以避免在纹理生成时出现内存填充问题glPixelStorei(GL_UNPACK_ALIGNMENT, 1);// 通过循环加载前 128 个 ASCII 字符。for (GLubyte c = 0; c < 128; c++){// FT_Load_Char 用来加载字符的字形信息,如果加载失败,则输出错误信息并跳过该字符if (FT_Load_Char(face, c, FT_LOAD_RENDER)){std::cout << "ERROR::FREETYTPE: Failed to load Glyph" << std::endl;continue;}// 为每个字符生成一个纹理unsigned int texture;glGenTextures(1, &texture);glBindTexture(GL_TEXTURE_2D, texture);//使用 GL_RED 格式来存储单通道的灰度数据glTexImage2D(GL_TEXTURE_2D,0,GL_RED,face->glyph->bitmap.width,face->glyph->bitmap.rows,0,GL_RED,GL_UNSIGNED_BYTE,face->glyph->bitmap.buffer);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);//将原始数据转换为Character数组Character character = {texture,glm::ivec2(face->glyph->bitmap.width, face->glyph->bitmap.rows),glm::ivec2(face->glyph->bitmap_left, face->glyph->bitmap_top),face->glyph->advance.x};Characters.insert(std::pair<char, Character>(c, character));}// 清理资源glBindTexture(GL_TEXTURE_2D, 0);FT_Done_Face(face);FT_Done_FreeType(ft);
}

渲染函数

  • 使用并设置文字shader,使用当前的VAO
  • 遍历所有提供的字符,在Characters中取出对应的数据
  • 计算位置长宽,更新顶点缓存,绑定记录的纹理
  • 绘制完后偏移光标位置
void TextRenderer::RenderText(std::string text, float x, float y, float scale, glm::vec3 color)
{// activate corresponding render state	this->TextShader.Use();this->TextShader.SetVector3f("textColor", color);glActiveTexture(GL_TEXTURE0);glBindVertexArray(this->VAO);// iterate through all charactersstd::string::const_iterator c;for (c = text.begin(); c != text.end(); c++){Character ch = Characters[*c];// 计算位置和长宽float xpos = x + ch.Bearing.x * scale;float ypos = y + (this->Characters['H'].Bearing.y - ch.Bearing.y) * scale;float w = ch.Size.x * scale;float h = ch.Size.y * scale;// 为每个字符更新对应的VBO缓存float vertices[6][4] = {{ xpos,     ypos + h,   0.0f, 1.0f },{ xpos + w, ypos,       1.0f, 0.0f },{ xpos,     ypos,       0.0f, 0.0f },{ xpos,     ypos + h,   0.0f, 1.0f },{ xpos + w, ypos + h,   1.0f, 1.0f },{ xpos + w, ypos,       1.0f, 0.0f }};// 绑定指定的纹理glBindTexture(GL_TEXTURE_2D, ch.TextureID);// update content of VBO memoryglBindBuffer(GL_ARRAY_BUFFER, this->VBO);glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices);glBindBuffer(GL_ARRAY_BUFFER, 0);// render quadglDrawArrays(GL_TRIANGLES, 0, 6);// 更新光标位置x += (ch.Advance >> 6) * scale; // bitshift by 6 to get value in pixels (1/64th times 2^6 = 64)}glBindVertexArray(0);glBindTexture(GL_TEXTURE_2D, 0);
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/894249.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【4Day创客实践入门教程】Day4 迈向高手之路——进一步学习!

Day4 迈向高手之路——进一步学习&#xff01; 目录 Day4 迈向高手之路——进一步学习&#xff01;更多的开发板外壳制作 Day0 创想启程——课程与项目预览Day1 工具箱构建——开发环境的构建Day2 探秘微控制器——单片机与MicroPython初步Day3 实战演练——桌面迷你番茄钟Day4…

深度学习之“缺失数据处理”

缺失值检测 缺失数据就是我们没有的数据。如果数据集是由向量表示的特征组成&#xff0c;那么缺失值可能表现为某些样本的一个或多个特征因为某些原因而没有测量的值。通常情况下&#xff0c;缺失值由特殊的编码方式。如果正常值都是正数&#xff0c;那么缺失值可能被标记为-1…

日志收集Day007

1.配置ES集群TLS认证: (1)elk101节点生成证书文件 cd /usr/share/elasticsearch ./bin/elasticsearch-certutil cert -out config/elastic-certificates.p12 -pass "" --days 3650 (2)elk101节点为证书文件修改属主和属组 chown elasticsearch:elasticsearch con…

arm-linux-gnueabihf安装

Linaro Releases windows下打开wsl2中的ubuntu&#xff0c;资源管理器中输入&#xff1a; \\wsl$gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf.tar.xz 复制到/home/ark01/tool 在 Ubuntu 中创建目录&#xff1a; /usr/local/arm&#xff0c;命令如下&#xff1a; …

LabVIEW透镜多参数自动检测系统

在现代制造业中&#xff0c;提升产品质量检测的自动化水平是提高生产效率和准确性的关键。本文介绍了一个基于LabVIEW的透镜多参数自动检测系统&#xff0c;该系统能够在单一工位上完成透镜的多项质量参数检测&#xff0c;并实现透镜的自动搬运与分选&#xff0c;极大地提升了检…

【算法】动态规划专题① ——线性DP python

目录 引入简单实现稍加变形举一反三实战演练总结 引入 楼梯有个台阶&#xff0c;每次可以一步上1阶或2阶。一共有多少种不同的上楼方法&#xff1f; 怎么去思考&#xff1f; 假设就只有1个台阶&#xff0c;走法只有&#xff1a;1 只有2台阶&#xff1a; 11&#xff0c;2 只有3台…

C++11(中)

新增默认成员函数 C11之前&#xff0c;默认成员函数有六个&#xff0c;构造函数&#xff0c;析构函数&#xff0c;拷贝构造&#xff0c;拷贝赋值重载&#xff0c;取地址重载&#xff0c;const 取地址重载。 C11增加了 移动构造 和 移动赋值重载 如果类没有实现移动构造&…

强化学习笔记——4策略迭代、值迭代、TD算法

基于策略迭代的贝尔曼方程和基于值迭代的贝尔曼方程&#xff0c;关系还是不太理解 首先梳理一下&#xff1a; 通过贝尔曼方程将强化学习转化为值迭代和策略迭代两种问题 求解上述两种贝尔曼方程有三种方法&#xff1a;DP&#xff08;有模型&#xff09;&#xff0c;MC&#xff…

计算机网络 笔记 网络层 3

IPv6 IPv6 是互联网协议第 6 版&#xff08;Internet Protocol Version 6&#xff09;的缩写&#xff0c;它是下一代互联网协议&#xff0c;旨在解决 IPv4 面临的一些问题&#xff0c;以下是关于 IPv6 的详细介绍&#xff1a; 产生背景&#xff1a; 随着互联网的迅速发展&…

【搜索回溯算法篇】:拓宽算法视野--BFS如何解决拓扑排序问题

✨感谢您阅读本篇文章&#xff0c;文章内容是个人学习笔记的整理&#xff0c;如果哪里有误的话还请您指正噢✨ ✨ 个人主页&#xff1a;余辉zmh–CSDN博客 ✨ 文章所属专栏&#xff1a;搜索回溯算法篇–CSDN博客 文章目录 一.广度优先搜索&#xff08;BFS&#xff09;解决拓扑排…

23.Word:小王-制作公司战略规划文档❗【5】

目录 NO1.2.3.4 NO5.6​ NO7.8.9​ NO10.11​ NO12​ NO13.14 NO1.2.3.4 布局→页面设置对话框→纸张&#xff1a;纸张大小&#xff1a;宽度/高度→页边距&#xff1a;上下左右→版式&#xff1a;页眉页脚→文档网格&#xff1a;勾选只指定行网格✔→ 每页&#xff1a;…

视频脚本生成器(基于openai API和streamlit)

utils.py&#xff1a; # 所有和ai交互的代码放进utils.py里&#xff08;utils 通常是 “utilities” 的缩写&#xff0c;意为 “实用工具” 或 “实用函数”&#xff09;from langchain.prompts import ChatPromptTemplate from langchain_openai import ChatOpenAI from lan…

Android --- CameraX讲解

预备知识 surface surfaceView SurfaceHolder surface 是什么&#xff1f; 一句话来说&#xff1a; surface是一块用于填充图像数据的内存。 surfaceView 是什么&#xff1f; 它是一个显示surface 的View。 在app中仍在 ViewHierachy 中&#xff0c;但在wms 中可以理解为…

Longformer:处理长文档的Transformer模型

Longformer&#xff1a;处理长文档的Transformer模型 摘要 基于Transformer的模型由于自注意力操作的二次复杂度&#xff0c;无法处理长序列。为了解决这一限制&#xff0c;我们引入了Longformer&#xff0c;其注意力机制与序列长度呈线性关系&#xff0c;使其能够轻松处理数…

python学opencv|读取图像(五十二)使用cv.matchTemplate()函数实现最佳图像匹配

【1】引言 前序学习了图像的常规读取和基本按位操作技巧&#xff0c;相关文章包括且不限于&#xff1a; python学opencv|读取图像-CSDN博客 python学opencv|读取图像&#xff08;四十九&#xff09;原理探究&#xff1a;使用cv2.bitwise()系列函数实现图像按位运算-CSDN博客…

MySQL为什么默认引擎是InnoDB ?

大家好&#xff0c;我是锋哥。今天分享关于【MySQL为什么默认引擎是InnoDB &#xff1f;】面试题。希望对大家有帮助&#xff1b; MySQL为什么默认引擎是InnoDB &#xff1f; 1000道 互联网大厂Java工程师 精选面试题-Java资源分享网 MySQL 默认引擎是 InnoDB&#xff0c;主要…

蓝桥杯真题k倍区间

题目如下 代码解析&#xff1a; 成功AC

python项目之requirements.txt文件

Python项目中可以包含一个 requirements.txt 文件&#xff0c;用于记录所有依赖包及其精确的版本号用以新环境部署。 当我们开发新项目的时候&#xff0c;会用virtualenv创建很多python独立环境&#xff0c;这时候就会出现在不同环境下安装相同的模块的情况&#xff0c;这时候…

算法题(53):对称二叉树

审题&#xff1a; 需要我们判断二叉树是否满足对称结构&#xff0c;并返回判断结果 思路&#xff1a; 方法一&#xff1a;递归 其实是否对称分成两部分判断 第一部分&#xff1a;根节点是否相等 第二部分&#xff1a;根节点一的左子树和根节点二的右子树是否相等&#xff0c;根…

使用 cmake

使用前注意 : CMake是一种跨平台的构建系统&#xff0c;它用于管理软件构建过程&#xff0c;尤其适合多语言、多配置的项目。CMake不直接构建软件&#xff0c;而是生成特定构建工具&#xff08;如Makefile或Visual Studio项目&#xff09;所需的配置文件。 如果仅仅使用 qt 编…