Unity3D学习(12)之粒子光环

2023-10-20 21:59
文章标签 学习 粒子 unity3d 光环

本文主要是介绍Unity3D学习(12)之粒子光环,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

        这是师兄的博客,写得很棒,所以转载和大家分享,心中万分膜拜,参考网站首页的光环效果:http://i-remember.fr/en 

        利用Unity做了一个类似的光环:(后面还有进阶效果哦~)


        可以观察到光环有最小半径和最大半径,并且光环的中间部分比边缘有更多的粒子。眼尖的可以发现这个光环至少有2层,外环顺时针旋转,内环逆时针旋转。除此以外,每个粒子都会游离,并不是规规矩矩地转圈。

        我是这么设计的:

        1. 所有粒子运动由程序控制。

        2. 使用参数方程 x = cos(t), y = sin(t) 计算粒子位置,其中t是角度。

        3. 使用PingPong函数让粒子在半径方向上游离。


步骤一

        新建一个空对象,重命名为ParticleHalo,然后在其下面新建一个空的子对象,重命名为Clockwise_outer。


        选择子对象,添加组件->Effects->Particle System。


步骤二

        新建C#脚本,命名为ParticleHalo。

[csharp] view plain copy
print ? 在CODE上查看代码片 派生到我的代码片
  1. using UnityEngine;  
  2. using System.Collections;  
  3.   
  4. public class ParticleHalo : MonoBehaviour  
  5. {  
  6.     void Start ()  
  7.     {  
  8.     }  
  9.   
  10.     void Update ()  
  11.     {  
  12.     }  
  13. }  
using UnityEngine;
using System.Collections;public class ParticleHalo : MonoBehaviour
{void Start (){}void Update (){}
}

步骤三

        定义新的结构CirclePosition,用来记录每个粒子的当前半径、角度和时间,其中时间是做游离运动需要的。

[csharp] view plain copy
print ? 在CODE上查看代码片 派生到我的代码片
  1. public class CirclePosition  
  2. {  
  3.     public float radius = 0f, angle = 0f, time = 0f;  
  4.     public CirclePosition(float radius, float angle, float time)  
  5.     {  
  6.         this.radius = radius;   // 半径  
  7.         this.angle = angle;     // 角度  
  8.         this.time = time;       // 时间  
  9.     }  
  10. }  
public class CirclePosition
{public float radius = 0f, angle = 0f, time = 0f;public CirclePosition(float radius, float angle, float time){this.radius = radius;   // 半径this.angle = angle;     // 角度this.time = time;       // 时间}
}
        声明ParticleHalo的私有变量,粒子系统和粒子是必须的,CirclePosition对应每个粒子,因此也是必须的。

[csharp] view plain copy
print ? 在CODE上查看代码片 派生到我的代码片
  1. private ParticleSystem particleSys;  // 粒子系统  
  2. private ParticleSystem.Particle[] particleArr;  // 粒子数组  
  3. private CirclePosition[] circle; // 极坐标数组  
    private ParticleSystem particleSys;  // 粒子系统private ParticleSystem.Particle[] particleArr;  // 粒子数组private CirclePosition[] circle; // 极坐标数组
        其次粒子的数量、粒子大小、旋转的最大最小半径等也得有。

[csharp] view plain copy
print ? 在CODE上查看代码片 派生到我的代码片
  1. public int count = 10000;       // 粒子数量  
  2. public float size = 0.03f;      // 粒子大小  
  3. public float minRadius = 5.0f;  // 最小半径  
  4. public float maxRadius = 12.0f; // 最大半径  
  5. public bool clockwise = true;   // 顺时针|逆时针  
  6. public float speed = 2f;        // 速度  
  7. public float pingPong = 0.02f;  // 游离范围  
    public int count = 10000;       // 粒子数量public float size = 0.03f;      // 粒子大小public float minRadius = 5.0f;  // 最小半径public float maxRadius = 12.0f; // 最大半径public bool clockwise = true;   // 顺时针|逆时针public float speed = 2f;        // 速度public float pingPong = 0.02f;  // 游离范围
        ParticleHalo开始的时候需要设置粒子发射器的参数,因为粒子的运动全部由程序实现,所以记得要把粒子的初始速度设置为0。

[csharp] view plain copy
print ? 在CODE上查看代码片 派生到我的代码片
  1. void Start ()  
  2. {   // 初始化粒子数组  
  3.     particleArr = new ParticleSystem.Particle[count];  
  4.     circle = new CirclePosition[count];  
  5.   
  6.     // 初始化粒子系统  
  7.     particleSys = this.GetComponent<ParticleSystem>();  
  8.     particleSys.startSpeed = 0;            // 粒子位置由程序控制  
  9.     particleSys.startSize = size;          // 设置粒子大小  
  10.     particleSys.loop = false;  
  11.     particleSys.maxParticles = count;      // 设置最大粒子量  
  12.     particleSys.Emit(count);               // 发射粒子  
  13.     particleSys.GetParticles(particleArr);  
  14.   
  15.     RandomlySpread();   // 初始化各粒子位置  
  16. }  
    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();   // 初始化各粒子位置}

