【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中调用以下函数下落更新,并每帧检查是否与玩家发生碰撞
不同状态下能力内部标志位变化:
| Destoryed | Activated | |
|---|---|---|
| 下落状态 | false | false |
| 与玩家碰撞后 | true | true |
| 倒计时结束后 | true | false |
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);
}
相关文章:
【OpenGL】OpenGL游戏案例(二)
文章目录 特殊效果数据结构生成逻辑更新逻辑 文本渲染类结构构造函数加载函数渲染函数 特殊效果 为提高游戏的趣味性,在游戏中提供了六种特殊效果。 数据结构 PowerUp 类只存储存活数据,实际逻辑在游戏代码中通过Type字段来区分执行 class PowerUp …...
DeepSeek大模型技术深度解析:揭开Transformer架构的神秘面纱
摘要 DeepSeek大模型由北京深度求索人工智能基础技术研究有限公司开发,基于Transformer架构,具备卓越的自然语言理解和生成能力。该模型能够高效处理智能对话、文本生成和语义理解等复杂任务,标志着人工智能在自然语言处理领域的重大进展。 关…...
DeepSeek本地版安装简易教程(windows)
第一步:下载 第二步:安装 先安装ollama,安装完毕保持ollama运行,设置ollama通过防火墙,再安装deepseek,7b代表下载的r1版本,版本越高消耗资源越大 第三步:开放windows防火墙 第四步…...
RK3568使用QT搭建TCP服务器和客户端
文章目录 一、让RK3568开发板先连接上wifi二、客户端代码1. `widget.h` 文件2. `widget.cpp` 文件**详细讲解**1. **`Widget` 类构造函数 (`Widget::Widget`)**2. **UI 布局 (`setupUI`)**3. **连接按钮的槽函数 (`onConnectClicked`)**4. **发送消息按钮的槽函数 (`onSendMess…...
Python爬虫之——Cookie存储器
目录 专栏导读1、背景介绍2、库的安装3、核心代码4、完整代码总结 专栏导读 🌸 欢迎来到Python办公自动化专栏—Python处理办公问题,解放您的双手 🏳️🌈 博客主页:请点击——> 一晌小贪欢的博客主页求关注 &…...
理解动手学深度学习的自编包d2l
跟着李沐的《动手学深度学习-PyTorch版》入门Python编程和Pytorch框架,以前是重度Matlab用户,对于Python里的各种包很不习惯。特别是,本书还自己做了一个名为d2l包,有几个问题很是困惑。今天终于弄明白了,写在这里&…...
大语言模型(LLM)模拟金融市场参与者行为
大语言模型(LLM)模拟金融市场参与者行为 研究背景 传统深度学习模型通过识别市场数据历史模式预测市场,但未捕捉个体决策过程。LLM 虽能学习人类对不同提示的反应,但在模拟金融市场参与者时面临挑战:个体投资者不总是理性决策,LLM 可能无法捕捉;LLM 数值和金融知识可靠…...
蓝桥杯刷题DAY1:前缀和
所谓刷题,讲究的就是细心 帕鲁服务器崩坏【算法赛】 “那个帕鲁我已经观察你很久了,我对你是有些失望的,进了这个营地,不是把事情做好就可以的,你需要有体系化思考的能力。” 《幻兽帕鲁》火遍全网,成为…...
Hive:窗口函数(1)
窗口函数 窗口函数OVER()用于定义一个窗口,该窗口指定了函数应用的数据范围 对窗口数据进行分区 partition by 必须和over () 一起使用, distribute by经常和sort by 一起使用,可以不和over() 一起使用.DISTRIBUTE BY决定了数据如何分布到不同的Reducer上…...
OpenCV:SIFT关键点检测与描述子计算
目录 1. 什么是 SIFT? 2. SIFT 的核心步骤 2.1 尺度空间构建 2.2 关键点检测与精细化 2.3 方向分配 2.4 计算特征描述子 3. OpenCV SIFT API 介绍 3.1 cv2.SIFT_create() 3.2 sift.detect() 3.3 sift.compute() 3.4 sift.detectAndCompute() 4. SIFT 关…...
爬虫基础(一)HTTP协议 :请求与响应
前言 爬虫需要基础知识,HTTP协议只是个开始,除此之外还有很多,我们慢慢来记录。 今天的HTTP协议,会有助于我们更好的了解网络。 一、什么是HTTP协议 (1)定义 HTTP(超文本传输协议ÿ…...
视频拼接,拼接时长版本
目录 视频较长,分辨率较大,这个效果很好,不耗用内存 ffmpeg imageio,适合视频较短 视频较长,分辨率较大,这个效果很好,不耗用内存 ffmpeg import subprocess import glob import os from nats…...
【字符串两大注意事项】
表达字符串的方式 1.双引号:"hello world" 2.字符指针:char* ptr "hello world" 3.字符数组:char arr[] "hello world"辨析 项目表示方式代表含义内存分布1“hello world”字符串字面量字符串常量就是数据…...
【4Day创客实践入门教程】Day1 工具箱构建——开发环境的构建
Day1 工具箱构建——开发环境的构建 目录 Day1 工具箱构建——开发环境的构建1.元件选型2.准备工具3. 开发板准备焊接排针具体步骤注意事项与技巧 4. 软件环境配置与固件烧录Thonny IDE软件环境配置配置Micropython环境与烧录固件**问题:**买的是4M/16M,…...
如何让一个用户具备创建审批流程的权限
最近碰到一个问题,两个sandbox,照理用户的权限应该是一样的,结果开发环境里面我可以左右的做各种管理工作,但是使用change set上传后,另一个环境的同一个用户,没有相对于的权限,权限不足。 当时…...
Docker Hello World
Docker Hello World 引言 Docker 是一个开源的应用容器引擎,可以让开发者打包他们的应用以及应用的依赖包到一个可移植的容器中,然后发布到任何流行的 Linux 机器上,也可以实现虚拟化。本文将带领您从零开始,学习如何使用 Docker 运行一个简单的 "Hello World"…...
本地部署DeepSeek开源多模态大模型Janus-Pro-7B实操
本地部署DeepSeek开源多模态大模型Janus-Pro-7B实操 Janus-Pro-7B介绍 Janus-Pro-7B 是由 DeepSeek 开发的多模态 AI 模型,它在理解和生成方面取得了显著的进步。这意味着它不仅可以处理文本,还可以处理图像等其他模态的信息。 模型主要特点:Permalink…...
ITS290F Human Computer Interaction
ITS290F Human Computer Interaction & User Experience Design Lab 1. Introduction to CodePen What you’ll learn in this lab: • Understanding CodePen • Creating a front-end page • Using Google form to submit your lab work CodePen is a cloud-based in…...
【详细教程】如何在Mac部署Deepseek R1?
DeepSeek是目前最火的国产大模型,官方App用户太多服务经常出现卡顿,部署一个本地DeepSeek R1可以方便使用。 1.系统最低要求 macOS 11 Big Sur 或更新 2.下载ollama https://ollama.com/ 3.安装DeepSeek R1 打开终端 运行命令 ollama run deepseek-…...
DeepSeek能下围棋吗?(续)
休息了一下,接着琢磨围棋,其实前面一篇里的规则有个漏洞的,就是邻居关系定义有问题,先回顾一下游戏规则: 游戏规则 定义: 1.数字对,是指两个1到9之间的整数组成的有序集合。可与记为(m,n)&…...
网络安全技术简介
网络安全技术简介 随着信息技术的迅猛发展,互联网已经成为人们日常生活和工作中不可或缺的一部分。与此同时,网络安全问题也日益凸显,成为全球关注的焦点。无论是个人隐私泄露、企业数据被盗取还是国家信息安全受到威胁,都与网络…...
Keepalived高可用集群企业应用实例一
一、实现master/slave的keepalived单主架构 1.master配置 global_defs { notification_email { 2676401238qq.com } notification_email_from keepalivedKA1.xiao.org smtp_server 127.0.0.1 smtp_connect_timeout 30 router_id ka1.xiao.org vrrp_skip_check_adv_addr vrr…...
愿景:做机器视觉行业的颠覆者
一个愿景,两场战斗,专注制胜。 一个愿景:做机器视觉行业的颠覆者。 我给自己创业,立一个大的愿景:做机器视觉行业的颠覆者。 两场战斗:无监督-大模型 上半场,无监督。2025-2030,共五…...
【产品经理学习案例——AI翻译棒出海业务】
前言: 本文主要讲述了硬件产品在出海过程中,翻译质量、翻译速度和本地化落地策略是硬件产品规划需要考虑的核心因素。针对不同国家,需要优化翻译质量和算法,关注市场需求和文化差异,以便更好地满足当地用户的需求。同…...
算法总结-数组/字符串
文章目录 1.合并两个有序数组1.答案2.思路 2.移除元素1.答案2.思路 3.删除有序数组中的重复项 II1.答案2.思路 4.多数元素1.答案2.思路 5.轮转数组1.答案2.思路 6.买卖股票的最佳时机1.答案2.思路 7.买卖股票的最佳时机 II1.答案2.思路 8.跳跃游戏1.答案2.思路 9.H 指数1.答案2…...
被裁与人生的意义--春节随想
还有两个月就要被迫离开工作了十多年的公司了,不过有幸安安稳稳的过了一个春节,很知足! 我是最后一批要离开的,一百多号同事都没“活到”蛇年。看着一批批仁人志士被“秋后斩首”,马上轮到我们十来个,个中滋味很难言清…...
Linux解决输入法卡死问题
说明:在Ubuntu系统中,如果您需要重启输入法服务(比如fcitx或ibus),您可以按照以下步骤操作。这些步骤适用于大多数基于Ubuntu的发行版,例如Ubuntu、Linux Mint等。 一、重启Fcitx输入法服务 1、使用Ctrl …...
2501,编写dll
DLL的优点 简单的说,dll有以下几个优点: 1)节省内存.同一个软件模块,若是源码重用,则会在不同可执行程序中编译,同时运行这些exe时,会在内存中重复加载这些模块的二进制码. 如果使用dll,则只在内存中加载一次,所有使用该dll的进程会共享此块内存(当然,每个进程会复制一份的d…...
4-图像梯度计算
文章目录 4.图像梯度计算(1)Sobel算子(2)梯度计算方法(3)Scharr与Laplacian算子4.图像梯度计算 (1)Sobel算子 图像梯度-Sobel算子 Sobel算子是一种经典的图像边缘检测算子,广泛应用于图像处理和计算机视觉领域。以下是关于Sobel算子的详细介绍: 基本原理 Sobel算子…...
【算法设计与分析】实验5:贪心算法—装载及背包问题
目录 一、实验目的 二、实验环境 三、实验内容 四、核心代码 五、记录与处理 六、思考与总结 七、完整报告和成果文件提取链接 一、实验目的 掌握贪心算法求解问题的思想;针对不同问题,会利用贪心算法进行问题建模、求解以及时间复杂度分析&#x…...
