- 概述
简要介绍本题目的内容,目标。
目标:实现纵板射击游戏:飞机大战(planes_army)
内容: 1、完成地图展示、方向控制,敌我子弹发射,碰撞检测等基本射击游戏元素
2、支持游戏存盘和读盘功能
3、成绩排行榜功能
4、游戏难易程度调节
5、游戏音乐与音效
- 所要完成的任务说明
序号 | 功能分类 | 任务 |
---|---|---|
1 | 必做 | 完成地图展示、方向控制,敌我子弹发射,碰撞检测等基本射击游戏元素 |
2 | 必做 | 支持游戏存盘和读盘功能 |
3 | 必做 | 成绩排行榜功能 |
4 | 选作 | 1、游戏难易程度调节 2、游戏音乐与音效 |
- 完成情况
功能序号 | 完成情况说明 |
---|---|
1 | 基本完成 |
2 | 基本完成 |
3 | 基本完成 |
4 | 基本完成 |
- 开发环境说明
简要介绍自己所用的开发环境,包括操作系统,开发工具等。
操作系统:Windows11
开发工具:Visual Studio Visual Studio Code
Easyx图形库
Windows API接口程序
-
四**.**程序分析与设计
-
系统分析
以下任意要求在文档中缺少,不得分
要求1:创建了哪些函数,对函数分别进行详细说明。 说明要包含:
- 函数原型;
- 函数的功能,如果有较为复杂的算法也需要说明;
- 各参数的意义;
- 函数的返回值类型
(从主函数中依次出现的自定义函数和自定义函数中出现的函数进行说明)
编号:001 | int main() |
---|---|
函数功能 | 程序运行主函数 |
参数说明 | 可以不填 |
函数返回值 | Int型:程序运行结果 |
编号:002 | void data_initialize() |
---|---|
函数功能 | 分数数据初始化: 获取list.txt文件中的分数不进行排序(按照从大到小的顺序) 将数据储存在全局变量:numbers[]数组中 用来获取TOP10的分数情况 |
参数说明 | 无 |
函数返回值 | void |
编号:003 | void load() |
---|---|
函数功能 | 将图片音乐等素材载入到程序中 |
参数说明 | 无 |
函数返回值 | void |
编号:004 | void show() |
---|---|
函数功能 | 展示背景,玩家飞机,敌军飞机,玩家子弹,敌机子弹 |
参数说明 | 无 |
函数返回值 | void |
编号:005 | void Auto_Update() |
---|---|
函数功能 | 自动处理函数: 玩家子弹位移, 敌机子弹位移, 敌机位移, (敌机的速度和敌机子弹的速度都是每20分增加一个档次) 玩家飞机碰撞敌机判断, 玩家子弹击中敌机判断, 敌机子弹击中玩家判断, 玩家飞机爆炸音乐加载 |
参数说明 | 无 |
函数返回值 | void |
编号:006 | void Update_Input() |
---|---|
函数功能 | 玩家飞机爆炸判断, 按键信息获取:作用于玩家飞机移动, 空格按键判断:是否发射子弹, 打开玩家子弹发射音乐 |
参数说明 | 无 |
函数返回值 | void |
编号:007 | void cleardevide() |
---|---|
函数功能 | 刷新打开的图形窗口的图片内容,防止出现重影 |
参数说明 | 无 |
函数返回值 | void |
编号:008 | void show()(第二次到达该函数的时候为飞机爆炸的时候) |
---|---|
函数功能 | 展示飞机爆炸图片 |
参数说明 | 无 |
函数返回值 | void |
编号:009 | void Sleep(DWORD milliseconds) |
---|---|
函数功能 | 在 Windows 系统下用来使线程暂停一段时间 |
参数说明 | 毫秒数 |
函数返回值 | void |
编号:010 | void GetAsyncKeyState(VK_ESCAPE) |
---|---|
函数功能 | Windows API 中的一个函数,用于获取指定虚拟键的状态 |
参数说明 | GetAsyncKeyState(VK_ESCAPE) & 0x8000 是一个按位与运算,用于获取 VK_ESCAPE 对应虚拟键码的键盘状态。其中 VK_ESCAPE 表示 Esc 键,是一个预定义常量 GetAsyncKeyState 返回值的最高位为 1,表示对应虚拟键按下,按位与运算结果为 0x8000。否则,返回值最高位为 0,按位与运算结果为 0。 GetAsyncKeyState(VK_ESCAPE) & 0x8000 这个表达式的作用是获取当前 Esc 键的按键状态。如果返回值为 0x8000,则表示 Esc 键处于按下状态;否则表示 Esc 键未被按下 |
函数返回值 | SHORT 类型的值,用于表示指定虚拟键的状态 |
编号:011 | FILE *fopen(const char *filename, const char *mode) |
---|---|
函数功能 | 文件操作 |
参数说明 | const char *filename:要打开的文件名 const char *mode:文件打开格式: "r": 以只读方式打开文件 "w": 以写入方式打开文件,如果文件存在,则截断文件为零长度,如果文件不存在则创建一个新文件 "a": 以追加方式打开文件,如果文件不存在则创建一个新文件 "r+": 以读写方式打开文件,打开后文件指针位于文件开头 "w+": 以读写方式打开文件,如果文件存在,则截断文件为零长度;如果文件不存在则创建一个新文件 "a+": 以读取和追加方式打开文件,如果文件不存在则创建一个新文件 |
函数返回值 | 文件指针(FILE*) |
编号:012 | int fprintf(FILE*stream,const char *format, ...); |
---|---|
函数功能 | 函数是 C 标准库中用于将格式化数据输出到文件的函数 |
参数说明 | 第一个参数 stream 是要输出的文件流,可以是 stdout、stderr 或使用 fopen 函数打开的文件;第二个参数 format 是格式化字符串,指定输出方式和输出数据类型;后续的参数是要输出的数据,数量可以根据格式化字符串中的说明符而变化。 |
函数返回值 | int:写入文件的字符数,如果出现错误则返回一个负数 |
编号:013 | int fclose(FILE *stream); |
---|---|
函数功能 | fclose 函数的主要功能是关闭打开的文件流,释放相关资源以便文件能够正确保存数据。在文件流被关闭之后,就不能再往该文件流中写入或读取任何数据,否则可能会导致程序异常退出或数据损坏 |
参数说明 | tream 是要关闭的文件流,可以是使用 fopen 函数打开的文件流或标准输出流 stdout、stderr。 |
函数返回值 | fclose 函数没有返回值,但它的执行结果会根据是否成功关闭文件流而设置相应的错误码。如果函数执行成功,则返回 0;否则返回一个非零(通常为 -1)的整数表示关闭文件流失败。 |
编号:014 | void gameover(); |
---|---|
函数功能 | 展示游戏结束的时候的图片: 将玩家最后的分数存入list.txt文件中(保存分数) |
参数说明 | 无 |
函数返回值 | void |
编号:015 | char *fgets(char *str, int n, FILE *stream); |
---|---|
函数功能 | 从文件流中读取一行数据的函数 那么while循环中的fgets即可实现将文件中的每行数据都读取下来 |
参数说明 | charstr 为要保存数据的字符串,int n 为字符串str最大可接受长度,FILE stream表示需要打开的文件指针 |
函数返回值 | 返回值是一个指向缓冲区的指针,即读取到的数据字符串。如果读取成功,则返回缓冲区的起始位置,如果读取失败或到达文件末尾,则返回 NULL 指针 |
编号:016 | int atoi(const char* str); |
---|---|
函数功能 | 将数字字符串改为对应样式的int类型数字 |
参数说明 | const char* str:需要转换的字符串 |
函数返回值 | int:返回值是一个整数,表示转换后的结果。如果转换失败,则返回值为 0 |
编号:017 | void sort(numbers,numbers+cnt,cmp) |
---|---|
函数功能 | 将传入sort的数组进行排序,默认为从小到大,修改cmp函数可以实现从大到小(从大到小恰好可以使用前10的数据) |
参数说明 | 第一个参数numbers 是要排序的数组首元素的指针;第二个参数cnt表示数组中元素的个数;第三个参数 cmp是一个函数,用于指定排序比较的方法。 |
函数返回值 | void |
编号:018 | bool cmp(int a, int b) |
---|---|
函数功能 | 应用于sort函数的比较函数 |
参数说明 | int a:int类型整数 int b:int类型整数 |
函数返回值 | bool |
编号:019 | DWORD mciSendString(LPCTSTR lpCommand, LPTSTR lpReturnString, UINT uReturnLength, HWND hWndCallback); |
---|---|
函数功能 | 通过指定不同的命令字符串来控制不同的多媒体设备。例如,使用 "open" 命令可以打开一个多媒体文件并创建一个对应的设备 ID;使用 "play" 命令可以播放一个已经打开的文件;使用 "pause" 命令可以暂停当前的播放操作;使用 "stop" 命令可以停止当前的播放操作并关闭设备 ID 等等。具体的命令及其参数可以参考 Windows 平台上的多媒体命令文档。 |
参数说明 | 第一个参数 lpCommand 是一个以 null 结尾的字符串,用于指定要执行的多媒体命令;第二个参数 lpReturnString 是一个输出缓冲区,用于返回命令执行结果的信息;第三个参数 uReturnLength 表示输出缓冲区的长度(以字符为单位);第四个参数 hWndCallback 是一个回调窗口句柄,用于接收多媒体命令的执行结果通知消息。 |
函数返回值 | 返回值是一个无符号整数,表示命令执行结果的状态码。如果返回值为 0 或者不确定,则可以通过检查输出缓冲区中的信息来确定命令的执行结果。 |
编号:020 | int initgraph(int width, int height, DWORD flags = 0); |
---|---|
函数功能 | EasyX 图形库中用于初始化图形窗口的函数 |
参数说明 | int:width:整数类型,表示图形窗口的宽度(单位为像素)。 int:height:整数类型,表示图形窗口的高度(单位为像素)。 int:flags:可选参数,DWORD 类型,表示创建窗口的附加标志,默认值为 0。 |
函数返回值 | int:如果成功初始化图形环境,则返回 0;否则返回错误代码。可以调用 GetError() 函数获取错误描述信息。 |
编号:021 | void loadimage(IMAGE *pDstImg, LPCTSTR pImgFile, int nWidth = 0, int nHeight = 0, bool bResize = false); |
---|---|
函数功能 | 加载图片文件 |
参数说明 | IMAGE *pDstImg:图片变量的图片指针 LPCTSTR pImgFile:文件位于计算机的地址(或者相对地址) int nWidth:需要图片加载的尺寸的宽 int nHeight:需要图片加载的尺寸的高度 |
函数返回值 | void |
编号:022 | void begin(IMAGE img_begin); |
---|---|
函数功能 | 自定义函数:用于显示启动界面: 动态排行榜 开始游戏选项 继续游戏选项 基本提示信息 |
参数说明 | IMAGE img_begin:传递启动界面图片 |
函数返回值 | void |
编号:023 | void BeginBatchDraw(); // Begin batch drawing mode |
---|---|
函数功能 | 开始双缓冲绘图 |
参数说明 | 无 |
函数返回值 | void |
编号:024 | void outtextxy(int x, int y, LPCTSTR str); |
---|---|
函数功能 | EasyX 中用于在指定位置输出文本的函数 |
参数说明 | int x:内容输出的x坐标 int y:内容输出的y坐标 LPCTSTR str:输出内容字符串 |
函数返回值 | void |
编号:025 | int sprintf_s(char *buffer, size_t sizeOfBuffer, const char *format, ...); |
---|---|
函数功能 | C/C++ 中标准库中的格式化输出函数之一,用于将指定格式的数据按照指定方式输出到指定字符串中 |
参数说明 | buffer:字符数组类型,表示要输出的字符串缓冲区,即输出结果将存储在该缓冲区中。 sizeOfBuffer:无符号整数类型,表示缓冲区的大小,即最多可以向该缓冲区中输出多少个字符。注意:为了避免发生缓冲区溢出的情况,建议将该值设为输出格式化字符串所需缓冲区大小(包括结尾的 NULL 字符)的上限值。 format:字符串类型,表示输出格式化字符串,该格式化字符串包括普通字符串和格式控制符。具体格式控制符的含义和使用方法与 printf() 函数相同。 |
函数返回值 | 如果成功输出字符串,则返回字符数(不包括结尾的 NULL 字符);否则返回负数。可以通过调用 GetError() 函数获取输出错误的原因。 |
编号:026 | void FlushBatchDraw(); |
---|---|
函数功能 | 在默认情况下,EasyX 将图形操作制作为一个“批处理”(Batch)进行处理,即将多个图形操作缓存到内存中,在一定条件下将它们一次性地提交到显示设备中,以提高绘图效率。但是,在某些情况下需要手动刷新缓冲区,以便及时更新显示内容。这时就可以使用 FlushBatchDraw() 函数强制提交批处理缓冲区,刷新显示内容。 |
参数说明 | 无 |
函数返回值 | void |
编号:027 | int rand(void); |
---|---|
函数功能 | rand() 函数主要用于生成指定范围内的伪随机数。使用该函数后,可以在 0~RAND_MAX(通常为 32767)之间生成一个随机整数,也可以通过使用 srand() 函数,设置生成随机数的种子,以产生更加多样化、具有更强随机性的随机数序列。 |
参数说明 | void |
函数返回值 | 该函数返回生成的伪随机数,数据类型为整型。 |
编号:028 | bool MouseHit(); |
---|---|
函数功能 | MouseHit() 函数主要用于检测当前是否存在鼠标事件的产生,如果有,则返回 true,否则返回 false。鼠标事件包括按键和移动事件等。使用该函数后,可以实现对鼠标事件的实时检测,以便及时作出相应处理。 |
参数说明 | 无 |
函数返回值 | 该函数返回一个布尔类型的值,true 表示当前存在鼠标事件,false 表示当前不存在鼠标事件 |
编号:029 | MOUSEMSG GetMouseMsg(); |
---|---|
函数功能 | 用于获取鼠标事件 |
参数说明 | 无 |
函数返回值 | 返回一个 MOUSEMSG 结构体,它包含以下字段: x:鼠标事件发生的 x 坐标。 y:鼠标事件发生的 y 坐标。 mkState:表示鼠标按键的状态,可以是以下值之一: MK_LBUTTON:左键按下。 MK_RBUTTON:右键按下。 MK_MBUTTON:中键按下。 MK_SHIFT:Shift 键按下。 MK_CONTROL:Ctrl 键按下。 |
要求2:函数之间的调用关系图
2、函数设计
要求:
在每个3级标题下,对创建的每个函数分别画出流程图(推荐使用visio或者draw.io等专业绘图工具),贴出对应代码和注释,无流程图、无代码注释、注释与流程图对应不上不得分。
下列是gameover()函数的修改部分
-
五**.**测试分析
-
测试与调试
测试和调试结果正确,代码可正常运行,可以实现纵榜设计游戏(飞机大战)的基本要求,能够正常游玩,满足基本设计要求
- 测试结果分析
测试结果与调试结果一致,可正常运行,游戏可正常游玩
- 结论
完成基本要素,达到基本要求
-
六**.**系统演示
-
系统个功能运行截图与说明
开始游戏**/**继续游戏
(完成地图展示、方向控制,敌我子弹发射,碰撞检测等基本射击游戏元素)
(上下左右由W/A/S/D或者上下左右键实现)
(空格发射子弹)
点击开始游戏:进入游戏展示背景地图,开始游玩游戏,上下左右键或者WASD键控制玩家飞机位置,实现方向移动,当玩家点击空格时实现玩家飞机发射子弹,初始分数为0分,每击中敌机分数增加,分数每到达一个20分的区间,敌机和敌机子弹的移动速度增加一个档次:
也就是每20分游戏难度提升;但敌机子弹击中玩家飞机时或者玩家飞机碰撞敌机时玩家飞机爆炸,该局游戏结束。
存档和读档:
在游玩过程中点击ESC键实现读取当前玩家的分数信息和玩家的飞机坐标:存入data.txt文件中,结束当前游戏
下次点击继续游戏时继续上次保存的分数,(将上次的存档清空变为0)
在继续游戏的过程中若玩家飞机爆炸,那么下次继续游戏就是0分(与开始游戏的效果一致)
若多次存档会覆盖上次存档,只有最后一次存档有效,(只要不覆盖存档或者存档未被使用,那么存档可以在任意时刻打开)
显示排行榜:(用图片加以显示:虽然视频里有)
排行榜为实时变化排行榜:依据当前玩家的score进行自动调整
开始游戏的界面右上角显示排行榜:TOP10:正常游戏后将不再显示排行榜数据
(通过运行项目时打开list.txt文件读取数据,将数据排序,将排序好的数据覆盖原来的数据,读取前面10位数据即可实现展示排行榜
游戏难度调节:
(难度调节依赖于分数变化:进行自动调节)
函数展示:具体图片及视频展示见前面视频内容
玩家游玩分数每增加20分难度自动提升
游戏音乐和音效:
(前面已经有展示了,此处不特意进行展示)
运行项目文件时载入音效文件,将主BGM设置循环播放,按下时发射子弹打开发射音乐,击中敌机时打开敌机爆炸音乐文件,玩家飞机爆炸时打开玩家飞机爆炸音乐文件
-
七**.**设计体会
-
设计过程中的问题及其解决方案
-
首次实施项目开发,很难确定所有的需求,可能会随着项目的开发而逐渐变化。
解决方案:可以采用敏捷开发方法,将需求分解为小部分,迭代式地进行开发。同时,在项目设计之前,需要对需求进行多方面的调研、讨论,尽量确保需求的完整性和准确性。
- Easyx头文件中的部分图形处理函数参数填写错误
解决方案:参考Easyx参考手册
- 无法打包VS Studio项目文件
解决方案:参考B站教学视频,向同学请教
d、代码调试困难,项目文件运行时多次报错
解决方案:可以采用调试工具,如断点调试,来快速定位和解决问题
- 设计过程中的错误及其原因分析
a、未击中敌机时敌机反复出现在一个位置
原因:敌机的x坐标只在子弹击中敌机时随机更新
解决法案:新增加一个if判断语句,当敌机y坐标达到图形框的末尾时运行一遍敌机x坐标的更新
b、TOP10 score只显示 了9条数据
原因:for(int i=0;i<9;i++)
解决方案:for(int i=0;i<10;i++)
c、玩家飞机在敌机附近即触发了爆炸判断语句
原因:判断语句为爆炸判断语句敌机x,y坐标的附近触发
解决方案:修改判断语句的触发条件为
position_x >= enemy_x && position_x <= enemy_x + 100 && position_y >= enemy_y && position_y <= enemy_y + 180
修改判断区间
- 难度提升过快
原因:难度递增系数过大
解决方案:调小难度系数
- 体会和收获
(因为学习了部分C++语言知识点,故使用了部分C++语法:比如sort函数和bool cmp函数)
a、深入了解 C/C++:通过开发一个 C/C++ 项目,深入了解C/C++ 的语法、特性和编程思想等等。为我更好地掌握 C/C++,并为后续的编程工作打下基础。
b、学习使用开发工具:VS Studio 是一款功能强大的集成开发环境,它为开发者提供了多种编程工具和辅助功能,如代码自动补全、调试器、版本控制等等。通过开发一个 C/C++项目,我学习到了如何使用这些开发工具和辅助功能,提高编程效率。
c、锻炼编程能力:开发一个C/C++ 项目需要具备较强的编程能力和团队协作能力,能够独立完成各种开发任务,并且与其他开发人员进行协同开发。在项目开发过程中,我面临了各种挑战和问题,需要我不断地学习和提升自己的解决问题的能力。
d、获得实际开发经验:通过开发一个 C/C++ 项目,我将获得实际的开发经验,包括需求分析、架构设计、编码、测试和上线等方面。使我更好地掌握软件开发的流程和方法,为未来的职业发展打下基础。