        RandomlySpread将所有的粒子随机分布在圆圈轨道上。

[csharp] view plain copy
print ? 在CODE上查看代码片 派生到我的代码片
  1. void RandomlySpread()  
  2. {  
  3.     for (int i = 0; i < count; ++i)  
  4.     {   // 随机每个粒子距离中心的半径,同时希望粒子集中在平均半径附近  
  5.         float midRadius = (maxRadius + minRadius) / 2;  
  6.         float minRate = Random.Range(1.0f, midRadius / minRadius);  
  7.         float maxRate = Random.Range(midRadius / maxRadius, 1.0f);  
  8.         float radius = Random.Range(minRadius * minRate, maxRadius * maxRate);  
  9.   
  10.         // 随机每个粒子的角度  
  11.         float angle = Random.Range(0.0f, 360.0f);  
  12.         float theta = angle / 180 * Mathf.PI;  
  13.   
  14.         // 随机每个粒子的游离起始时间  
  15.         float time = Random.Range(0.0f, 360.0f);  
  16.   
  17.         circle[i] = new CirclePosition(radius, angle, time);  
  18.   
  19.         particleArr[i].position = new Vector3(circle[i].radius * Mathf.Cos(theta), 0f, circle[i].radius * Mathf.Sin(theta));  
  20.     }  
  21.   
  22.     particleSys.SetParticles(particleArr, particleArr.Length);  
  23. }  
    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);}
        OK,将脚本挂载在Closkwise_outer上,此时可以试着运行一下,成功的话会出现这样的画面:


步骤四

        嗯,怎么让粒子旋转起来呢?简单的想法是在Update函数里逐渐改变粒子的角度:

