当前位置: 首页 > ds >正文

C++“类吸血鬼幸存者”游戏制作的要点学习

古之学者必有师,对于技术的提升,只靠自己的摸索虽然能得到深刻的经验,但往往没有较高的效率。笔者这些天学习了BV1eM4m1S74K“提瓦特幸存者”的C++开发,也是实现了该类型游戏的开发。

今天,就通过经验总结,亲手结束这一小段学习过程!


游戏的基本框架

所有游戏的底层框架都是通过一个主循环来刷新画布,每个循环中都实现读取操作、处理数据、绘制画面这三个步骤。

int main() {while (running) {//动态延时(记录本次循环开始的时间)DWORD start_time = GetTickCount();  //32位无符号整数,长度不随编译器变化//读取操作区域while (peekmessage(&msg)) {}//处理数据区域cleardevice();//渲染画面区域FlushBatchDraw();//记录循环结束时间DWORD end_time = GetTickCount();DWORD delta_time = end_time - start_time;//1秒144次刷新,即帧率为144,一个循环就是一帧if (delta_time < 1000 / 144) {Sleep(1000 / 144 - delta_time);}}EndBatchDraw();return 0;
}

在这些操作执行完后,加上动态延时从而实现游戏开发中“帧”的概念。这也是游戏引擎设计的基本框架,是不是很有Update()函数的感觉?

关于渲染缓冲区:

为了隐藏渲染或其他处理的过程,BeginBatchDraw函数会让后续的渲染操作在缓冲区中进行,像一块闲置的画布,当调用FlushBatchDraw或EndBatchDraw,即所有操作绘制结束时,直接代替当前画布,达到无缝衔接的效果。

一些代码细节:

视频的创作者在许多地方都体现了C++的编码细节,比如使用static静态变量只在第一次调用时创建(后续调用会跳过)复用同一内存;使用TCHAR这一Windows兼容类型适应非英文环境;同样使用宽字符串适应非英文环境……

头文件依赖问题

在这次C++开发的过程中,我总是被所谓“出现未定义的变量名”给搞到破防。这是头文件重复包含导致的。

一个项目各个区域的执行顺序是全局变量->静态全局变量->函数声明->静态函数声明->类定义->静态类成员定义->命名空间变量->函数定义……头文件一般用来包含变量与函数的声明、类的定义等重要信息。

头文件的循环包含引发编译错误的原因:

编译器处理 #include 时,会把对应头文件内容嵌入包含位置。若头文件循环包含,其可能会陷入无限递归尝试展开头文件的情形。即便使用包含守卫( #ifndef 三件套 )或 #pragma once 规避重复展开,由于头文件解析时需要对方类型完成自身声明或定义,循环依赖会导致部分必要的声明或定义无法在依赖解析阶段正确处理。

类似A.h在第一行包含了B.h,同时B.h的第一行包含了A.h, B类使用的A类的定义,但是在头文件依赖解析A.h时,先展开B.h,但B.h遍历到A.h会因为包含守卫而跳过,这下B类里A就没有了定义……

这里有三个编程习惯可以尽量避免头文件重复包含的问题:

1、使用前向声明,在使用A的头文件中声明声明一下A类。不需要访问具体成员时需要。

2、使用包含守卫,即 #ifndef、#define、#endif 连招。

3、使用 #pragma once,可替代包含守卫,使头文件在一个编译单元中只包含一次。

但这些方法只能尽量避免我们遇到的问题,最重要的还是在一开始就规划好项目的结构。保持头文件声明、源文件定义的好习惯,在必要时进行重构,让文件包含的脉络清晰,一目了然。

动画与MCI工具

游戏中使用的动画分为骨骼动画(关键帧动画)与序列帧动画。序列帧动画就是让图片素材以若干个帧为单位进行交替,从而达到动画播放的效果。

这里使用自定义图集类来批量载入名称有规律的图片素材。

Atlas::Atlas(LPCTSTR path, int num) {TCHAR path_file[256];for (size_t i = 0; i < num; ++i) {_stprintf_s(path_file, path, i);IMAGE* frame = new IMAGE();loadimage(frame, path_file);frame_list.push_back(frame);}
}

这种方法依次用从零开始的自然数代替图集中的数字部分,实现图片载入。

MCI工具(媒体控制接口)能够让我们以字符串的形式对windows系统发出指令,控制音乐的播放。

但在我的测试中,mp3文件在播放时,会明显影响游戏的帧率。经过测试与查询,这与MP3格式文件的特性有关:编解码边播放。而MCI解码的消息可能会打断Sleep,让主循环提前醒来,导致帧率变高。

所以在加载音频文件时,更推荐使用WAV格式:1、MCI 加载 MP3 资源时,底层会创建额外的线程或窗口,并且会向主消息队列发送消息(比如 MM_MCINOTIFY),EasyX 的 peekmessage 也会处理这些消息。2、WAV 文件是无压缩格式,处理简单,不会影响主线程;而 MP3 需要解码,可能会影响主线程的消息分发和定时精度。3、某些 Windows 环境下,MCI 加载MP3会让Sleep变得不准确,主循环实际刷新频率变高。


小结

虽然本篇图文列出的点很少,但是这次学习经历切切实实加深了游戏开发的理解。我想这些框架性的东西也可能成为游戏引擎开发的一个开端,而通过C++而不是依赖引擎的开发,更能深入底层逻辑,让日后对代码的优化的方向更清晰。

如有补充纠正欢迎留言。

http://www.xdnf.cn/news/19631.html

相关文章:

  • 计算机毕设推荐:基于python的农产品价格数据分析与预测的可视化系统的设计与实现 基于Python农产品管理系统【源码+文档+调试】
  • 前后端联合实现多个文件上传
  • Java全栈开发面试实录:从基础到微服务架构的深度解析
  • Python 基础综合与实践教案:密码验证、循环、分支条件、图形绘制
  • ReconDreamer++
  • Polkadot - ELVES
  • 你的数据是如何被保护的?
  • 解决浏览器的**混合内容安全策略**(Mixed Content Security Policy)带来的无法访问页面
  • 联合体Union
  • Backroom:信息代币化 AI 时代数据冗杂的解决方案
  • 【系统分析师】高分论文:论原型法及其在系统开发中的应用
  • 【Proteus仿真】按键控制系列仿真——LED灯表示按键状态/按键控制LED灯/4*4矩阵键盘控制LED
  • 部署在windows的docker中的dify知识库存储位置
  • NMOS概述
  • python---类.函数名(self) 和 self.函数名()的调用方式
  • 数据结构 二叉树
  • RocketMQ5.0+保姆级单点Docker部署教程
  • 暴力破解基础知识(一)
  • 深入解析 Oracle 并发与锁机制:高并发环境下的数据一致性之道
  • 【数论】P10558 [ICPC 2024 Xi‘an I] XOR Game|普及+
  • 深度学习导论:从理论起源到前沿应用与挑战
  • Halcon学习--(1)常用算子
  • 大模型RAG项目实战:向量数据库Faiss
  • 蓓韵安禧活性叶酸源于上市企业生产
  • 手写MyBatis第44弹:解密MyBatis四大核心组件拦截之道
  • 【influxdb】InfluxDB 2.x 线性写入详解
  • 【IDE问题篇】新电脑安装Keil5,出现找不到arm 编译器版本5编译报错;改为版本6后旧代码编译是出现编译报错
  • 自然语言处理NLP:嵌入层Embedding中input_dim的计算——Tokenizer文本分词和编码
  • android中常见布局及其约束
  • 超越关键词:RAG系统如何破解用户查询的“模糊密码”