3D俯视角射击——用Unity还原东方弹幕(上)

作者:QXYO
前言
之前我们的专栏中介绍过2D的俯视角射击,这次就来试试在3D场景下的实现。移动射击的实现方法差不多,所以本次的主要目标是在3D场景下还原东方的符卡(弹幕)效果。
我们先来看看最终结果:


本文主要内容:
1. 3D场景俯视角射击
2. 可配置化弹幕
3. 还原东方弹幕
注:为了提升表现效果而使用的模型动画、屏幕后效(PostProcessing)等方法不在本次教程范围内。同时本文只写出了部分关键代码,完整项目下载详见本文末尾。
一、3D场景俯视角移动
实现目标:键盘移动,鼠标射击。不射击时面向移动方向,射击时面向射击方向,转向有转向速度,射击方向为鼠标点击方向。
在之前2D俯视角射击中已经介绍过移动的方法,这里就不再赘述,想要了解的同学可参考:
当然3D人物转向应需要转向速度,否则会显得很突兀,可用 Quaternion.LookRotation控制转向, Quaternion.Slerp控制转向速度。
二、弹幕可配置化
实现目标:通过修改Inspector面板中的参数,即可实现简单的弹幕样式。
本次主要实现两种弹幕样式,散射弹幕和平行弹幕。实现这两种样式需要的参数(BulletData)如下。
public int Count = 1; //一次生成的子弹的数量
public float LifeTime = 4f; //子弹生命周期
public float CdTime = 0.1f; //子弹间隔时间
public float Speed = 10; //子弹移动速度
public float Angle = 0; //相邻子弹间的旋转角度
public float Distance = 0; // 相邻子弹的间隔
public Color BulletColor = Color.white; //子弹的颜色
public Vector3 R_Offset = Vector3.zero; //初始旋转的偏移量
public Vector3 P_Offset = Vector3.zero; //位置的偏移量
首先创建个球体作为子弹预制体,OnEnable时,在FixedUpdate实现移动,当规定了子弹的位置和方向,子弹就会朝着正前方移动。
transform.position = transform.position + transform.forward * BulletSpeed * Time.fixedDeltaTime;
然后是通过设定的参数实现弹幕样式:散射弹幕,如下图所示,以角色正前方为初始方向,当单次弹幕数量为奇数或是偶数时,子弹旋转角度的逻辑是不一样的。


同理平行弹幕也不相同,下面给出代码,其实就是一个简单的数学运算。
int num = bulletData.Count / 2;
for (int i = 0; i < bulletData.Count; i++)
{
GameObject go = pool.Get(bulletData.Position, bulletData.LifeTime);
//从对象池中获取,对对象池不了解,不需要优化的话可以直接实例子弹的预制体
Bullet bullet = go.GetComponent<Bullet>();
go.GetComponent<Renderer>().material.SetColor("_EmissionColor",bulletData.color);//修改子弹外发光颜色
bullet.BulletSpeed = bulletData.Speed;
if (bulletData.Count % 2 == 1)
{
go.transform.rotation = bulletData.direction * Quaternion.Euler(0, bulletData.Angle * num, 0);
go.transform.position = go.transform.position + go.transform.right * num * bulletData.Distance;
num--;
}
else
{
go.transform.rotation = bulletData.direction * Quaternion.Euler(0, bulletData.Angle / 2 + bulletData.Angle * (num - 1), 0);
go.transform.position = go.transform.position + go.transform.right * ((num - 1) * bulletData.Distance + bulletData.Distance / 2);
num--;
}
}
代码中使用到了对象池相关知识,在本专栏之前文章中有详细介绍过对象池,详情可见:
控制射击间隔时间可以用协程的方式达成,我这里用了一个偷懒的方法:因为在FixedUpdate中每秒是固定运行50次(可在Project Setting中修改),所以在FixedUpdate中可以用i++来控制时间
if (LimitI > CdTime * 50)
{
...
//执行射击方法
LimitI = 0;
}
三、还原弹幕效果
1.波与粒的境界
用简单的函数绘制成优美的弹幕,在文章开头大家应该已经看到了所实现的效果,这里再放上一张对比图。

新建个空物体用来发射弹幕,除了之前提到的弹幕所需参数外,还需要
public float SelfRotation = 0; // 每帧自转角度
public float AddRotation = 0; // 每帧自转角度增量
实现其实非常简单,每次发射5发子弹,角度间隔为360/5°,然后发射子弹的物体每帧进行越来越快的旋转,弹幕就能形成上面的效果。
SelfRotation += AddRotation;
SelfRotation = SelfRotation >= 360 ? SelfRotation - 360 : SelfRotation;
//为了防止数值过大进行了限制。
var q = Quaternion.Euler(0, SelfRotation, 0);
transform.rotation = transform.rotation * q;
if (LimitI > CdTime * 50)
{
BulletManager.Instance.ShootConfig(bulletData, m_bullet1_pool);
LimitI = 0;
}
附上具体参数

2.怨灵猫乱步

可以看到子弹是从11个不同位置生成,每次生成4圈弹幕,每圈弹幕角度随机,经过一定时间后才开始移动。
子弹的延时还是通过记录一个DelayI来实现。
void FixedUpdate()
{
if (Shoot)
{
Shooting();
}
else if(DelayI > DelayTime * 50)
{
Shoot = true;
}
else
{
DelayI++;
}
}
private void OnEnable()
{
DelayI = 0;
Shoot = false;
}
同时还需要一个与发射中心距离的参数
public float CenterDis = 0; // 与发射点的距离
修改子弹位置到中心点前方
go.transform.position = go.transform.position + go.transform.forward * bulletData.CenterDis;
来看看效果

详细参数可以下载本文末尾的项目自行查看。想要完美还原的话,剩下的就是将发射物体移动到指定位置、调整延迟时间等,可以使用Dotween等方式实现,本次就不再进行展示了。
由于篇幅原因本次只展示了这么多,有兴趣的同学可以自己尝试去实现其他符卡。
最后不要忘了这是在3D场景下的弹幕,所以这样也是能做到的。

工程地址:https://pan.baidu.com/share/init?surl=JEXp9FsNswaihwSBkiHs-Q
提取码:mpik

对游戏开发学习感兴趣的盆友,欢迎访问:http://levelpp.com/
同时,也欢迎加入游戏开发群搅基:610475807