• 粒子系统


    学习了3D游戏的粒子系统,参考了I-remember上的例子光环,决定自己做一个。开始动手之前,需要细心观察并发现,光环分两层,一层顺时针旋转另外一层逆时针旋转,且中间部分粒子较多;粒子在旋转的同时会在光环之间游离。

    基础准备

    新建空对象ParticleCircle,再建两个子对象Clockwise和Anticlockwise,为其添加Particle System组件,新建ParticleCircle脚本并挂载在子对象上:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    using UnityEngine;
    using System.Collections;
    public class CirclePosition
    {
    public float radius = 0f, angle = 0f, time = 0f;
    public (float radius, float angle, float time)
    {
    this.radius = radius;
    this.angle = angle;
    this.time = time;
    }
    }
    public class ParticleCircle : MonoBehaviour {
    private ParticleSystem particleSys; // 粒子系统
    private ParticleSystem.Particle[] particleArr;
    private CirclePosition[] circle; // 极坐标数组
    public int count = 10000; // 粒子数量
    public float size = 3f; // 粒子大小
    public float minRadius = 4.0f; // 最小半径
    public float maxRadius = 8.0f; // 最大半径
    public bool clockwise = true; // 顺时针|逆时针
    public float speed = 2f; // 速度
    public float pingPong = 0.02f; // 游离范围
    private int tier = 10; // 速度差分层数
    public Gradient colorGradient; // 颜色控制
    void RandomlySpread() // 随机布置粒子位置
    {
    for (int i = 0; i < count; ++i)
    {
    float midRadius = (maxRadius + minRadius)/2;
    float minRate = Random.Range(1.0f, midRadius/minRadius);
    float maxRate = Random.Range(midRadius/maxRadius, 1.0f);
    float radius = Random.Range(minRadius * minRate, maxRadius * maxRate);
    float angle = Random.Range(0.0f, 360.0f); // 随机每个粒子的角度
    float theta = angle/180 * Mathf.PI;
    float time = Random.Range(0.0f, 360.0f); // 随机每个粒子的游离起始时间
    circle[i] = new CirclePosition(radius, angle, time);
    particleArr[i].position = new Vector3(circle[i].radius * Mathf.Cos(theta), 0f, circle[i].radius * Mathf.Sin(theta));
    }
    particleSys.SetParticles(particleArr, particleArr.Length);
    }
    void Start () {
    particleArr = new ParticleSystem.Particle[count]; // 初始化粒子数组
    circle = new CirclePosition[count];
    particleSys = this.GetComponent<ParticleSystem>(); // 初始化粒子系统
    particleSys.startSpeed = 0;
    particleSys.startSize = size;
    particleSys.loop = false;
    particleSys.maxParticles = count; // 设置最大粒子量
    particleSys.Emit(count); // 发射粒子
    particleSys.GetParticles(particleArr);
    RandomlySpread(); // 初始化各粒子位置
    }

    旋转粒子

    在Update函数里逐渐改变粒子的角度使粒子旋转,其中tier使粒子角度改变量不一致,这样粒子旋转就不会看起来像是图片在旋转:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    void Update () { // 通过改变角度使粒子旋转
    for (int i = 0; i < count; i++)
    {
    if (clockwise)
    circle[i].angle -= (i%tier + 1)*(speed/circle[i].radius/tier);
    else
    circle[i].angle += (i%tier + 1)*(speed/circle[i].radius/tier);
    circle[i].angle = (360.0f + circle[i].angle)%360.0f; // 保证angle在0~360度
    float theta = circle[i].angle/180 * Mathf.PI;
    particleArr[i].position = new Vector3(circle[i].radius * Mathf.Cos(theta), 0f, circle[i].radius * Mathf.Sin(theta));
    }
    particleSys.SetParticles(particleArr, particleArr.Length);
    }

    游离粒子

    Unity的Mathf的PingPong函数使得值在范围内来回变动,使用它让粒子半径来回变动。以下添加到Update函数中:

    1
    2
    circle[i].time += Time.deltaTime;
    circle[i].radius += Mathf.PingPong(circle[i].time / minRadius / maxRadius, pingPong) - pingPong / 2.0f;

    添加透明度

    仔细观察i-remember会发现不同地方亮度不同,也可以说是透明度不同,使用Gradient类可以解决这个问题。一下添加到start函数中:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    GradientAlphaKey[] alphaKeys = new GradientAlphaKey[5]; // 初始化梯度颜色控制器
    alphaKeys[0].time = 0.0f; alphaKeys[0].alpha = 1.0f;
    alphaKeys[1].time = 0.4f; alphaKeys[1].alpha = 0.4f;
    alphaKeys[2].time = 0.6f; alphaKeys[2].alpha = 1.0f;
    alphaKeys[3].time = 0.9f; alphaKeys[3].alpha = 0.4f;
    alphaKeys[4].time = 1.0f; alphaKeys[4].alpha = 0.9f;
    GradientColorKey[] colorKeys = new GradientColorKey[2];
    colorKeys[0].time = 0.0f; colorKeys[0].color = Color.white;
    colorKeys[1].time = 1.0f; colorKeys[1].color = Color.white;
    大专栏  粒子系统 class="line">colorGradient.SetKeys(colorKeys, alphaKeys);

    然后在Update中根据粒子的角度改变粒子的透明度:

    1
    particleArr[i].color = colorGradient.Evaluate(circle[i].angle / 360.0f);

    修改参数

    修改主摄像机到合适位置:

    另外一层是逆时针旋转的,因此把clockwise的勾去掉,同时修改参数直到效果满意:

    完整代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    using UnityEngine;
    using System.Collections;
    public class CirclePosition
    {
    public float radius = 0f, angle = 0f, time = 0f;
    public (float radius, float angle, float time)
    {
    this.radius = radius;
    this.angle = angle;
    this.time = time;
    }
    }
    public class ParticleCircle : MonoBehaviour {
    private ParticleSystem particleSys; // 粒子系统
    private ParticleSystem.Particle[] particleArr;
    private CirclePosition[] circle; // 极坐标数组
    public int count = 10000; // 粒子数量
    public float size = 3f; // 粒子大小
    public float minRadius = 4.0f; // 最小半径
    public float maxRadius = 8.0f; // 最大半径
    public bool clockwise = true; // 顺时针|逆时针
    public float speed = 2f; // 速度
    public float pingPong = 0.02f; // 游离范围
    private int tier = 10; // 速度差分层数
    public Gradient colorGradient; // 颜色控制
    void RandomlySpread() // 随机布置粒子位置
    {
    for (int i = 0; i < count; ++i)
    {
    float midRadius = (maxRadius + minRadius)/2;
    float minRate = Random.Range(1.0f, midRadius/minRadius);
    float maxRate = Random.Range(midRadius/maxRadius, 1.0f);
    float radius = Random.Range(minRadius * minRate, maxRadius * maxRate);
    float angle = Random.Range(0.0f, 360.0f); // 随机每个粒子的角度
    float theta = angle/180 * Mathf.PI;
    float time = Random.Range(0.0f, 360.0f); // 随机每个粒子的游离起始时间
    circle[i] = new CirclePosition(radius, angle, time);
    particleArr[i].position = new Vector3(circle[i].radius * Mathf.Cos(theta), 0f, circle[i].radius * Mathf.Sin(theta));
    }
    particleSys.SetParticles(particleArr, particleArr.Length);
    }
    void Start () {
    particleArr = new ParticleSystem.Particle[count]; // 初始化粒子数组
    circle = new CirclePosition[count];
    particleSys = this.GetComponent<ParticleSystem>(); // 初始化粒子系统
    particleSys.startSpeed = 0;
    particleSys.startSize = size;
    particleSys.loop = false;
    particleSys.maxParticles = count; // 设置最大粒子量
    particleSys.Emit(count); // 发射粒子
    particleSys.GetParticles(particleArr);
    RandomlySpread(); // 初始化各粒子位置
    GradientAlphaKey[] alphaKeys = new GradientAlphaKey[5]; // 初始化梯度颜色控制器
    alphaKeys[0].time = 0.0f; alphaKeys[0].alpha = 1.0f;
    alphaKeys[1].time = 0.4f; alphaKeys[1].alpha = 0.4f;
    alphaKeys[2].time = 0.6f; alphaKeys[2].alpha = 1.0f;
    alphaKeys[3].time = 0.9f; alphaKeys[3].alpha = 0.4f;
    alphaKeys[4].time = 1.0f; alphaKeys[4].alpha = 0.9f;
    GradientColorKey[] colorKeys = new GradientColorKey[2];
    colorKeys[0].time = 0.0f; colorKeys[0].color = Color.white;
    colorKeys[1].time = 1.0f; colorKeys[1].color = Color.white;
    colorGradient.SetKeys(colorKeys, alphaKeys);
    }
    void Update () { // 通过改变角度使粒子旋转
    for (int i = 0; i < count; i++)
    {
    if (clockwise)
    circle[i].angle -= (i%tier + 1)*(speed/circle[i].radius/tier);
    else
    circle[i].angle += (i%tier + 1)*(speed/circle[i].radius/tier);
    circle[i].angle = (360.0f + circle[i].angle)%360.0f; // 保证angle在0~360度
    float theta = circle[i].angle/180 * Mathf.PI;
    particleArr[i].position = new Vector3(circle[i].radius * Mathf.Cos(theta), 0f, circle[i].radius * Mathf.Sin(theta));
    circle[i].time += Time.deltaTime; // 使粒子半径波动
    circle[i].radius += Mathf.PingPong(circle[i].time/minRadius/maxRadius, pingPong) - pingPong/2.0f;
    particleArr[i].color = colorGradient.Evaluate(circle[i].angle/360.0f); // 根据角度改变透明度
    }
    particleSys.SetParticles(particleArr, particleArr.Length);
    }
    }

    ParticleSea

    以下是另外一个利用粒子系统写的粒子海洋,有兴趣可以做做:

  • 相关阅读:
    线程安全的signals
    排序
    TCMalloc : ThreadCaching Malloc
    C++箴言:争取异常安全的代码
    windows 内存泄露debug的相关函数
    分析几个驱动
    CAsyncSocket对象不能跨线程之分析 (转载)
    优化理论
    标 题: C++0x把Concept去掉了
    几个流行的http 服务器开源软件
  • 原文地址:https://www.cnblogs.com/lijianming180/p/12433283.html
Copyright © 2020-2023  润新知