unity实现自定义粒子系统

前言

在unity中,就有自带的粒子系统,但是它有一个缺点,就是粒子上很难找到搭载上组件的地方,因此,在本篇博客中,我们就开始学实现一个自定义的粒子系统。

方法

首先,要实现一个粒子系统,得先要让粒子能移动,并且也要让粒子能自动销毁 。因此,就需要自主实现一个临时组件,是particle组件。

particle组件

particle组件实现简单,只需要在Update方法里不断让对象以某个向量前移即可。并等待一小会死亡的时间,然后销毁游戏物体即可,在销毁的途中,我们还可以逐渐让物体颜色变得透明。

csharp 复制代码
public float dieTime = 2f;
public Vector3 direction = Vector3.forward * 0.05f;

private IEnumerator toDie()
{
    for (int i = 0; i < 10; i++)
    {
        yield return new WaitForSeconds(dieTime / 10);
        gameObject.GetComponent<MeshRenderer>().material.color -= new Color(0, 0, 0, 0.1f);
    }
    gameObject.SetActive(false);
}
void Start()
{
    StartCoroutine(toDie());
}
void Update()
{
    transform.localPosition += direction;
}

这两个组件实现好了,接下来就能试着模拟一个粒子了。

模拟好后,也就证明我们的particle组件有效了。


接下去,就要实现粒子系统的核心组件,就是fly组件啦。

fly组件细分一下,有两部分,一部分调整粒子的属性,另一部分则是直接粒子发射出去。我们先把后一部分做好,在做前一部分。

将粒子发射出去

发射一个粒子,就要有一个粒子。要让这个粒子具有particle组件,就要自动给它添加上去。发射粒子要等待,就要一个发射粒子的协程。由于发射粒子时方向要不变,所以在发射时要指定粒子朝向。别忘了要时刻发射粒子。

csharp 复制代码
public GameObject o = 0.1f;
public float summonTime = 0.1f;
private bool isEnd = true;

IEnumerator shot()
{
    isEnd = false;
    yield return new WaitForSeconds(summonTime);
    Instantiate(o, this.transform.position, o.transform.rotation);
    isEnd = true;
}

void Start()
{
    if (null == o.GetComponent<particle>())
    {
        o.transform.AddComponent<particle>();
    }
}

void Update()
{
    if (isEnd)
    {
        StartCoroutine(shot());
    }
}

调整粒子的属性

粒子的属性,最重要的就是粒子的移动的方向。粒子移动的方向在随机同时,要求速度一样,就是向量长度一样,因此,就要用球体函数解决了。

球体函数为 <math xmlns="http://www.w3.org/1998/Math/MathML"> x 2 + y 2 + z 2 = r 2 x^2+y^2+z^2=r^2 </math>x2+y2+z2=r2, <math xmlns="http://www.w3.org/1998/Math/MathML"> x x </math>x和 <math xmlns="http://www.w3.org/1998/Math/MathML"> y y </math>y这两个未知数是随机的,但在粒子移动方向射到球体内壁后忽略 <math xmlns="http://www.w3.org/1998/Math/MathML"> z \mathbf{z} </math>z轴 的某个正方形范围内。假设 <math xmlns="http://www.w3.org/1998/Math/MathML"> r = 1 r=1 </math>r=1,未知数 <math xmlns="http://www.w3.org/1998/Math/MathML"> z z </math>z则可以根据其他的三个未知数来计算,因此,未知数 <math xmlns="http://www.w3.org/1998/Math/MathML"> z z </math>z的计算公式为 <math xmlns="http://www.w3.org/1998/Math/MathML"> z = 1 − x 2 − y 2 z=\sqrt{1-x^2-y^2} </math>z=1−x2−y2 。

但是,此时还有问题,就是 <math xmlns="http://www.w3.org/1998/Math/MathML"> y y </math>y有时会脱离球体的截面,因此,就要用到圆的函数衍生来的公式 <math xmlns="http://www.w3.org/1998/Math/MathML"> y = 1 − x 2 y=\sqrt{1-x^2} </math>y=1−x2 ,以此就解决......了吗?不,由于 <math xmlns="http://www.w3.org/1998/Math/MathML"> y y </math>y要在某个范围内,设范围的边长一半为 <math xmlns="http://www.w3.org/1998/Math/MathML"> a a </math>a,所以最终公式是 <math xmlns="http://www.w3.org/1998/Math/MathML"> y = m i n ( 1 − x 2 , a ) y = min(\sqrt{1-x^2}, a) </math>y=min(1−x2 ,a)。(由于 <math xmlns="http://www.w3.org/1998/Math/MathML"> r r </math>r为 <math xmlns="http://www.w3.org/1998/Math/MathML"> 1 1 </math>1,所以 <math xmlns="http://www.w3.org/1998/Math/MathML"> a a </math>a的最大值为 <math xmlns="http://www.w3.org/1998/Math/MathML"> 1 1 </math>1)

得出这些公式,最后将得到的方向旋转到对象的方向,并设置一下粒子的死亡时间和粒子的速度,就可以设定粒子发射的方向了。

csharp 复制代码
public float a = 0.5f;
public float speed = 0.0618f;
public float dieTime = 2f;

void Update()
{
    if (isEnd)
    {
        if (a < 0)
        {
            a = 0;
        }
        if (a > 1)
        {
            a = 1;
        }
        float x = UnityEngine.Random.Range(-a, a);
        float y = UnityEngine.Random.Range(-(Mathf.Sqrt(1 - x * x) < a ? Mathf.Sqrt(1 - x * x) : a), Mathf.Sqrt(1 - x * x) < a ? Mathf.Sqrt(1 - x * x) : a);
        Vector3 spinPos = transform.rotation * new Vector3(x, y, Mathf.Sqrt(1 - x * x - y * y));
        o.GetComponent<particleRun>().direction = spinPos * speed;
        o.GetComponent<particleRun>().dieTime = dieTime;
        StartCoroutine(shot());
    }
}

接下来,就可以添加这个fly组件以模拟雪了。

之后,我们自制的粒子系统做好了。

后言

在做好粒子系统后,要使粒子能够漂浮,就需要一个浮动的组件,下篇博客教你如何自制一个浮动组件。

示例组件

相关推荐
不爱写代码的玉子1 小时前
HALCON透视矩阵
人工智能·深度学习·线性代数·算法·计算机视觉·矩阵·c#
开开心心就好4 小时前
高效Excel合并拆分软件
开发语言·javascript·c#·ocr·排序算法·excel·最小二乘法
钢铁男儿7 小时前
C# 类和继承(扩展方法)
java·servlet·c#
爱炸薯条的小朋友8 小时前
C#由于获取WPF窗口名称造成的异常报错问题
windows·c#·wpf
Rose 使者10 小时前
全球IP归属地查询接口如何用C#进行调用?
c#·api·ip地址
~plus~12 小时前
Harmony核心:动态方法修补与.NET游戏Mod开发
开发语言·jvm·经验分享·后端·程序人生·c#
htj1012 小时前
C# 使用正则表达式
正则表达式·c#
~plus~12 小时前
WPF八大法则:告别模态窗口卡顿
开发语言·经验分享·后端·程序人生·c#
就是有点傻12 小时前
使用WPF的Microsoft.Xaml.Behaviors.Wpf中通用 UI 元素事件
c#