天下性能 唯池不破

    xiaoxiao2021-03-25  98

    转自:http://blog.csdn.net/boyxiaolong/article/details/38180941

    文章名字是我杜撰的,之前一直在做服务器开发,上周有机会接触了客户端,发现很多资源没有有效管理起来,甚至有资源泄露的发生,我就先针对特效做了pool,结果在一定程度上纠正之前一直很难解决的位置同步问题。

    总结上来客户端的资源有:模型、特效、音频、动画等。

    音频怎么管理起来呢,http://answers.Unity3D.com/questions/482218/best-practices-for-playing-a-lot-of-audio.html这个链接的撸主也提出同样问题:大概是项目有大量音频,如果都是运行时加载,开销太大,怎么解决呢?

    这个是下周我的一个私人课题,哈哈,毕竟是业余客户端,首先了解Audio的相关API,如果播放音频,停止播放,暂停,重新播放等,如果在某个地点或者某个对象上播放;基本上控制好这几个操作就可以写一个简单的AudioPool了。

    网上也找到一个简单的AudioMgr:

    [csharp]  view plain  copy // /   //   // Audio Manager.   //   // This code is release under the MIT licence. It is provided as-is and without any warranty.   //   // Developed by Daniel Rodríguez (Seth Illgard) in April 2010   // http://www.silentkraken.com   //   // /      using UnityEngine;   using System.Collections;      public class AudioManager : MonoBehaviour   {       public AudioSource Play(AudioClip clip, Transform emitter)       {           return Play(clip, emitter, 1f, 1f);       }          public AudioSource Play(AudioClip clip, Transform emitter, float volume)       {           return Play(clip, emitter, volume, 1f);       }          /// <summary>       /// Plays a sound by creating an empty game object with an AudioSource       /// and attaching it to the given transform (so it moves with the transform). Destroys it after it finished playing.       /// </summary>       /// <param name="clip"></param>       /// <param name="emitter"></param>       /// <param name="volume"></param>       /// <param name="pitch"></param>       /// <returns></returns>       public AudioSource Play(AudioClip clip, Transform emitter, float volume, float pitch)       {           //Create an empty game object           GameObject go = new GameObject ("Audio: " +  clip.name);           go.transform.position = emitter.position;           go.transform.parent = emitter;              //Create the source           AudioSource source = go.AddComponent<AudioSource>();           source.clip = clip;           source.volume = volume;           source.pitch = pitch;           source.Play ();           Destroy (go, clip.length);           return source;       }          public AudioSource Play(AudioClip clip, Vector3 point)       {           return Play(clip, point, 1f, 1f);       }          public AudioSource Play(AudioClip clip, Vector3 point, float volume)       {           return Play(clip, point, volume, 1f);       }          /// <summary>       /// Plays a sound at the given point in space by creating an empty game object with an AudioSource       /// in that place and destroys it after it finished playing.       /// </summary>       /// <param name="clip"></param>       /// <param name="point"></param>       /// <param name="volume"></param>       /// <param name="pitch"></param>       /// <returns></returns>       public AudioSource Play(AudioClip clip, Vector3 point, float volume, float pitch)       {           //Create an empty game object           GameObject go = new GameObject("Audio: " + clip.name);           go.transform.position = point;              //Create the source           AudioSource source = go.AddComponent<AudioSource>();           source.clip = clip;           source.volume = volume;           source.pitch = pitch;           source.Play();           Destroy(go, clip.length);           return source;       }   }   就是对AudioClip,正是我需要补的基础知识。

    然后这里还有一些Pool的知识 http://forum.unity3d.com/threads/simple-reusable-object-pool-help-limit-your-instantiations.76851/:

    其中还有代码,针对特效的管理:

    [csharp]  view plain  copy using UnityEngine;   using System.Collections;       public class Effect : MonoBehaviour   {       /// <summary>       /// The array of emitters to fire when the effect starts.       /// </summary>       public ParticleEmitter[] emitters;             /// <summary>       /// The length of the effect in seconds.  After which the effect will be reset and pooled if needed.       /// </summary>       public float effectLength = 1f;                   /// <summary>       /// Should the effect be added to the effects pool after completion.       /// </summary>       public bool poolAfterComplete = true;                       /// <summary>       /// Resets the effect.       /// </summary>       public virtual void ResetEffect ()       {           if(poolAfterComplete)           {               ObjectPool.instance.PoolObject(gameObject);           } else {               Destroy(gameObject);           }       }             /// <summary>       /// Starts the effect.       /// </summary>       public virtual void StartEffect ()       {           foreach ( ParticleEmitter emitter in emitters )           {               emitter.Emit();           }                     StartCoroutine(WaitForCompletion());       }             public IEnumerator WaitForCompletion ()       {           //Wait for the effect to complete itself           yield return new WaitForSeconds(effectLength);                     //Reset the now completed effect           ResetEffect();                 }                     }   比如同一设置特效长度,启动特效后启动所以离子,并启动一个协程,时间一到就ResetEffect,这时候回放到Pool里。

    [csharp]  view plain  copy using UnityEngine;   using System.Collections;       public class SoundEffect : MonoBehaviour   {             /// <summary>       /// The sound source that will be played when the effect is started.       /// </summary>       public AudioSource soundSource;             /// <summary>       /// The sound clips that will randomly be played if there is more than 1.       /// </summary>       public AudioClip[] soundClips;             /// <summary>       /// The length of the effectin seconds.       /// </summary>       public float effectLength = 1f;             /// <summary>       /// Should the effect be pooled after its completed.       /// </summary>       public bool poolAfterComplete = true;                         /// <summary>       /// Resets the effect.       /// </summary>       public virtual void ResetEffect ()       {           if(poolAfterComplete)           {               ObjectPool.instance.PoolObject(gameObject);           } else {               Destroy(gameObject);           }       }             /// <summary>       /// Starts the effect.       /// </summary>       public virtual void StartEffect ()       {           soundSource.PlayOneShot(soundClips[Random.Range(0,soundClips.Length)]);                     StartCoroutine(WaitForCompletion());       }             public IEnumerator WaitForCompletion ()       {           //Wait for the effect to complete itself           yield return new WaitForSeconds(effectLength);                     //Reset the now completed effect           ResetEffect();                 }               }       音频的管理跟特效管理类似,就是播放的API不一样,操作完成后一样进行回收。

    那这个池子是怎么写的呢?

    [csharp]  view plain  copy using UnityEngine;   using System.Collections;   using System.Collections.Generic;       public class ObjectPool : MonoBehaviour   {             public static ObjectPool instance;             /// <summary>       /// The object prefabs which the pool can handle.       /// </summary>       public GameObject[] objectPrefabs;             /// <summary>       /// The pooled objects currently available.       /// </summary>       public List<GameObject>[] pooledObjects;             /// <summary>       /// The amount of objects of each type to buffer.       /// </summary>       public int[] amountToBuffer;             public int defaultBufferAmount = 3;             /// <summary>       /// The container object that we will keep unused pooled objects so we dont clog up the editor with objects.       /// </summary>       protected GameObject containerObject;             void Awake ()       {           instance = this;       }             // Use this for initialization       void Start ()       {           containerObject = new GameObject("ObjectPool");                     //Loop through the object prefabs and make a new list for each one.           //We do this because the pool can only support prefabs set to it in the editor,           //so we can assume the lists of pooled objects are in the same order as object prefabs in the array           pooledObjects = new List<GameObject>[objectPrefabs.Length];                     int i = 0;           foreach ( GameObject objectPrefab in objectPrefabs )           {               pooledObjects[i] = new List<GameObject>();                              int bufferAmount;                             if(i < amountToBuffer.Length) bufferAmount = amountToBuffer[i];               else                   bufferAmount = defaultBufferAmount;                             for ( int n=0; n<bufferAmount; n++)               {                   GameObject newObj = Instantiate(objectPrefab) as GameObject;                   newObj.name = objectPrefab.name;                   PoolObject(newObj);               }                             i++;           }       }             /// <summary>       /// Gets a new object for the name type provided.  If no object type exists or if onlypooled is true and there is no objects of that type in the pool       /// then null will be returned.       /// </summary>       /// <returns>       /// The object for type.       /// </returns>       /// <param name='objectType'>       /// Object type.       /// </param>       /// <param name='onlyPooled'>       /// If true, it will only return an object if there is one currently pooled.       /// </param>       public GameObject GetObjectForType ( string objectType , bool onlyPooled )       {           for(int i=0; i<objectPrefabs.Length; i++)           {               GameObject prefab = objectPrefabs[i];               if(prefab.name == objectType)               {                                     if(pooledObjects[i].Count > 0)                   {                       GameObject pooledObject = pooledObjects[i][0];                       pooledObjects[i].RemoveAt(0);                       pooledObject.transform.parent = null;                       pooledObject.SetActiveRecursively(true);                                             return pooledObject;                                         } else if(!onlyPooled) {                       return Instantiate(objectPrefabs[i]) as GameObject;                   }                                     break;                                 }           }                         //If we have gotten here either there was no object of the specified type or non were left in the pool with onlyPooled set to true           return null;       }             /// <summary>       /// Pools the object specified.  Will not be pooled if there is no prefab of that type.       /// </summary>       /// <param name='obj'>       /// Object to be pooled.       /// </param>       public void PoolObject ( GameObject obj )       {           for ( int i=0; i<objectPrefabs.Length; i++)           {               if(objectPrefabs[i].name == obj.name)               {                   obj.SetActiveRecursively(false);                   obj.transform.parent = containerObject.transform;                   pooledObjects[i].Add(obj);                   return;               }           }       }         }       其实这个Pool有多重写法,我在项目里的写法就是写成单例类。以资源的文件位置为Key,比如一个特效名字是“hit/release/bloat”就存进去。

    这个帖子的启发点是播放一个时间段的特效用协程来完成,比现有项目用一个Mgr管理高效多了。

    转载请注明原文地址: https://ju.6miu.com/read-17550.html

    最新回复(0)