用Unity重现《空洞骑士》的苦痛之路(4)——完结篇

作者:繁华如梦
前言:
终于来到本系列的最后一篇,感觉整个人都快肝废了。本来想鸽的,最终凭着最后一点肝续了下来。

在之前的篇章中,我们已经完成游戏主体的搭建。接下来只需要添加上对应的特效,就能够使整个游戏变得更加完整。(PS:本期中代码并没有过多讲解且并不完整,说得最多的是思路,还请留意)
超级冲刺特效
超级冲刺的特效大致是由4种特效组合来完成的,分别是:
1.冲刺特效
2.聚集特效
3.聚集完成特效
4.地面水晶
在上面的的特效中,某一部分由于找不到对应的图片,将不会与原版一样,而且其中还有与人物动画进行配合的部分,将不会实现类似的细节。还请各位童鞋留意。
聚集与冲刺特效
聚集状态与冲刺状态下的特效,都是使用的帧动画来进行制作的。在合适的地方播放该帧动画即可。此处就不在多说了。
聚集完成特效
聚集完成的特效,由于缺少相关的序列帧,此处只能自己搭建图像来近似相关的图像以及动画。流程就是,先参照原版游戏特效画面,自己通过游戏素材搭建出一帧帧画面,并通过代码控制物体的显示,来模拟出相应的效果。(还好这个只有4帧,多了我还真的吃不消)大致帧序列如下:

(盗文章的人实在是太多了,不知道加水印是否有用)
地面水晶
在超级冲刺的时候,地面会随时间向上生成紫色的水晶,并向周围扩散生成。其中有一个小细节,就是水晶只会生成在地面,在空中不会进行生成。此处,我们偷一下懒,水晶并不是动态生成,而是一开始搭建好的,然后在需要生成的时候,通过朝地面打射线的方式判断是否在地面,来决定是否显示水晶。如下图:

特效组合后效果如下:

代码大致如下。其中考虑到在墙上的超级冲刺,需要事先设定好转向的旋转信息,防止动态修改出现误差:
Quaternion leftQua; //超级冲刺特效地面水晶在墙上的旋转
Quaternion rightQua;
GameObject particleObj; //超级冲刺物体(紫色水晶)
void Start()
{
particleObj.transform.Rotate(new Vector3(0, 0, -90));//初始化左右旋转后的四元素,用于后面特效转向
leftQua = particleObj.transform.rotation;
particleObj.transform.Rotate(new Vector3(0, 0, 180));
rightQua = particleObj.transform.rotation;
particleObj.transform.Rotate(new Vector3(0, 0, -90));
}
public void SuperSprintFunc()
{
if (Input.GetKeyDown(InputManager.Instance.superKey))
{
....
}
else if (Input.GetKey(InputManager.Instance.superKey))
{
....
LookParticObj();
}
}
public void LookParticObj()
{
GameObject tempObj = particleObjs[lookLevel];//particleObjs 数组存储所有的特效(水晶)物体
SpriteRenderer[] renderers = tempObj.GetComponentsInChildren<SpriteRenderer>(true);
for (int i=0;i<renderers.Length;i++)
{
ParticObjIsLook(renderers[i].transform);
}
}
public bool ParticObjIsLook(Transform objTrs)
{
RaycastHit2D hit2D;
if (isClimb) //如果进行了爬墙状态 射线发射方向改变
{
if (nowDir == PlayDir.Right)
{
hit2D = Physics2D.Raycast(objTrs.position, Vector3.right, boxSize.y, playerLayerMask);
}
else
{
hit2D = Physics2D.Raycast(objTrs.position, Vector3.left, boxSize.y, playerLayerMask);
}
}
else
{
hit2D = Physics2D.Raycast(objTrs.position, Vector3.down, boxSize.y, playerLayerMask);
}
if (hit2D.transform!=null)
{
objTrs.gameObject.SetActive(true);
return true;
}
objTrs.gameObject.SetActive(false);
return false;
}
二段跳特效
二段跳特效也是使用2种特效来组合完成的。分别是二段跳的翅膀光翼,以及落下的羽毛特效。光翼没有对应的素材,此处直接跳过。只使用Unity的粒子系统来制作与羽毛落下的效果。
制作特效前,首先先找到对应的羽毛图片,如下:(注:此处图片是我在PS中切出来的3张单独的图片。)

然后为3张不同的羽毛新建不同的材质。之后在着色器选项中,选择Particles/Additive,再在后续的选项选择素材羽毛图片即可。虽然2D的精灵图应该是使用着色器Sprites/Default,但是我们这里选择粒子特效的着色器,效果更好(PS:着色器可以自由选择,你觉得好看就行)。如下图:

接下来新建一个空物体,并添加上粒子系统组件(Particle System),在Renderer(渲染)模块下的material选项,选择我们创建好的材质。如下图:

在这里简单说一下我们会用到的属性。
Duration(粒子发射的持续时间)
Start Lifetime(粒子的存活时间)
Start Speed(粒子的速度)
Gravity Modifier(重力修正,可以理解为重力的倍数)

Emission(发射模块)
Rate over Time(单位的时间发出的粒子数)
Rate over Distance(单位的移动距离发出的粒子数)

