《吃豆人》接上一篇。(其实是在等下载《火焰纹章-风花雪月》顺便来续写)
七、走向胜利
统计豆子的数量,每吃一个就自减,减到0胜利。
打开PacmanGameModeBase.h ,声明一个 int 类型的变量用来表示豆子数量,再声明一个函数,就叫 BeanWereEaten 吧...重写父类的BeginPlay。
在BeginPlay中通过迭代器获取豆子数量并统计,在 BeanWereEaten 中减少豆子数量,默认每次-1。减到0玩家胜利。
在Bean类中声明一个DestroyBean函数,记得要加UFUNCTION() 。
然后在BeginPlay中绑定监听
最后实现DestroyBean函数,获取GameMode,调用BeanWereEaten方法。
我们在豆子管理中加了log,当豆子被吃时就会打印当前的剩余数量。
超级豆,吃了会削弱敌人。
新建SuperBean类,继承自Bean。本来想重写DestroyBean,在里面添加削弱敌人的逻辑,但是编译时被告知,不能重写UFUNCTION修饰的函数。最后只好新建一个DestroySuperBean,然后重写Bean的BeginPlay函数,在调用Super::BeginPlay之后,再新增一个OnDestroyed的事件绑定,绑定到新建的DestroySuperBean。这样SuperBean的OnDestroyed就被添加了两次监听,据我观察AddDynamic是会追加的,不是覆盖,因此在新的DestroySuperBean中,只需要添加削弱敌人的调用就好了。
接下来去GameMode里添这个WeakenEnemy方法,添加后先写个空方法,暂时只打印个log。待会再去实现。
创建基于SuperBean的蓝图,调整下尺寸和材质,以便和普通豆子区分,然后在四角删除4个普通豆子,将超级豆拖进场景,运行发现超级豆被吃时,可以打印计数,也可以触发WeakEnemy。
那么,可以去实现WeakEnemy方法了。
打开PacmanGameModeBase类,声明个敌人的结合,用来存储所有敌人,这样在WeakEnemy时方便获取敌人的引用。
在BeginPlay中收集敌人,在WeakenEnemy中遍历敌人,调用敌人的Weaken函数。Weaken是 一个新增的函数,需要在敌人类中去声明并实现。
打开Enemy类,这里需要用到UE4的定时器,首先在Enemy类中声明一个FTimerHandle。还要两个函数用来削弱敌人、正常化敌人。另外,再写个枚举记录敌人的当前状态。
在BeginPlay中设置默认状态,设置敌人的最大移动速度,因为虚弱状态会改变速度。
然后去实现削弱和正常化函数。削弱时改变状态,更换材质,减少速度,然后在指定时间后调用恢复正常的方法。
编译播放,再吃到超级豆时,敌人就会被减速和变色,然后计时结束后又会恢复原样。
八、玩家与敌人交互
现在玩家与敌人接触时,只会相互阻挡,修改敌人和玩家的碰撞描述文件,修改Pacman与Enemy的碰撞响应为overlap。最后在敌人类中添加碰撞监听。
接下来的一些内容,都是基本的游戏逻辑,胜利、失败、暂停、继续也都是有限状态机实现的,与虚幻4特性无关,已经消耗了太多时间在学习UE4上,项目那边要补进度了,因此不再一一记录。
这里主要说一些遇到的问题。敌人的控制器忽然失灵了,看引擎的日志,发现有个警告 warnin C4458 查了一下,说是自己声明的变量名和父类中的变量名冲突了,改个名就好了。另外有时候项目连续崩溃,无法重启虚幻4,需要在vs中重新生成解决方案。
还遇到个问题是,玩家与敌人碰撞时,会连续多次触发overlap事件,这是因为,我在之前调试碰撞时,把玩家胶囊体、球体、网格的碰撞预设都改成了Pacman,其实只要设置一个胶囊体就可以了,其他还原。
还遇到一个巨坑的问题,google也没找到解决办法。
上面的设置虽然能使overlap触发一次,但是当地人自动回家时,会被玩家挡住去路。于是,新增一个Empty碰撞通道和描述,所有碰撞响应都设为忽略。将Pacman和Enemy加给各自的胶囊体,然后各自的其他组件都使用Empty,这样在敌人回家时,不会被玩家挡住。
但是,问题就回来了
OnComponentBeginOverlap总是调用两次,研究很久,发现如果碰撞后,不立即将玩家放置到出生点,就不会overlap两次。。于是将“玩家返回出生点”改成了异步方式。就好了。
九、UI和音效
新建HUD的子类,声明一个字体UFont,重写一个DrawHUD函数,在蓝图中给字体赋值,在DrawHUD中绘制想要的内容。我这里只写了玩家生命值。
添加音效。
需要使用 wav 格式的音频文件,将文件直接拖进Content Browser下的目录中就导入了。这里添加两种,一个是用引擎添加的背景音乐,一个是用C++添加的敌人被攻击音效。
背景音乐是自动播放且循环的,右键拖进来的音频文件->创建Cue 然后在SoundCue编辑面板的右侧找到 looping 添加到节点中,放在WavePlayer和Output之间
然后在游戏场景中添加一个环境音效,把刚刚新建的SoundCue丢给环境音效,播放游戏就有了一个自动播放且循环的背景音乐了。
接下来是敌人音效,首先找到要使用的音频文件,右键复制引用,接下来敌人类中声明一个USoundWave,用ConstructHelpers去获取刚刚复制的路径来实例化USoundWave。在敌人被杀时调用UGameplayStatics::PlaySoundAtLocation来播放。
到此,音效添加完成。
十、结束
我想以H5的方式启动游戏时,又出现了一些错误,基本都是需要引入头文件的问题。如果是.h文件,类名前加上class,如果是.cpp文件,需要include,可以去官网搜索用到的类,将网页滑倒最后,找到类的路径 引入即可。
H5运行效果,字体有点奇怪。
后续我还想在敌人的家里放一个只阻挡玩家,不阻挡敌人的“墙”,玩家的出生点也放一个只阻挡敌人的,这样就都有基地了。还有场景的两个出口,现在走过去就会掉进深渊。。。想加个左右互传的传送阵。还有视角问题,想改成第一人称视角的,再把球换成人形,做个小地图。。。想想简直就是旷野之息的迷宫了喂 ԅ(¯﹃¯ԅ)。
放个项目工程文件
链接:https://pan.baidu.com/s/110aKqjRNIzwPG0d_raP-Ew
提取码:e9hi