目录
一、实验目的与要求
1. 理解A*寻路算法原理。
2. 进一步熟悉地图编辑器的使用。
3. 实现游戏中的人工智能。
二、实验内容与方法
1.完成游戏编译(5分)
成功编译并运行教材P200“游戏AI实例-迷宫寻宝”。
2.完成修改内容一 (5分)
修改游戏代码,实现修改内容一,即修改窗口大小。
3.完成修改内容二 (10分)
修改游戏代码,实现修改内容二,即增加DANGEROUS区域。
4.完成修改内容三 (10分)
修改游戏代码,实现修改内容三,即增加按键监听。
5.完成修改内容四 (10分)
修改游戏代码,实现修改内容四,即增加地图层次。
6.完成Bug修改(10分)
通过更改游戏代码的方式修复BUG,如:点击笑脸出现“No Way” 和“Found Treasure”的叠加。
7.添加配乐、音效(5分)
为游戏添加配乐、音效。
8. 游戏优化升级(5分)
自行发挥想象力,优化游戏功能。
9.完成实验报告 (40分)
截图记录关键步骤,分析实验结果,撰写心得体会。
三、实验步骤与过程
1. 完成游戏编译
首先我们使用 cocos new MyGame -p com.2020152011.edu -l cpp -d Experiment_5 命令创建新项目。
(图1.1 - PowerShell窗口)
创建结果如下:
(图1.2 - 创建结果截图)
接着我们把本次实验的代码内容复制替换到生成的项目中的"Classes"文件夹内。
(图1.3 - "Classes"文件夹)
下面同样我们把实验内容中的图片、地图、字体资源复制到生成的项目中的"Resources"文件夹内。
(图1.4 - " Resources "文件夹)
接下来打开Visual Studio,添加、删除现有项(类)。
(图1.5 - Visual Studio窗口)
最后点击调试器按钮,稍等片刻,即可成功运行本次实验游戏。
(图1.6 - 调试器按钮)
(图1.7 - 游戏运行成功初始画面)
这里,我们顺便也把游戏显示名称进行修改,在applicationDidFinishLaunching函数中,对于中文的部分调用转换函数,之后点击调试器按钮,可以看到如图1.9,中文正常显示。
略
(图1.8 - AppDelegate.cpp - applicationDidFinishLaunching函数)
略
(图1.9 - 修改后的运行窗口)
2. 完成修改内容一
我们来修改游戏窗口大小,修改在AppDelegate.cpp中的designResolutionSize参数。
(图2.1 - AppDelegate.cpp - 修改后的designResolutionSize参数)
可以看到修改后的窗口大小可以让 map 完整显示。
(图2.2 - 修改窗口大小后游戏画面)
3. 完成修改内容二
接下来我们增加DANGEROUS区域。首先用TiledMap编辑原地图,进行些许改动并加上“墓碑”(ID为290),如下图3.1红框内所示:
(图3.1 - 新地图以及墓碑示意图)
我们设定以下规则:
- 当宝藏设置在“墓碑”处,或途径“墓碑”,屏幕中央提示“Dangerous!”信息
- 当“寻宝精灵”到达“墓碑”处时,“寻宝精灵”会停在“墓碑”处,屏幕中央的提示字样“Dangerous!”改换显示为“Die!”,此时点击屏幕没有反应。
- 如果“宝箱”刚好在“墓碑”处,则当“寻宝精灵”到达时,“宝箱”也会消失,但是不会显示“Found Treasure!”。
代码部分中,在节点结构体mapNode中添加新的成员type,表示节点的类型标志。
(图3.2 - MazeScene.h - 节点结构体mapNode)
在算路径时就判断检测该路径上是否有“墓碑”,如果有则提示“Dangerous!”; 在为“寻宝精灵”决定运动路径时就只加入遇到“墓碑”前的路径即可(白色的预测路径的线段还是完整的),因此实际运动时的目标点为途经的“墓碑”,如果到达“墓碑”则角色死亡,游戏结束。
(图3.3 - MazeScene.cpp - moveOnPath函数1/3)
(图3.4 - MazeScene.cpp - moveOnPath函数2/3)
(图3.5 - MazeScene.cpp - moveOnPath函数3/3)
(图3.6 - MazeScene.cpp - judge函数)
因为在地图添加了新的元素,所以要在地图初始化initMap函数中増加对新元素的判断。
(图3.7 - MazeScene.cpp - initMap函数)
而提示字样也是在init函数中先预置。
(图3.8 - MazeScene.cpp - init函数)
(图3.9 - 途经危险区域时示意图)
(图3.10 - 到达危险区域时示意图)
4. 完成修改内容三
下面我们增加按键监听,当按下 R 键后,使游戏重新开始。
(图4.1 - 键盘监听器部分代码)
并在init函数中添加键盘监听。
(图4.2 - MazeScene.cpp - init函数)
5. 完成修改内容四
下面我们增加地图层次。首先用TiledMap编辑地图,制作第二层的地图,如下图5.1,该地图带有本人学号后4位,并且有新的元素“楼梯”,将在下面详细介绍。
(图5.1 - 第二层的地图)
我们设定以下规则:
- 当宝藏设置在“楼梯”处,或途径“楼梯”时,“寻宝精灵”会在到达“楼梯”处时停在“楼梯”处。
- 当“寻宝精灵”到达“楼梯”处时,会根据是“上楼梯”或“下楼梯”执行对应的场景切换,比如玩家在地下第一层到达“下楼梯”处,则会切换到第二层的地图,并且“寻宝精灵”的位置是在原本的“下楼梯”处,而不是原点。
- 当进行楼层场景切换时,会在屏幕中央提示该楼层为第几层。
- 楼层之间的“楼梯”位置是一样的,区别在于是“上楼梯”和“下楼梯”,当进行楼层切换后,不能点击原本的“楼梯”处返回原来楼层。
- 除了“楼梯”(ID为3、26)、“墓碑”(ID为290的)以及“墙壁”(ID为79、80)外的元素,都是可以直接通过的。
接下来根据以上的规则,我们来添加修改代码。
因为涉及到全局变量,我们用一个头文件GlobalConfig.h来保存这些全局变量。
(图5.2 - GlobalConfig.h)
因为在地图添加了新的元素,所以要在地图初始化initMap函数中増加对新元素的判断。
(图5.3- MazeScene.cpp - initMap函数)
update函数也是同理。
(图5.4 - MazeScene.cpp - update函数)
之后在moveOnPath函数中增加对“楼梯”的判断。
(图5.5 - MazeScene.cpp - moveOnPath函数)
judge函数也是同理,其中到达下楼梯后会增加层数是因为该层数表示的是地下层数。
(图5.6 - MazeScene.cpp - judge函数)
在onTouchBegan函数添加新的判断条件,禁止玩家进行楼层切换后,点击原本的“楼梯”处返回原来楼层。
(图5.7 - MazeScene.cpp - onTouchBegan函数)
为了在楼层场景切换后正确显示“寻宝精灵”的位置,需要修改init函数和initMap函数,把“寻宝精灵”放到遇到“楼梯”之后的位置。
(图5.8 - MazeScene.cpp - init函数)
(图5.9 - MazeScene.cpp - initMap函数)
然后在init函数中设定在屏幕中央提示楼层数,其将其设置1.5秒后消失。
(图5.10 - MazeScene.cpp - init函数)
(图5.11 - MazeScene.cpp - removeFloorTip函数)
具体效果如下图5.12:
(图5.12 - 效果图)
6. 完成Bug修改
注意到在游戏过程中,如果点击笑脸,也就是当前寻宝精灵所在的位置时,会出现提示文字“No Way”和“Found Treasure”的叠加。
(图6.1 - 游戏BUG示意图)
解法方法就是在点击时,判断点击位置是否与当前寻宝精灵所处位置相同,如果相同,则禁止点击。
我们修改MazeScene.cpp中的onTouchBegan函数,实现如上的效果。
(图6.2 - MazeScene.cpp - onTouchBegan函数)
之后在某次运行调试的时候,我发现此BUG并未完全解决,如果在游戏开始的时候就点击寻宝精灵,依然会出现提示文字“No Way”和“Found Treasure”的叠加。
在仔细研究代码逻辑之后,发现这是因为先前添加的判断条件在一开始寻宝精灵移动前是不能生效的,也就是m_destination的值一开始是空指针,只有在执行完一次移动操作之后才是非空,这也解释了为什么执行完一次移动操作之后再点击寻宝精灵就会正常显示禁止符号。
由此,我们在初始化的时候给m_destination也设一个初值即可解决这个问题。
(图6.3 - MazeScene.cpp - initMap函数)
(图6.4 - 效果图)
注意到在游戏过程中,如果寻路终点刚好在墓碑的位置,会短暂出现提示文字“Die”和“Found Treasure”的叠加。
在仔细研究代码逻辑之后,发现这是因为update函数中的碰撞方式判断条件为精灵sprite之间的碰撞,按原代码逻辑,两个精灵碰撞后则视为“寻宝精灵找到宝箱,运动结束”并执行后续代码,但是,假设精灵间只是刚好接触到边,也算是产生了碰撞,而此时寻宝精灵并没有完全停止运动。代码逻辑矛盾,容易引发错误。因此碰撞检测方案更改为“精灵坐标中心相等”。
(图6.5 - MazeScene.cpp - update函数)
7. 添加配乐、音效
下面我们为游戏添加配乐和音效。首先使用SimpleAudioEngine时需要引入头文件和命名空间,在每个用到配乐和音效的文件加入以下内容。
(图7.1 - 引入头文件和命名空间)
接下来添加游戏的背景音乐,因为游戏需要不断切换场景,为了不打断背景音乐的播放,我们把播放音乐的代码放到AppDelegate.cpp中。
(图7.2 - AppDelegate.cpp - 播放音乐的代码)
然后添加游戏的各种音效。
(图7.3 - MazeScene.cpp - 当移动终点在无法到达区域(死路)时的失败音效)
(图7.4 - MazeScene.cpp - 点击区域为禁止区域时的音效)
(图7.5 - MazeScene.cpp - 找到宝箱的音效)
(图7.6 - MazeScene.cpp - 下楼梯的音效)
(图7.7 - MazeScene.cpp - 上楼梯的音效)
值得注意的是,当到达“墓碑”时,如果危险音效还在播放,那么它将会被强制停止。
(图7.8 - MazeScene.cpp - 路径中有“墓碑”时的危险音效)
(图7.9 - MazeScene.cpp - 到达“墓碑”时的失败音效)
8. 游戏优化升级
1) 增加音乐按钮
主要根据状态标号musicState来控制音乐的播放。
(图8.1 - MazeScene.cpp - init函数)
(图8.2 - MazeScene.cpp - menuMusicCallback函数)
(图8.3 - 效果图)
2) 添加场景过渡效果
为了让上下楼梯时的过程更加炫酷,在原本切换场景的代码部分添加场景过渡效果。
(图8.4 - MazeScene.cpp - 场景过渡效果代码)
(图8.5 - 效果图)
3) 添加游戏失败后的操作提示
游戏过程中,在遇到“墓碑”或死路后,将不能再继续游戏,只能通过键盘R键重新开始游戏,我们来把这一操作提示,在游戏失败后显示出来。
(图8.6 - 关键代码)
(图8.7 - 效果图)
4) 增加寻宝精灵的表情状态
为了增加游戏的多元性,我添加了两种寻宝精灵的表情状态,一个是找到宝藏的快乐状态,一个是游戏结束的失败状态,当新的状态出现时,会先把旧的状态隐藏。
(图8.8 - 关键代码)
(图8.9 - 关键代码)
(图8.10 - 效果图)
(图8.11 - 效果图)
四、实验结论或心得体会
根据实验指引顺利的完成了实验,通过这次实验,我理解了A*寻路算法原理,进一步熟悉了地图编辑器的使用,和实现了游戏中的人工智能,并根据以上知识完成了实验。
这次实验用了比较多心机去完成,透过上网找资料实现了一些自己想要的功能,总的来说是一次不错的体验,期待未来能够做出更多有趣的游戏。