• 周四. 11 月 21st, 2024

5G编程聚合网

5G时代下一个聚合的编程学习网

热门标签

"反直觉" 的Unity粒子系统API

admin

11 月 28, 2021

前几天一位实习生希望可以在脚本上控制粒子系统的粒子数,这个东西嘛 我当初也在试用期的时碰过一下。这些API都忘记的一干二净了。
所以搜了一下官方的API,经查后才发现,新版本的粒子系统API 都修改了,分成了不同的模块。UnityEngine.ParticleSystem – Unity 脚本 API

而事实上Unity官网也有实习生想要的效果(在EmissionModule.rateOverTime)

ParticleSystem-emission – Unity 脚本 API

但是我直接这样写发现会报错

void Update(){

    emissionRate += 1;

    ps.emission.rateOverTime = new ParticleSystem.MinMaxCurve(emissionRate);

}

因为ps.emssion 的类型是 EmissionModule, 而EmissionModule 是struct 而不是class。所以直接这样些就报错了。

过程中我也一直怀疑是不是Unity的文档太老旧了,抑或文档出错了?

因为按照直觉,因为 ps.emission 作为结构体,其已经是一个数据的副本。修改其数据再也不影响到粒子系统才对。

        //     Script interface for the EmissionModule of a Particle System.
        public struct EmissionModule
        {
            public bool enabled { get; set; }            
            public MinMaxCurve rateOverTime { get; set; }            
            public float rateOverTimeMultiplier { get; set; }           
            public MinMaxCurve rateOverDistance { get; set; }           
            public float rateOverDistanceMultiplier { get; set; }
            public int burstCount { get; set; }
            public Burst GetBurst(int index);
            public int GetBursts(Burst[] bursts);
            public void SetBurst(int index, Burst burst);
            public void SetBursts(Burst[] bursts);
            public void SetBursts(Burst[] bursts, int size);
            // ... Obsolete ... ignore
        }

但是Unity 就是这样,经过测试直接修改此emission 是可以控制粒子系统的。

其原理也很简单,估计在C++的部分还是隐藏了一个专门控制的句柄,只是在C#中没有显示出来。

例如

struct EmissionModule
{
    private int handle;
    public int rate;
    void SetRate(){
      var ps = GetPsInCpp(handle);
        ps.rate = this.rate;      
    }
}

在实际使用时,可以一直保存 emission, 在Update时不断调用也是可以的。

public class ParticleSystemController : MonoBehaviour
{
    [SerializeField]
    new ParticleSystem  particleSystem;

    [SerializeField]
    int emissionRate = 10;

    ParticleSystem.EmissionModule emissionModule;
    void Start(){
        //var rot = particleSystem.emission.rateOverTime;
        var emission = particleSystem.emission;
        this.emissionModule = emission;
    }
    void Update()
    {
        //var emission = particleSystem.emission;
        var emission = this.emissionModule;


        emission.rateOverTime = new ParticleSystem.MinMaxCurve(emissionRate);
    }
}

如果途中将Destroy(particleSystem)

此脚本会报错

NullReferenceException: Do not create your own module instances, get them from a ParticleSystem instance
UnityEngine.ParticleSystem+EmissionModule.set_rateOverTime (UnityEngine.ParticleSystem+MinMaxCurve value) (at <893f889776d241068c95ad83f5452958>:0)
ParticleSystemController.Update ()

可是看不出更深的调用堆栈。

那为什么不设计成class呢?我估计Unity 是希望不要存在太多的内存碎片。

发表回复