【unity】学习制作2D横板冒险游戏-5-

野猪的受伤和死亡

野猪的受伤动画

剪切图片并添加动画

野猪的死亡动画

在素材中并没有野猪死亡的动画素材,可以用受伤的动画进行修改,在死亡时受伤状态的快速闪烁+渐变消失的效果
复制一份受伤动画,修改受伤动画的帧率,让其快速闪烁

记得要添加到动画树中,不然没法进行下一步修改

渐变消失则用透明度来解决

野猪的受伤逻辑

连接动画树逻辑

取消loop time,因为第二次播放要重新播放,不需要播放第一次剩余的动画

编辑代码,代码写在父类敌人中

野猪受攻击时,野猪将停止移动

应用在野猪受伤事件中

完成,但是现在野猪被攻击后无法脱离被攻击的状态,导致僵在原地,而如果在受伤代码最后加上修改状态的代码,会因为代码运行太快导致被攻击状态只有一瞬间无法完成被击退的逻辑
因此我希望在野猪完成受伤的动画之后,执行状态的转变。
由此要使用协同程序的方式

C#迭代器;协同程序

MyCoroutine方法是一个协同程序,它使用yield return来暂停执行并在指定的时间后继续。StartCoroutine方法启动这个协同程序。

在代码中用协程的方法使野猪在受到击退后,停顿0.45s即击退动画一段时间后,将野猪状态恢复

野猪的死亡逻辑

且死亡状态不能移动

调用死亡事件

野猪,包括碰撞体之类的销毁

我希望在执行完最后一帧死亡动画后,野猪进行销毁,因此要用到帧事件

帧事件

在死亡动画的最后一帧添加帧事件

应用帧事件

完成,野猪死亡

图层间的碰撞

但是,经过测试在野猪死亡动画到消失的那个短时间内,如果人物靠近依旧会造成伤害
优化思路为在野猪死亡瞬间,修改野猪的图层,并设置两图层之间不会发生碰撞
将野猪死亡后图层安排到Ignore Raycast

图层碰撞设置edit/project setting/physics2D/Layer Collision Matrix

在代码中实现图层的转换

设计模式-有限状态机和抽象类多态

父类继承:一个饼皮,我要做菠萝披萨和腊肠披萨,那么继承一个饼皮,然后再自行加其他东西

抽象类是一种不能直接实例化的类,通常用于定义一些通用的行为或属性,供其他子类继承和实现。

抽象类继承:抽象一个饼皮,但不限于是薄饼皮还是厚饼皮,发面饼皮还是死面饼皮

父类继承允许子类复用父类的代码,并提供特定的实现(给一个饼皮);而抽象继承则定义了一个通用的框架,要求子类实现特定的方法。(要有一个饼皮)

创建抽象基类代码

基本函数声明

创建巡逻逻辑脚本

一键添加饼皮

现在,我创建了一个最基本巡逻状态,敌人会有各种各样的状态,在每个状态又会有不一样的行为
我们需要到enemy代码中去进行状态的转换,且会优化一些enemy中的代码,确保敌人在正确的状态有正确的行为

在enemy中声明各个状态

定义Onaeable周期函数,使得巡逻状态为最基础的状态,并持续不断执行

在update和fix update中分别执行当前状态的逻辑更新和物理更新

定义Ondisable退出函数,退出函数会在场景人物关闭后执行一次,用来进行状态的关闭

这样就完成了一个状态的运行

如果我们要在状态中访问enemy脚本的内容,我们就要在他所继承的抽象类中获得变量
这样就可以访问了

在enemy中传递出去

将enemy中的移动代码转移过来,因为追击和巡逻移动ing代码不同,

要注意变量的访问

在上文中,为了变量的访问,我修改了变量的访问限定符,因此变量会在unity控制器中显示,但是我们并不需要他显示出来
加上前缀

[HideInInspector]是一个属性修饰符,其主要作用是在Unity的Inspector窗口中隐藏变量或属性。

在以后怪物肯定不止一个,比如野猪或者蜗牛
而代码不能将野猪的和蜗牛的放在一起执行,因为他们之间有不同的状态
因此确定当前的敌人是谁

在野猪代码中重载一个更新函数,并给其父类enemy中的状态确定是谁

代码逻辑如下

等待时不要移动

地面检测,让检测器随着野猪转头,缺点是会增加运算量,因为要帧更新检测器位置

成功,野猪移动正常,且撞墙和悬崖边会等待一会并转头

修复一些小问题😰

在野猪移动的过程中,我发现野猪在移动后等待的时间中,野猪像”爬上了高坡“
整体模型会比移动时高上一些
初次判断为地图边缘碰撞体有些突起,在检查瓦片后没有发现异常
再次研究发现为动画中心点处理有问题,在切割动画素材时静止动画的中心点未与其余动画中心点保持一致
解决:修改了静止动画的中心点,解决成功

