天天看點

[Unity 3D] Unity 3D 性能優化(二)IsAlive

U3D的粒子系統腳本接口相信很多人都用過,ParticleSyetem類的一系列接口都有一個bool類型的參數——withChildren,通過這個參數可以直接将相同的判斷或者操作應用到一整個通過Transform父子關系樹關聯起來的ParticleSystem執行個體集合上。然而,但凡友善的功能,裡面就必然有性能陷阱……

以IsAlive這個接口為例(用來判斷粒子系統是否所有粒子都已經消亡,一般用在非loop的例子發射器上),看看U3D裡是如何實作這個接口的:

public bool IsAlive()  

{  

    bool withChildren = true;  

    return this.IsAlive(withChildren);  

}  

public bool IsAlive(bool withChildren)  

    if (withChildren)  

    {  

        ParticleSystem[] particleSystems = ParticleSystem.GetParticleSystems(this);  

        ParticleSystem[] array = particleSystems;  

        for (int i = 0; i < array.Length; i++)  

        {  

            ParticleSystem particleSystem = array[i];  

            if (particleSystem.Internal_IsAlive())  

            {  

                return true;  

            }  

        }  

        return false;  

    }  

    return this.Internal_IsAlive();  

可以看到,如果傳遞的withChildren參數為true,那麼函數會先嘗試調用GetParticleSystems(this)來擷取包括下級gameObject在内的所有能找得到的粒子系統元件,然後對這些粒子系統元件依次再調用IsAlive判斷。而如果withChildren為false,就僅僅會判斷自身。那麼自然,開銷大小與否,關鍵就在GetParticleSystems的實作上了。

internal static ParticleSystem[] GetParticleSystems(ParticleSystem root)  

    if (!root)  

        return null;  

    List<ParticleSystem> list = new List<ParticleSystem>();  

    list.Add(root);  

    ParticleSystem.GetDirectParticleSystemChildrenRecursive(root.transform, list);  

    return list.ToArray();  

private static void GetDirectParticleSystemChildrenRecursive(Transform transform, List<ParticleSystem> particleSystems)  

    foreach (Transform transform2 in transform)  

        ParticleSystem component = transform2.gameObject.GetComponent<ParticleSystem>();  

        if (component != null)  

            particleSystems.Add(component);  

            ParticleSystem.GetDirectParticleSystemChildrenRecursive(transform2, particleSystems);  

U3D對擷取所有下級gameObject執行個體上的粒子系統元件使用了遞歸的方式,并在遞歸結束後傳回結果清單時做了一次清單元素複制(List.ToArray()),并且在擷取粒子系統元件的時候用的是transform2.gameObject.GetComponent<ParticleSystem>(),而不是transform2.GetComponent<ParticleSystem>(),從上一篇文章裡我們已經用實驗證明了,前一種方式開銷更大。看到這裡,我們心裡大概已經有譜了,那就是——效率絕對不會高到哪裡去,影響性能的地方太多了……還是設計一個小實驗來看看這種情況下應該用什麼樣的方式更好吧:

設計實驗——一個兩層結構,一個父gameObject挂載一個ParticleSystem元件,兩個子gameObject分别挂載一個PariticleSystem元件,采用兩種不同的方式對這個組合判斷IsAlive各8×1024×1024次。

方案一,直接對父gameObject上的PariticleSystem調用IsAlive(true);

方案二,在循環前,先用GetComponentsInChildren将所有的PariticleSystem存入一個List,循環中對這個List做周遊,對List裡每一個ParticleSystem調用IsAlive(false);

實驗結果——方案一約3900ms,方案二約65ms。

結果對比很明顯。其實,U3D提供的這個接口的意義在于,當不是需要進行那麼頻繁的調用時,可以用IsAlive(true)來省掉手動擷取所有子粒子系統的過程,讓代碼簡潔一些,雖然U3D目前對這個接口的實作有的地方還值得斟酌。ParticleSystem提供的這一族接口(IsAlive隻是其中之一,此外還有Play,Pause,Stop等等),如果使用頻率不是很高,比如僅僅是初始化或者銷毀的時候做一次性調用,那麼即便是withChildren參數是true也沒有什麼大不了的,還能少些很多代碼,何樂而不為;但如果需要頻繁調用,比如每幀都對粒子系統集合判斷IsAlive,這種情況下,一定不能懶惰,該寫的東西還是要寫的。另外值得注意的一點,IsAlive這一族接口的無參形式,是預設withChildren為true的,使用的時候可别搞錯了。

PS,留意一下GetDirectParticleSystemRecursive的實作方式,你會發現它有一個遞歸條件,就是節點上必須要有PariticleSystem元件,在遞歸過程中,一旦發現某個節點上沒有ParticleSystem元件時,父子關系樹上的這一枝就算周遊到頭了,再往下即便是還有ParticleSystem存在也會被忽略。是以,如果你面對的ParticleSystem集合就恰好存在這樣的斷層,那最好還是自己勤快一點,自己動手用GetComonentsInChildren來查找所有的粒子系統元件。

本文轉蓬萊仙羽51CTO部落格,原文連結:http://blog.51cto.com/dingxiaowei/1366356,如需轉載請自行聯系原作者