作者:QXYO
前言
在上篇中只介绍了两种弹幕样式的生成,很明显对于一款弹幕游戏来说是远远不够的,所以本次就再来尝试还原一下东方的两种弹幕,反射子弹和曲线激光。
同样的先来看看效果
反射子弹
曲线激光
本文主要内容:
- 控制子弹的反射
2. 曲线激光的生成
一、子弹在矩形范围内反射
虽然标题是矩形,但同样可以控制子弹是否只在单面反射。在Unity中可以用Vector3.Reflect
public static Vector3 Reflect(Vector3 inDirection, Vector3 inNormal)
来获取到子弹与指定平面的反射角,并且有多方法来判断子弹是否到了指定平面,碰撞检测、射线、数学运算等方式。这里我就用数学方法来实现子弹反射。
首先定义矩形参数
public float RectangleX = 0; //长方形的一个边
public float RectangleZ = 0; //长方形的一个边
public Transform CenterPos; //图形中心点
如果是未旋转的矩形那么边界平面就很好计算了,就是中心点位置+长或宽/2的位置,如果是旋转过的矩形那么就需要点简单的数学计算了,这里就不再展示。
var disX = transform.position.x - CenterPos.position.x;
var AbsdisX = disX > 0 ? disX : -disX;
//取x轴距离的绝对值,在数值不溢出的情况下比Mathf.Abs()效率高
var disZ = transform.position.z - CenterPos.position.z;
var AbsdisZ = disZ > 0 ? disZ : -disZ;
获取到边界后通过之前提到的 Vector3.Reflect方法就可以获取到反射角,传入的参数为方向也就是transform.forward和平面的法向量。
...
return Vector3.Reflect(transform.forward, disX > 0 ? -CenterPos.right : CenterPos.right);
...
return Vector3.Reflect(transform.forward, disZ > 0 ? -CenterPos.forward : CenterPos.forward);
...
如果中心点位置每帧移动速度过快,内部的子弹就会“逃出”反射范围,但仍然会在原范围内进行反射移动,所以可以添加判断在范围外的子弹不进行检测。
下面给出完整代码
if (isRectangleRef) //长方形检测
{
var disX = transform.position.x - CenterPos.position.x;
var AbsdisX = disX > 0 ? disX : -disX; //取x轴距离的绝对值,在数值不溢出的情况下比Mathf.Abs()效率高
var disZ = transform.position.z - CenterPos.position.z;
var AbsdisZ = disZ > 0 ? disZ : -disZ;
if (RectangleX !=0 && !isOut && AbsdisX > RectangleX)
{
isOut = true;
return Vector3.Reflect(transform.forward, disX > 0 ? -CenterPos.right : CenterPos.right);
}
if (RectangleZ != 0 && !isOut && AbsdisZ > RectangleZ)
{
isOut = true;
return Vector3.Reflect(transform.forward, disZ > 0 ? -CenterPos.forward : CenterPos.forward);
}
if (AbsdisX < RectangleX && AbsdisZ < RectangleZ)
{
isOut = false;
}
}
二、子弹在圆形范围内反射
原理和之前一样,只不过要通过算距离来判断是否到达边界。需要参数:距离圆心的半径(radius)。注意检测距离一般用距离的平方来判断,减少开根号带来的性能消耗。
var dis =(transform.position - CenterPos.position).sqrMagnitude;
到达边界之后的法线向量其实就是该点朝向圆心的向量,即(CenterPos.position - transform.position)
if (!isOut) //防止在圈外重复检测
{
if (dis > Radius * Radius && dis < Radius * Radius + 4) //防止移动过快导致圆圈外的子弹的运动问题
{
isOut = true;
return Vector3.Reflect(transform.forward, (CenterPos.position - transform.position).normalized);//法线向量就是当前位置朝向圆形半径的向量
}
}
else
{
if (dis < Radius * Radius)
{
isOut = false;
}
}
来看下效果
三、曲线激光
形成直线激光子弹的方式有很多,特效粒子、长方形预制体等。但这些方法实现方便配置的曲线激光比较困难,所以本次使用的是LineRenderer组件。生成的曲线为正、余弦曲线。
本次主要用到的LineRenderer 属性:
Positions: 设置线的开始点和结束点的位置。曲线就是生成了许多的位置信息并将其连接起来形成曲线。
Start Width:设置线开始时的宽度。
End Width:设置线结束时的宽度。
Use World Space:是否用世界坐标,可以根据需求判断是否勾选,本方法取消了勾选。
同样的先设置参数
public float frequency; //频率
public float amplitude; //振幅
public float lineDistance; //相邻两个点的距离
public float width; //设置宽度
public int lineCount; //曲线生成所需点个数,越多越平滑
正、余弦曲线的偏移量(Y的值)可以很简单的通过 Mathf.Cos()来获取,曲线的移动有几种方法:
1、通过开始就计算出Y轴的偏移量,使LineRenderer的每个Position的相对于自身旋转的X轴不变,Y轴随时间变化为计算的偏移量,因为取消勾选了Use World Space,所以可以通过修改自身的transform.position来实现移动。
void GetCurve() //获取曲线坐标
{
for (int i = 0; i < lineCount; i++)
{
float t = frequency * Mathf.PI / lineCount * i;
linePos[i] = new Vector3(disI * i, 1, Mathf.Cos(t) * amplitude);
}
}
2、LineRenderer的前一个点的位置是后一个点下一次移动到的位置,这样每帧修改第一个点的位置即可。为了防止激光突兀的全部显示,点位要逐个生成。本次生成和移动的方式是通过协程来实现。
float GetPosY(float x) //获取Y轴偏移量
{
return Mathf.Cos(frequency * x * Mathf.PI) * amplitude;
}
...
IEnumerator Move()
{
if (CreatCount == lineCount) //判断点位是否全部生成完毕
{
linePos = new Vector3[lineRenderer.positionCount];
lineRenderer.GetPositions(linePos); // 重新获取当前点位数据
}
var offset = new Vector3(GetPosY(lineDistance / lineCount * disI), 1, lineDistance / lineCount * disI);
//获取本次移动后第一个点的位置
disI++;
if (CreatCount < lineCount) //逐步生成点位
{
CreatCount++;
lineRenderer.positionCount = CreatCount;
}
lineRenderer.SetPosition(CreatCount - 1, offset);
linePos[CreatCount - 1] = offset;
for (int i = CreatCount - 2; i >= 0; i--) //跟随前一个点位
{
lineRenderer.SetPosition(i, linePos[i + 1]);
}
yield return new WaitForSeconds(speed * Time.fixedDeltaTime);
}
以上都是在3D场景下的弹幕,大家有兴趣也可以修改成3D样式。
由于还原东方弹幕所需要大量时间进行微调参数,所以本次就只将一些基础弹幕样式展现出来供大家参考,最后放上工程链接,建议使用2019以上的版本打开:
地址:
https://pan.baidu.com/share/init?surl=WbHvaViGDLlwBTP3hIwziA
提取码:vf43
对游戏开发学习感兴趣的盆友,欢迎访问:http://levelpp.com/
同时,也欢迎加入游戏开发群搅基:610475807