[csharp] view plain copy
print ? 在CODE上查看代码片 派生到我的代码片
  1. void Update ()  
  2. {  
  3.     for (int i = 0; i < count; i++)  
  4.     {  
  5.         if (clockwise)  // 顺时针旋转  
  6.             circle[i].angle -= 0.1f;  
  7.         else            // 逆时针旋转  
  8.             circle[i].angle += 0.1f;  
  9.   
  10.         // 保证angle在0~360度  
  11.         circle[i].angle = (360.0f + circle[i].angle) % 360.0f;  
  12.         float theta = circle[i].angle / 180 * Mathf.PI;  
  13.   
  14.         particleArr[i].position = new Vector3(circle[i].radius * Mathf.Cos(theta), 0f, circle[i].radius * Mathf.Sin(theta));  
  15.     }  
  16.   
  17.     particleSys.SetParticles(particleArr, particleArr.Length);  
  18. }  
    void Update (){for (int i = 0; i < count; i++){if (clockwise)  // 顺时针旋转circle[i].angle -= 0.1f;else            // 逆时针旋转circle[i].angle += 0.1f;// 保证angle在0~360度circle[i].angle = (360.0f + circle[i].angle) % 360.0f;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);}
        运行一下,你很快会发现,有点假!特别是当粒子比较大比较明显的时候。

        没错,这是因为我们为每个粒子角度添加的增量都是一样的,因此看上去就好像是一张图片在旋转,而不是每个粒子在运动。

        为解决这个问题,我让粒子角度的增量不全部一样,需要添加一个差分层变量tier:

[csharp] view plain copy
print ? 在CODE上查看代码片 派生到我的代码片
  1. private int tier = 10;  // 速度差分层数  
  2. void Update ()  
  3. {  
  4.     for (int i = 0; i < count; i++)  
  5.     {  
  6.         if (clockwise)  // 顺时针旋转  
  7.             circle[i].angle -= (i % tier + 1) * (speed / circle[i].radius / tier);  
  8.         else            // 逆时针旋转  
  9.             circle[i].angle += (i % tier + 1) * (speed / circle[i].radius / tier);  
  10.   
  11.         // 保证angle在0~360度  
  12.         circle[i].angle = (360.0f + circle[i].angle) % 360.0f;  
  13.         float theta = circle[i].angle / 180 * Mathf.PI;  
  14.   
  15.         particleArr[i].position = new Vector3(circle[i].radius * Mathf.Cos(theta), 0f, circle[i].radius * Mathf.Sin(theta));  
  16.     }  
  17.   
  18.     particleSys.SetParticles(particleArr, particleArr.Length);  
  19. }  
    private int tier = 10;  // 速度差分层数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);// 保证angle在0~360度circle[i].angle = (360.0f + circle[i].angle) % 360.0f;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);}
        有了这个差分层变量,所有粒子分成了10个阵营,每个阵营角度增量不一样。我还在后面添加了一个系数,这个系数和粒子的半径关联,使得离中心越远的粒子角度增量越小,转得越慢。有了这个变化,再运行一下,发现粒子运动没那么死板了。


步骤五

        现在粒子已经可以旋转了,但是还是感觉有点死板,为什么呢?原来是因为粒子只在角度变化上有了区分,在半径方向上还是很统一。有什么办法能令到粒子的半径在允许的波动范围内移动呢?非常幸运的是,Unity的Mathf类提供了方法PingPong。PingPong顾名思义就是乒乓球的意思,乒乓球是来回运动的,因此PingPong函数就是使得值在范围内来回变动,使用方法具体参考 API。

        把下面的程序添加到Update方法中:

[csharp] view plain copy
print ? 在CODE上查看代码片 派生到我的代码片
  1. // 粒子在半径方向上游离  
  2. circle[i].time += Time.deltaTime;  
  3. circle[i].radius += Mathf.PingPong(circle[i].time / minRadius / maxRadius, pingPong) - pingPong / 2.0f;  
            // 粒子在半径方向上游离circle[i].time += Time.deltaTime;circle[i].radius += Mathf.PingPong(circle[i].time / minRadius / maxRadius, pingPong) - pingPong / 2.0f;
        然后看看效果是不是好点了:


步骤六

        现在的粒子已经近乎独立运动了,运动没问题了,接下来就是光效了。光效怎么体现?透明度是一个不错的方法。使用透明度可以使用Gradient类。在ParticleHalo类里添加新的私有变量,并在Start里面初始化Gradient对象。

[csharp] view plain copy
print ? 在CODE上查看代码片 派生到我的代码片
  1. public Gradient colorGradient;  
  2. void Start ()  
  3. {   // 初始化粒子数组  
  4.     particleArr = new ParticleSystem.Particle[count];  
  5.     circle = new CirclePosition[count];  
  6.   
  7.     // 初始化粒子系统  
  8.     particleSys = this.GetComponent<ParticleSystem>();  
  9.     particleSys.startSpeed = 0;            // 粒子位置由程序控制  
  10.     particleSys.startSize = size;          // 设置粒子大小  
  11.     particleSys.loop = false;  
  12.     particleSys.maxParticles = count;      // 设置最大粒子量  
  13.     particleSys.Emit(count);               // 发射粒子  
  14.     particleSys.GetParticles(particleArr);  
  15.   
  16.     // 初始化梯度颜色控制器  
  17.     GradientAlphaKey[] alphaKeys = new GradientAlphaKey[5];  
  18.     alphaKeys[0].time = 0.0f; alphaKeys[0].alpha = 1.0f;  
  19.     alphaKeys[1].time = 0.4f; alphaKeys[1].alpha = 0.4f;  
  20.     alphaKeys[2].time = 0.6f; alphaKeys[2].alpha = 1.0f;  
  21.     alphaKeys[3].time = 0.9f; alphaKeys[3].alpha = 0.4f;  
  22.     alphaKeys[4].time = 1.0f; alphaKeys[4].alpha = 0.9f;  
  23.     GradientColorKey[] colorKeys = new GradientColorKey[2];  
  24.     colorKeys[0].time = 0.0f; colorKeys[0].color = Color.white;  
  25.     colorKeys[1].time = 1.0f; colorKeys[1].color = Color.white;  
  26.     colorGradient.SetKeys(colorKeys, alphaKeys);  
  27.   
  28.     RandomlySpread();   // 初始化各粒子位置  
  29. }  
    public Gradient colorGradient;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);// 初始化梯度颜色控制器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);RandomlySpread();   // 初始化各粒子位置}
        然后在Update中根据粒子的角度改变粒子的透明度。

[csharp] view plain copy
print ? 在CODE上查看代码片 派生到我的代码片
  1. particleArr[i].color = colorGradient.Evaluate(circle[i].angle / 360.0f);  
    particleArr[i].color = colorGradient.Evaluate(circle[i].angle / 360.0f);

         运行后的效果: 


步骤七

        最后一步了,我们只做了外环,内环和外环的脚本是一样的,只是需要调整一下参数。首先回到对象层次树,在ParticleHalo下再新建空对象,命名为Anticlockwise_inner。同样添加脚本ParticleHalo.cs和粒子系统AddComponent->Effects->Particle System。


        内环是逆时针旋转的,因此把clockwise的勾去掉,同时修改参数直到效果满意。事实上,还可以添加一个顺时针的内环,让圆圈更明显。

 



进阶

        Unity自带的粒子不太给力,比如不够亮!不会发光!当粒子比较小的时候会变得很暗,相信你们也发现了。这时候有两个选择,要么自己写shader,要么使用第三方插件。介于目前水平和时间有限,我使用了第三方插件Glow11,在制作太阳系的章节中也有提到过。

        首先导入Glow11插件,Assets->Import Package->Custom Package->Glow11。

        给Main Camera添加Glow11组件(发现了吧,其实发光效果是产生在摄像机上的),修改粒子系统的材质 Particle System->Render->Material->Default-Material。

 

        现在看看效果怎样:


        闪瞎了…果然太亮了也不是很好…还是喜欢朦胧美。


这篇关于Unity3D学习(12)之粒子光环的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/250003

相关文章

Java深度学习库DJL实现Python的NumPy方式

《Java深度学习库DJL实现Python的NumPy方式》本文介绍了DJL库的背景和基本功能,包括NDArray的创建、数学运算、数据获取和设置等,同时,还展示了如何使用NDArray进行数据预处理... 目录1 NDArray 的背景介绍1.1 架构2 JavaDJL使用2.1 安装DJL2.2 基本操

HarmonyOS学习(七)——UI(五)常用布局总结

自适应布局 1.1、线性布局(LinearLayout) 通过线性容器Row和Column实现线性布局。Column容器内的子组件按照垂直方向排列,Row组件中的子组件按照水平方向排列。 属性说明space通过space参数设置主轴上子组件的间距,达到各子组件在排列上的等间距效果alignItems设置子组件在交叉轴上的对齐方式,且在各类尺寸屏幕上表现一致,其中交叉轴为垂直时,取值为Vert

Ilya-AI分享的他在OpenAI学习到的15个提示工程技巧

Ilya(不是本人,claude AI)在社交媒体上分享了他在OpenAI学习到的15个Prompt撰写技巧。 以下是详细的内容: 提示精确化:在编写提示时,力求表达清晰准确。清楚地阐述任务需求和概念定义至关重要。例:不用"分析文本",而用"判断这段话的情感倾向:积极、消极还是中性"。 快速迭代:善于快速连续调整提示。熟练的提示工程师能够灵活地进行多轮优化。例:从"总结文章"到"用

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06

学习hash总结

2014/1/29/   最近刚开始学hash,名字很陌生,但是hash的思想却很熟悉,以前早就做过此类的题,但是不知道这就是hash思想而已,说白了hash就是一个映射,往往灵活利用数组的下标来实现算法,hash的作用:1、判重;2、统计次数;

零基础学习Redis(10) -- zset类型命令使用

zset是有序集合,内部除了存储元素外,还会存储一个score,存储在zset中的元素会按照score的大小升序排列,不同元素的score可以重复,score相同的元素会按照元素的字典序排列。 1. zset常用命令 1.1 zadd  zadd key [NX | XX] [GT | LT]   [CH] [INCR] score member [score member ...]

【机器学习】高斯过程的基本概念和应用领域以及在python中的实例

引言 高斯过程(Gaussian Process,简称GP)是一种概率模型,用于描述一组随机变量的联合概率分布,其中任何一个有限维度的子集都具有高斯分布 文章目录 引言一、高斯过程1.1 基本定义1.1.1 随机过程1.1.2 高斯分布 1.2 高斯过程的特性1.2.1 联合高斯性1.2.2 均值函数1.2.3 协方差函数(或核函数) 1.3 核函数1.4 高斯过程回归(Gauss

【学习笔记】 陈强-机器学习-Python-Ch15 人工神经网络(1)sklearn

系列文章目录 监督学习:参数方法 【学习笔记】 陈强-机器学习-Python-Ch4 线性回归 【学习笔记】 陈强-机器学习-Python-Ch5 逻辑回归 【课后题练习】 陈强-机器学习-Python-Ch5 逻辑回归(SAheart.csv) 【学习笔记】 陈强-机器学习-Python-Ch6 多项逻辑回归 【学习笔记 及 课后题练习】 陈强-机器学习-Python-Ch7 判别分析 【学

系统架构师考试学习笔记第三篇——架构设计高级知识(20)通信系统架构设计理论与实践

本章知识考点:         第20课时主要学习通信系统架构设计的理论和工作中的实践。根据新版考试大纲,本课时知识点会涉及案例分析题(25分),而在历年考试中,案例题对该部分内容的考查并不多,虽在综合知识选择题目中经常考查,但分值也不高。本课时内容侧重于对知识点的记忆和理解,按照以往的出题规律,通信系统架构设计基础知识点多来源于教材内的基础网络设备、网络架构和教材外最新时事热点技术。本课时知识

线性代数|机器学习-P36在图中找聚类

文章目录 1. 常见图结构2. 谱聚类 感觉后面几节课的内容跨越太大,需要补充太多的知识点,教授讲得内容跨越较大,一般一节课的内容是书本上的一章节内容,所以看视频比较吃力,需要先预习课本内容后才能够很好的理解教授讲解的知识点。 1. 常见图结构 假设我们有如下图结构: Adjacency Matrix:行和列表示的是节点的位置,A[i,j]表示的第 i 个节点和第 j 个