在野猪从悬崖边等待并转身后,野猪并没有立刻移动,但是野猪的行走动画进行了切换,导致野猪在转身后滑步了一段时间。而撞墙检测后却没有发现这样的情况,转身之后正常行走
通过查看面板发现
在撞墙转身后,wait状态立刻变为false,而在悬崖转身后,wait状态没有立刻变为false.
初次判断为代码的问题,进入代码检查后为代码次序问题。
出错代码是在野猪转身后,先进行了地面检测让野猪进入了等待时间,才更新检测的位置
调换位置代码在检测前

解决成功

追击状态的转换

首先要做的就是关于敌人对于玩家的检测,在基类Enemy中添加
鉴于基类中代码有些多,先整理一下

#region
#endregion

定义一个函数,让他返回一个射线,返回值为是否检测到敌人的布尔值

提示我们还需要,角度,方向,距离和层、
定义以上变量

检测函数代码如下

在巡逻模式中,如果检测到敌人,则切换状态

枚举类型Enum

具体是要切换到哪种状态呢,比如敌人在不同的血量会有不同的阶段,因此要切换的状态可能比较多

创建一个Enum脚本在Assets-Scripts-Utilities路径下

在脚本中创建枚举类型用来枚举状态,逗号相隔开

切换Switch代码如下

这段代码使用了C# 8.0引入的switch表达式,它提供了一种简洁的方式来根据一个表达式的值返回不同的结果
这里声明了一个变量newState,其值将根据state的值通过switch表达式来确定。
如果NPC处于巡逻状态,则newState为巡逻状态对象;
如果NPC处于追逐状态,则newState为追逐状态对象;
如果NPC处于其他状态,则newState为null。

在巡逻状态中的代码如下

这样的作用是方便后期的维护和补充新的状态
现在有了状态的转换,我们要来编写一下追击状态
创建追击状态脚本

在Boar脚本中赋值

追击的代码如下
当切换状态后就会输出切换成功的文本

现在完善一下检测器的设置
将检测的图层设置为Player

为了能够更好的检查检测器,我们应该让检测器显示出来

代码完成后设置如下

检测位置如下

追击状态代码如下

现在完成了追击状态的切换,野猪进入这个状态一段时间后,就会“冷静下来”

更新代码如下

转换代码如下

再次进入巡逻状态时,速度应该,慢下来

进入unity面板设置丢失时间为三秒
运行游戏

成功

解决野猪冲锋状态下攻击野猪不会被击退的问题
原因是野猪的速度将击退力抵消,将野猪被攻击时的速度变为零

UI创建人物的状态栏

创建UI画布

在事件系统中替换新输入系统

应用自己创建的输入系统

在屏幕中的方框就是UI画布,我们的UI在这个画布中

画布大小为整个屏幕的大小

并根据我们屏幕实际的分辨率来调整UI布局

在游戏窗口固定比例方便制作UI画面

UI布局设置同样的比例

如果有玩家的高度是1080,宽度比1920长,则match 0 优先适配宽度 match 1 优先适配高度,0.5 则平均适配

所用的UI素材与人物素材一样像素为16

创建一个UI

在rect transform 面板中可以设置UI的位置

以左上角为基准点,保证不同电脑不同分辨率UI的相对位置相同

切割UI素材,设置一下图片

自动切割

自动切割并不能完全切割出我们要的效果,因此手动修改一下部分图片

对于切割时没有被识别出来的素材,按住鼠标画一下

切割好素材后添加进刚刚创建的UI中

剪辑素材时改名可以帮助更好找到

添加完成后比例不太对,调整比例

调整大小,鼠标直接拖拽,按住shift可以等比例调整

在添加一个生命条,调整好大小

叠层关系:在下方的会覆盖上方的可以调整一下顺序

我们希望血条会根据血量进行对应的减少,本质上是根据血量填充图片,血量越多填充越多
选择填充类型

血条是横向减少的因此,填充方向为横向

拉动滑动条就可以看到血量的减少了

为了优化UI的视角效果,我希望人物被攻击后血条有一个渐变效果,绿色的直接减少,后面还有红色的缓慢减少
在添加一个红色图层

以上血条UI就做好了
创建一个父类文件夹方便管理和调整位置

调整血量条的相对位置和绝对位置

调整父类的相对位置

选中父类调整其位置框,使其对齐左上角

选中三个子类,在transform窗口中设置相对位置在中间

位置取整,方便代码的编辑

能量条

绘制如下

头像框

绘制如下

在创建一个父类将血条,能量条,头像框包含进去

人物头像

素材中并没有人物的头像,因此用素材切割出一个头像
创建一个空白UI,更名为cut

在其子路径下,创建一个UI并加入一个人物的素材

