Unity3D学习——粒子光环

这次使用粒子系统模仿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;
        }
    }
}
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值