Unity3D学习笔记(9)—— 粒子光环

参考网站首页的光环效果: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。

using UnityEngine;
using System.Collections;

public class ParticleHalo : MonoBehaviour
{
    void Start ()
    {
    }

    void Update ()
    {
    }
}

步骤三

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

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对应每个粒子,因此也是必须的。

    private ParticleSystem particleSys;  // 粒子系统
    private ParticleSystem.Particle[] particleArr;  // 粒子数组
    private CirclePosition[] circle; // 极坐标数组

其次粒子的数量、粒子大小、旋转的最大最小半径等也得有。

    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。

    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将所有的粒子随机分布在圆圈轨道上。

    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函数里逐渐改变粒子的角度:

    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:

    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方法中:

            // 粒子在半径方向上游离
            circle[i].time += Time.deltaTime;
            circle[i].radius += Mathf.PingPong(circle[i].time / minRadius / maxRadius, pingPong) - pingPong / 2.0f;

然后看看效果是不是好点了:

步骤六

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

    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中根据粒子的角度改变粒子的透明度。

    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。

 

现在看看效果怎样:

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

时间: 2024-10-27 05:22:10

Unity3D学习笔记(9)—— 粒子光环的相关文章

unity3d学习笔记(十九)--ngui制作3d人物头顶的头像和血条

原地址:http://blog.csdn.net/lzhq1982/article/details/18793479 本系列文章由Aimar_Johnny编写,欢迎转载,转载请标明出处,谢谢. http://blog.csdn.net/lzhq1982/article/details/18793479 先上张图,自己做的一个demo. 这里的人物头像和血条是在3d世界生成的,所以有真正的纵深感和遮挡关系,废话不多说,看我是怎么实现的. 第一步,先在UI Root里制作头像和血条. 这个制作步骤基

cocos2dx学习笔记(5)——粒子特效CCParticleSystem

0.使用方法 拿 CCParticleExplosion 举例. //创建CCParticleExplosion特效 CCParticleSystem *p1 = CCParticleExplosion::create(); //设置特效贴图 p1->setTexture(CCTextureCache::sharedTextureCache()->addImage("cocos2dx.png")); //设置自动释放 p1->setAutoRemoveOnFinish

Unity3d 学习笔记(-) Monobehaviour

从今天起开始正式学习Unity3d!!!! 下面记录Monobehaviour相关内容. Monobehaviour执行顺序,图示很清晰,简单明了,可以通过此图洞悉协程(coroutine)的运行机制. Unity3d 学习笔记(-) Monobehaviour

Unity3D学习笔记之七创建自己的游戏场景

到现在为止我们已经拥有了比较完备的Prefab,已经可以创建宏大的游戏场景,并以第一人称视角在场景中漫游了.这里给大家做个小的示范,建一个小场景大家在创建场景的时候需要自由发挥,做个尽量大的场景出来. 这一系列教程以及素材均参考自人人素材翻译组出品的翻译教程<Unity游戏引擎的基础入门视频教程>,下载链接附在第二篇学习笔记中. 我们以最初的添加了First Person Controller的PFB_Straight为整个场景的中心点来展开.我们先从Project中Prefabs文件夹拖出来

Unity3D学习笔记之八为场景添加细节(一)

这一系列教程以及素材均参考自人人素材翻译组出品的翻译教程<Unity游戏引擎的基础入门视频教程>,下载链接附在第二篇学习笔记中. 我花了30分钟做了一个中等大小的迷宫场景,不知道大家自己发挥,做的场景大小如何. 在完成场景之后,我们看到Hierarchy视图里面的东西已经满了,所以我们先来整理一下Hierarchy视图.创建一个空的游戏物体命名为Environment. 然后来到Hierarchy视图,先讲First Person Controller找到,挪到最上方,然后选中第一个物体,按住

Unity3D学习笔记九为场景添加细节(二)

上节为场景中添加了第一块带有碰撞器的石头,本节我们来利用Prefab,将场景细节都添加进去,并且做的更完善. 这一系列教程以及素材均参考自人人素材翻译组出品的翻译教程<Unity游戏引擎的基础入门视频教程>,下载链接附在第二篇学习笔记中. 继续来到那块已经成形的石头--Boulder旁边,我们可以制作很多石头的Prefab,这样对于丰富我们巨大的场景很有利,我们先来创建第一个石头的Prefab按下Control+D复制一块石头,按住坐标轴拖动出来,按下R调整到缩放工具,按住中心的白色立方体缩小

一步一部学习Unity3d学习笔记系1.3 英雄联盟服务器集群架构猜想

说到了网游那就涉及到服务器了,时下最火的属英雄联盟了,我也是它的粉丝,每周必撸一把,都说小撸怡情,大撸伤身,强撸灰飞烟灭,也告诫一下同仁们,注意身体,那么他的服务器架构是什么呢,给大家分享一下, 具体的是什么架构,因为没有源码,也不知道怎么回事,只能根据当前一些经验,还有撸的时候的体验猜想出来的.和实际有偏差,大家勿喷在这里只是分享,和实际的也应该相差不大. 英雄联盟服务器其实就是一个单服,单服下面有一些集群,有用户服务器,的用户服务器实际上就是QQ用户服务器QQ用户也是有服务器集群组成 客户端

Unity3D学习笔记之六创建更多的Prefab

在写完上次的笔记后,我发现当前的Prefab只为地板添加了盒子碰撞器而忽略了墙壁和天花板,所以我们这次 首先为其他部分添加碰撞器.因为我们要以此Prefab为模板创建新的Prefab. 这一系列教程以及素材均参考自人人素材翻译组出品的翻译教程<Unity游戏引擎的基础入门视频教程>,下载链接附在第二篇学习笔记中. 首先选中一边墙壁,Component-Physics-Box Collider,然后根据当前坐标轴的方位和模型比例调节BoxCollider的厚度,例如这里我将X设为0.01,其他保

一步一部学习Unity3d学习笔记系1.1

最近开始在学习Unity3D,在网上找到了一些教程利用晚上回家休息的时间自学了一下,我就是一个小白,对Unity3D什么都不懂,可能需要一些美工的功底.刚好有点,需要点编程的基础,也好我也有点,对C#很熟,看着Unity3D那么火,我也抽时间来研究研究,看看指不定哪天就用到了. 首先准备工作是要到官网上下载一个Unity3D软件 官网http://unity3d.com/cn/ 下载喜欢的版本,至少要下载4.X以上的版本,初学者完全够用了. 下载安装完成以后先不要着急去学习菜单.因为菜单太多了不