Unity3D学习——粒子光环

来源:互联网 发布:同在一起网络剧 编辑:程序博客网 时间:2024/05/18 13:28

这次使用粒子系统模仿I Remember网站上面的光环效果
网站静态图

我实现的效果图

粒子系统设置

添加粒子系统到一个空对象下,并且给空对象挂上文末的脚本,设置粒子参数
注意:别忘了把该粒子系统拖到红线框中

设计思路

观察效果发现光环由两层顺时针转动的环组成
如下图,黑色圆环层粒子分布比较疏松范围广,红色圆环层密集而且有两个对称的缺口,并且红色顺时针转动速度较快
两个同心圆环
对于每个圆环,设置最大和最小半径,然后利用随机函数使粒子集中分布在中间半径处
对于粒子的具体位置,我们只需要得到它的实际半径以及圆心角即可转换成对应的(x, y, 0)坐标

  • 如何使粒子集中在圆环中间区域
    这里写图片描述
    如上图,要让粒子尽可能集中在蓝线附近,
    ①随机生成一个半径max到达黄区
    ②随机生成一个半径min在绿色区域内
    ③随机在(min, max)范围内生成新的半径randomRadius,这样randomRadius就会在中间蓝线附近
    代码如下:
  //maxR是最大半径,minR是最小半径  float midRadius = (maxR + minR) / 2;  float min = Random.Range(minR, midRadius);  float max = Random.Range(midRadius, maxR);  float randomRadius = Random.Range(min, max);
  • 如何形成对称的缺口
    因为粒子在两个对称方向分布稀疏,相反我们令它们在另两个方 向分布比较密集即可形成同样效果
    方法和上面的相似,也是利用三次随机函数
    例如,我令粒子在初始状态,密集的分布在0度和180度处
  for (int i = 0; i < particleNum; i++){      ...      //在(-9090)范围内生成集中在0度附近的的角度      float minAngle = Random.Range(-90f, 0.0f);      float maxAngle = Random.Range(0.0f, 90f);      float angle = Random.Range(minAngle, maxAngle);       //一半粒子向0度集中、令一半向180度集中      randomAngle = i % 2 == 0 ? angle : angle - 180;      ...   }
解决了上面两个主要问题后,再说一下粒子的存储、转动和缩放
  • 粒子存储
    为了管理粒子数据,新建粒子数据类,然后为其声明一个数组与粒子数组匹配
  //创建粒子系统  public ParticleSystem particleSystem;  //粒子数组  private ParticleSystem.Particle[] particlesArray;   //粒子属性数组    private particleClass[] particleAttr;   public class particleClass  {        public float radiu = 0.0f;//初始化半径        public float collect_radiu = 0.0f;//集合后的半径        public float temp_radiu = 0.0f;//粒子扩大缩放过程中的暂存半径        public float angle = 0.0f;        public particleClass(float radiu_, float angle_, float collect_)        {            radiu = radiu_;            angle = angle_;            collect_radiu = collect_;            temp_radiu = radiu_;        }   }
    利用一个粒子数组存放了宽环和窄环的所有粒子,试过比例后发现把5/12粒子分配给宽环,剩下的7/12给窄环会比较好看,所以在分配粒子属性时也会先判断是哪个环的粒子再做不同的设置。
  • 粒子转动
    在Update函数中根据不同环的速率改变粒子角度后,根据原来半径和新角度重新分布位置即可
    局部代码如下:
  for (int i = 0; i < particleNum; i++)  {        //窄环快、宽环粒子移动速度慢        if (i > particleNum * 5 / 12)            speed = 0.1f;        else            speed = 0.05f;        //改变粒子角度后即可实现粒子顺时针移动        particleAttr[i].angle -=  speed;        particleAttr[i].angle = particleAttr[i].angle % 360;        float rad = particleAttr[i].angle / 180 * Mathf.PI;        ……        //重新设置粒子位置        particlesArray[i].position = new Vector3(particleAttr[i].now_radiu * Mathf.Cos(rad), particleAttr[i].now_radiu * Mathf.Sin(rad), 0f);
  • 粒子缩放

    • 需要收缩时
      如果粒子当前半径平均半径大(外围粒子),则以一定的速度向预先算好的收缩后的半径缩小;反之(内围)则向预先算好的收缩半径扩大
    • 需要扩张恢复时
      则把当前半径初始半径作比较,移动和上面收缩类似

    为了看起来与网站效果接近一些,我令收缩时外环快、内环慢,扩张时外环慢、内环快。

    局部代码如下:

  //判断需不需要缩放,改变粒子的暂存半径  if(flag == 0)//需要向中间收缩  {        if (particleAttr[i].now_radiu > particleAttr[i].collect_radiu)        {              //两层环的收缩速度不同              if(i < particleNum * 5 / 12)                    particleAttr[i].now_radiu -=  3.0f * Time.deltaTime;              else                    particleAttr[i].now_radiu -= 4.0f * Time.deltaTime;        }        else if(particleAttr[i].now_radiu < particleAttr[i].collect_radiu)        {              if (i < particleNum * 5 / 12)                    particleAttr[i].now_radiu += 2.0f * Time.deltaTime;              else                    particleAttr[i].now_radiu += Time.deltaTime;        }   }   else if(flag == 1)//扩大   {            if (particleAttr[i].now_radiu < particleAttr[i].ini_radiu)            {                if (i < particleNum * 5 / 12)                    particleAttr[i].now_radiu += 2.0f * Time.deltaTime;                else                    particleAttr[i].now_radiu += 3.0f * Time.deltaTime;            }            else if (particleAttr[i].now_radiu > particleAttr[i].ini_radiu)            {                if (i < particleNum * 5 / 12)                    particleAttr[i].now_radiu -= 4.0f * Time.deltaTime;                else                    particleAttr[i].now_radiu -= 4.0f * Time.deltaTime;            }    }

完整代码

using UnityEngine;using System.Collections;public class ParticleRing: MonoBehaviour{    public class particleClass    {        public float ini_radiu = 0.0f;//初始化半径        public float collect_radiu = 0.0f;//集合后的半径        public float now_radiu = 0.0f;//粒子当前时刻半径,用于扩大缩小时与上两者比较        public float angle = 0.0f;        public particleClass(float radiu_, float angle_, float collect_)        {            ini_radiu = radiu_;            angle = angle_;            collect_radiu = collect_;            now_radiu = radiu_;        }    }    //创建粒子系统,    public ParticleSystem particleSystem;    //粒子数组    private ParticleSystem.Particle[] particlesArray;    //粒子属性数组    private particleClass[] particleAttr;     public int particleNum = 12000;    //较宽的环的内外半径    public float outMinRadius = 5.0f;    public float outMaxRadius = 10.0f;    //较窄的环(带缺口)的内外半径    public float inMinRadius = 6.0f;    public float inMaxRadius = 9.0f;    public float speed = 0.1f;    public int flag;    void Start()    {        flag = -1;        particleAttr = new particleClass[particleNum];        particlesArray = new ParticleSystem.Particle[particleNum];        particleSystem.maxParticles = particleNum;        particleSystem.Emit(particleNum);        particleSystem.GetParticles(particlesArray);        for (int i = 0; i < particleNum; i++)        {               //相应初始化操作,为每个粒子设置半径,角度            float randomAngle;            // 随机产生每个粒子距离中心的半径,同时粒子要集中在平均半径附近              float maxR, minR;            if(i < particleNum * 5 / 12)//这部分粒子属于较宽的那个环            {                maxR = outMaxRadius;                minR = outMinRadius;                randomAngle = Random.Range(0.0f, 360.0f);            }            else//窄环带缺口,设置一半向0度集中、一半向180度集中,以便在90度和-90度形成两个对称缺口            {                maxR = inMaxRadius;                minR = inMinRadius;                float minAngle = Random.Range(-90f, 0.0f);                float maxAngle = Random.Range(0.0f, 90f);                float angle = Random.Range(minAngle, maxAngle);                randomAngle = i % 2 == 0 ? angle : angle - 180;//利用对称性设置另一半粒子            }            float midRadius = (maxR + minR) / 2;            float min = Random.Range(minR, midRadius);            float max = Random.Range(midRadius, maxR);            float randomRadius = Random.Range(min, max);            float collectRadius;            //注意设置平均半径外围的粒子缩小时移动的距离少一些            if (randomRadius > midRadius)                collectRadius = randomRadius - (randomRadius - midRadius) / 2;            else                collectRadius = randomRadius - (randomRadius - midRadius) * 3 / 4;            //粒子属性设置            particleAttr[i] = new particleClass(randomRadius, randomAngle, collectRadius);            particlesArray[i].position = new Vector3(randomRadius * Mathf.Cos(randomAngle), randomRadius * Mathf.Sin(randomAngle), 0.0f);        }        //设置粒子        particleSystem.SetParticles(particlesArray, particleNum);    }    void Update()    {        for (int i = 0; i < particleNum; i++)        {            //根据新的角度            if (i > particleNum * 5 / 12)                speed = 0.1f;            else                speed = 0.05f;            particleAttr[i].angle -=  speed;            particleAttr[i].angle = particleAttr[i].angle % 360;            float rad = particleAttr[i].angle / 180 * Mathf.PI;            //判断需不需要缩放,改变粒子的暂存半径            if(flag == 0)//需要向中间收缩            {                if (particleAttr[i].now_radiu > particleAttr[i].collect_radiu)                {                    //两层环的收缩速度不同                    if(i < particleNum * 5 / 12)                        particleAttr[i].now_radiu -=  3.0f * Time.deltaTime;                    else                        particleAttr[i].now_radiu -= 4.0f * Time.deltaTime;                }                else if(particleAttr[i].now_radiu < particleAttr[i].collect_radiu)                {                    if (i < particleNum * 5 / 12)                        particleAttr[i].now_radiu += 2.0f * Time.deltaTime;                    else                        particleAttr[i].now_radiu += Time.deltaTime;                }            }            else if(flag == 1)//扩大            {                if (particleAttr[i].now_radiu < particleAttr[i].ini_radiu)                {                    if (i < particleNum * 5 / 12)                        particleAttr[i].now_radiu += 2.0f * Time.deltaTime;                    else                        particleAttr[i].now_radiu += 3.0f * Time.deltaTime;                }                else if (particleAttr[i].now_radiu > particleAttr[i].ini_radiu)                {                    if (i < particleNum * 5 / 12)                        particleAttr[i].now_radiu -= 4.0f * Time.deltaTime;                    else                        particleAttr[i].now_radiu -= 4.0f * Time.deltaTime;                }            }            //粒子新的位置            particlesArray[i].position = new Vector3(particleAttr[i].now_radiu * Mathf.Cos(rad), particleAttr[i].now_radiu * Mathf.Sin(rad), 0f);        }        particleSystem.SetParticles(particlesArray, particleNum);    }    void OnGUI()    {        if (GUI.Button(new Rect(0, 10, 100, 30), "扩大/缩小"))        {            flag = (flag == -1)? 0: 1 - flag;        }    }}
0 0