Shape (形状模块)
Shape(粒子发射的形状)
Radius(半径)

Size over Lifetime(粒子大小随时间变化模块)
Separate Axes(勾选该选项,分开处理3轴属性)
Size(调整对应属性,粒子的大小在生命周期内会按照曲线进行变化)

Rotation Over Lifetime(粒子旋转随时间变化模块)
Separate Axes(勾选该选项,分开处理3轴属性)
Angular velocity(角速度)(PS:由于是2d物体,此处只需要绕Z轴的旋转速度)

值得注意的是,上面的选项除了Duration,其余都是支持自定义曲线模式,来编辑粒子系统播放时数值随时间的变化。如下图:

接下来就是根据羽毛特效,来调整对应的参数了。(PS:上面的模块截图属性就是我使用的)由于上面的大部分属性都是使用了曲线模式来进行编辑的,这些参数根据自己的理解来进行设置即可。同时,为了羽毛特效的形态不同,我们还需要在为其余的2张不同形态的羽毛制作2个粒子特效,并将这3个特效制作为一个预制体,来完成这一效果!完成后如下图:

暗影冲刺特效
拖尾粒子
在进行暗影冲刺的时候,玩家身后会出现一条由黑色小圆点组成的拖尾。本质是一个拖尾粒子。制作同上,素材只需要一张黑色的小圆点图片,只不过不需要随时间进行粒子的生成,而是随移动距离来生成粒子。即Rate over Time数值为0,Rate over Distance数值按需调整即可。由于是圆形粒子,跟随时间的旋转也可以取消。完成后如下图:

然后就是在代码中,暗影冲刺开始时生成对应的特效物体,让特效物体随玩家进行移动,在暗影冲刺结束时停止跟随,并在一段时间后销毁特效物体。代码比较简单,这里就不在贴出了。
拖尾与回复动画
在播放暗影冲刺动画时,还附带有拖尾的帧动画,以及暗影冲刺能量回复的帧动画。一开始制作好相应的帧动画物体,并放在玩家的节点下,在冲刺开始以及结束的时候播放对应动画即可。动画如下:

暗影冲刺动画
在原作中,暗影冲刺在地上进行冲刺,与在空中进行冲刺略有不用。需要在播放动画时,进行判断,此处需要注意!动作如下:

特效物体组合后效果如下:

攻击交互特效
玩家攻击到不同的物体将会有不同的特效提示,此处摸一下鱼,只制作攻击碰撞到陷阱的交互特效。
仍然先将特效的帧动画制作出来。如下:

然后在代码中,攻击检测时进行生成对应的帧动画物体。需要注意的是,不同的攻击方向,生成的物体方向也是不一样的,代码大致如下:
public void CheckAckInteractive(int dir) //参数为当前的攻击方向
{
float distance = 1.8f; //射线的检测长度
RaycastHit2D hit2D = new RaycastHit2D();
Vector2 raySize = new Vector2(boxSize.x , boxSize.y); //射线的大小
switch (dir)
{
case 1:
hit2D = Physics2D.BoxCast(transform.position, raySize, 0, Vector2.left, distance, playerLayerMask);
break;
case 2:
hit2D = Physics2D.BoxCast(transform.position, raySize, 0, Vector2.right, distance, playerLayerMask);
break;
case 3:
hit2D = Physics2D.BoxCast(transform.position, raySize, 0, Vector2.up, distance, playerLayerMask);
break;
case 4:
hit2D = Physics2D.BoxCast(transform.position, raySize, 0, Vector2.down, distance, playerLayerMask);
break;
}
if (hit2D.collider != null)
{
if (hit2D.collider.gameObject.CompareTag("Trap")) //如果是陷阱就有后坐力
{
var tempObj = Instantiate(attactEffectObj, hit2D.point, Quaternion.identity);//攻击交互特效
switch (dir)//根据攻击方向旋转特效物体
{
case 1:
tempObj.transform.rotation = Quaternion.Euler(new Vector3(0, 0, -90));
break;
case 2:
tempObj.transform.rotation = Quaternion.Euler(new Vector3(0, 0, 90));
break;
case 3:
tempObj.transform.rotation = Quaternion.Euler(new Vector3(0, 0, 180));
break;
case 4://默认方向不需要修改
break;
}
}
}
结语
本系列到了现在项目算是完结了。虽然我漏掉了音效添加以及一些其余的细节内容,但是完整工程上面其实已经添加上去了对应的内容,需要的童鞋可以下载了解。文章后面还会有一个打包后游戏文件,欢迎下载试玩。(PS:封面沙雕图来源于百度贴吧@磁力菇1210)
工程下载链接:https://pan.baidu.com/share/init?surl=l3zbN8GScoAR3wp3eHp5sQ
提取码: qd8h
相关链接,很(mai)重(mai)要(mai)
空洞骑士购买链接:https://store.steampowered.com/app/367520/Hollow_Knight/
有线下学习游戏开发打算的童鞋,欢迎访问:http://www.levelpp.com/
线上课程的传送门如下:https://study.163.com/course/introduction/1006462005.htm?share=2&shareId=400000000584006
另有专业开发交(gao)流(ji)群等待大家强势插入:869551769