将人物素材的头放到空白UI中

在cut中添加一个mask组件
此时可以发现,只剩一个头了,其子路径图片被蒙版了

取消白色背景

成功,展示如下

掉血时UI的实现

创建控制血量的脚本

应用脚本

调用命名空间并声明变量

将变量进行赋值

script object

在血量UI中,我们需要调用Character脚本中的人物血量属性来进行ui的调整
如何来调用呢?我们需要使用script object

script object是游戏资产,他会永久的保存在游戏之中,确保变量不会在跨场景跨平台等等情况下消失

创建一个文件夹用来存放相应的代码

创建一个事件文件

代码需要继承ScriptableObject

这样代码会生成一个资产文件,我们需要给资产文件取一个名字以及我们找到他的路径

我们可以在资产菜单Event路径下找到他

代码编辑如下

该订阅方法与以前学习的订阅方法有所不同,现在的订阅是完全用代码控制,而以前学习的使用Inspector窗口中调用

要想调用事件,先调用命名空间

用unity action委托(事件)传递Charactor脚本,用来监控里面的血量变量

UnityAction 在 Unity 中是委托(delegate)类型,它用于定义没有返回值(void)并且可以接受最多四个参数的方法。
UnityAction 通常用于 Unity 的事件系统中,使得开发者可以很容易地将方法绑定到事件上,从而实现在特定事件发生时调用这些方法。

委托(delegate)是 event / Action 的底层实现,适用性最广。概念有点类似函数指针。
事件(event)是 对委托的封装,是受限制的委托。

启动事件的函数

在unity中创建一个文件夹用来存放事件

添加我们刚刚创建的event文件

我希望只要Character脚本中血量发生变化,就调用事件
在Character脚本中创建事件

用unityEvent进行广播,血量变更时就调用event文件

在game object中创建一个父类用于UI的数据转换(血量,场景。。


在文件夹中同样创建一个脚本用来进行代码控制UI的数据转换

挂载到game object的UIManager中

在代码中负责监听SO文件

unity面板中配置

注册事件取消注册事件和血量更新

在unity面板中赋值

在character面板中还要添加进行传递的代码

在进入游戏时也要更新一下

代码逻辑完成

但是UI的绘制还有点问题,调整一下

成功

代码的逻辑思路如下
比较复杂,慢慢来O.o

渐变的血条实现

如果红色条大于绿色条,红色条就随时间减少

完成

摄像机跟随和攻击抖动

添加摄像机插件

添加摄像机

跟随并观看player

运行游戏发现摄像头里人物太远了,

调整距离

调整一下跟随的中心点和跟随的缓冲区域

添加一些摄像机工具
pixelperfect

CinemachinePixelPerfect是Unity中Cinemachine虚拟相机的一个扩展,它主要用于改变虚拟相机的正交尺寸,以在2D像素游戏中保持像素的完美显示,防止像素抖动。

Confiner 2D

Cinemachine Confiner 2D是Unity中Cinemachine插件的一个组件,它用于限制虚拟相机的移动范围,确保相机在指定的2D区域内移动。

有了范围限制插件后还需要设置一个范围Bound ing shape 2D
创建一个Gameobject

添加多边形碰撞组件,选择触发器

碰撞器可以设置为任意形状,且摄像头只在该范围内移动

给摄像头添加上范围

通常来说限制只需要四个点

规范一下具体大小

现在,摄像头可能会在规定的时间,以规定的方式,被规定的范围限制用来绘制不同的场景。比如在人物通关到下一关后,摄像头应该显示第二关的内容并被第二关的范围限制
为了方便查找范围限制,给范围添加一个标签

创建一个脚本用来检测与切换摄像头的范围

将代码挂载到摄像机上面

在边界切换后还应该先清除掉摄像机之前的缓存

直接代码调用

启动函数

先手动清除之前绑定的范围,然后运行,检验代码是否成功

成功

震动反馈

添加lmpulse Listener

默认是3D震动,勾选2D的震动

创建一个gameobject

添加一个振动源脚本,里面有很多预制的效果

震动方式,可以看波谱

数字越大震动速度越慢

xyz轴的震动幅度,数字越大幅度越大

运行游戏后此处激活震动

通过代码调用震动

手动实例化

我希望在人物受伤和野怪受伤(证明玩家攻击了野怪)时进行震动
还是使用事件的调用方法,但与血量不同的是,事件不需要传输参数
创建一个没有参数的SO脚本

SO脚本代码如下

创建SO文件

在人物受伤和敌人受伤广播此事件

在摄像机控制的脚本中监听此事件

注册事件和注销事件

添加监听的事件

运行一下
成功,屏幕正常震动

posted @ 2025-03-29 15:38  plusu  阅读(65)  评论(0)    收藏  举报