一、前言
起初在制作塔防小游戏,想要实现红警中磁暴攻击效果,就找到这样一个制作思路就想自己能不能实现一下。
【独游工具箱】使用Unity中LineRenderer组件制作闪电特效_哔哩哔哩_bilibili
效果视频:
unity 使用Line Renderer实现闪电效果_哔哩哔哩_bilibili
效果图:
二、步骤
我的实现比较简单粗暴,从图片可以看出使用了多条线拼合在一起做到的闪电。
所以第一步就多建几个空物体,加上LineRenderer组件。这里cube我是用来充当碰撞体的。
然后,每个空物体的LineRenderer组件中的position给他多加几个操控点,我加了22个,去掉头尾正好20个嘛。
然后给每条线挂在我们的脚本,让脚本控制它当中20个点的位移,模拟闪电不可预测的曲折的路径。怎么实现都可以,我这里参考了第一个视频链接里的方法。
1、首先让它电弧抖动。我直接让每条线所有的控制点去采样一条二次函数。
到这里算是代码的第一部分实现电弧抖动的部分,写成函数就是这样。不过这里是得到二次函数的值,而我们要将控制点慢慢地变换到最后,所以再拿到这个值后取插值。详见完整代码。
float Arcing(float param)
{
//就是一个二次函数写成了代码
return _ArcingPowParam1 * Mathf.Pow((param - (float)posList.Count / 2 + _CenterOffset) * _ArcingPowParam1,2) + _Adjust;
}
2、然后就是让它的抖动有点变化,就是乘上一个Sin的曲线函数,通过缩放和偏移调整到想要的效果。
代码:
float Sine(float param)
{
return Mathf.Sin((float)param / posList.Count * 2 * 3.14f * _SineScaleX) * _SineScaleY;
}
3、然后添加一点抖动。
抖动就是加减随机值。
Vector3 Wiggle(int listIndex)
{
Vector3 v = new Vector3();
if (X) v.x = Random.Range(0, 10) * _RandomSize;
if (Y) v.y = Random.Range(0, 10) * _RandomSize;
if (Z) v.z = Random.Range(0, 10) * _RandomSize;
return v;
}
4、给其他四条线同样挂载脚本,参数微调整体就效果就出来了。
挂载的脚本UI如下:
三、完整代码
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Thunder : MonoBehaviour
{
public int PosSize;
public bool useArcing;
public bool useSine;
public bool useWiggle;
public bool X, Y, Z;
[Range(-1,1)]
public float _ArcingPowParam1;
[Range(0,5)]
public float _SineScaleX;
public float _SineScaleY;
public float _CenterOffset;
public float _Adjust;
public float _RandomSize;
public float _Speed;
LineRenderer lineRenderer;
List<Vector3> posList = new List<Vector3>();
[System.NonSerialized] public Vector3 startPos;
[System.NonSerialized] public Vector3 endPos;
float fps;
float sineRandom;
void Start()
{
lineRenderer = GetComponent<LineRenderer>();
startPos = lineRenderer.GetPosition(0);
endPos = lineRenderer.GetPosition(1);
LerpPos();
}
void Update()
{
fps += Time.deltaTime*_Speed;
if (fps >= 1)
{
sineRandom = (float)Random.Range(0, posList.Count * 10) / 10;
fps = 0;
}
List<Vector3> list = new List<Vector3>();
for (int i = 0; i < posList.Count; i++)
{
Vector3 point = posList[i];
if (useArcing)
{
Vector3 v = new Vector3();
if (X) v.x = Arcing(i);
if (Y) v.y = Arcing(i);
if (Z) v.z = Arcing(i);
point = Vector3.Lerp(posList[i], posList[i] + v, fps);
}
if (useSine && i != 0 && i != posList.Count-1)
{
Vector3 v = new Vector3();
if (X) v.x = Sine(i + sineRandom);
if (Y) v.y = Sine(i + sineRandom);
if (Z) v.z = Sine(i + sineRandom);
point += v;
}
if (useWiggle && i != 0 && i != posList.Count - 1)
point += Wiggle(i);
list.Add(point);
}
SetLinePosition(list);
}
void SetLinePosition(List<Vector3> listPoint)
{
lineRenderer.positionCount = listPoint.Count;
for(int i = 0; i < listPoint.Count; i++)
{
lineRenderer.SetPosition(i, listPoint[i]);
}
}
public void LerpPos()
{
int index = PosSize + 2;
for(int i=0; i < index; i++)
{
if (posList.Count < index)
posList.Add(Vector3.Lerp(startPos, endPos, (float)i / index));
else posList[i] = Vector3.Lerp(startPos, endPos, (float)i / index);
}
}
float Arcing(float param)
{
return _ArcingPowParam1 * Mathf.Pow((param - (float)posList.Count / 2 + _CenterOffset) * _ArcingPowParam1,2) + _Adjust;
}
float Sine(float param)
{
return Mathf.Sin((float)param / posList.Count * 2 * 3.14f * _SineScaleX) * _SineScaleY;
}
Vector3 Wiggle(int listIndex)
{
Vector3 v = new Vector3();
if (X) v.x = Random.Range(0, 10) * _RandomSize;
if (Y) v.y = Random.Range(0, 10) * _RandomSize;
if (Z) v.z = Random.Range(0, 10) * _RandomSize;
return v;
}
}