文章名字是我杜撰的。之前一直在做server开发,上周有机会接触了client,发现非常多资源没有有效管理起来。甚至有资源泄露的发生,我就先针对特效做了pool,结果在一定程度上纠正之前一直非常难解决的位置同步问题。
总结上来client的资源有:模型、特效、音频、动画等。
音频怎么管理起来呢,http://answers.unity3d.com/questions/482218/best-practices-for-playing-a-lot-of-audio.html这个链接的撸主也提出相同问题:大概是项目有大量音频,假设都是执行时载入,开销太大。怎么解决呢?
这个是下周我的一个私人课题,哈哈。毕竟是业余client。首先了解Audio的相关API,假设播放音频,停止播放,暂停。又一次播放等,假设在某个地点或者某个对象上播放;基本上控制好这几个操作就能够写一个简单的AudioPool了。
网上也找到一个简单的AudioMgr:
// ///// 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); } ///就是对AudioClip,正是我须要补的基础知识。/// 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. /// /// /// /// /// ///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 (); 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); } /// /// 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. /// /// /// /// /// ///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 (); source.clip = clip; source.volume = volume; source.pitch = pitch; source.Play(); Destroy(go, clip.length); return source; }}
然后这里另一些Pool的知识 http://forum.unity3d.com/threads/simple-reusable-object-pool-help-limit-your-instantiations.76851/:
当中还有代码,针对特效的管理:
using UnityEngine;using System.Collections; public class Effect : MonoBehaviour{ ///比方同一设置特效长度,启动特效后启动所以离子,并启动一个协程。时间一到就ResetEffect,这时候回放到Pool里。/// The array of emitters to fire when the effect starts. /// public ParticleEmitter[] emitters; ////// The length of the effect in seconds. After which the effect will be reset and pooled if needed. /// public float effectLength = 1f; ////// Should the effect be added to the effects pool after completion. /// public bool poolAfterComplete = true; ////// Resets the effect. /// public virtual void ResetEffect () { if(poolAfterComplete) { ObjectPool.instance.PoolObject(gameObject); } else { Destroy(gameObject); } } ////// Starts the effect. /// 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(); } }
using UnityEngine;using System.Collections; public class SoundEffect : MonoBehaviour{ ///音频的管理跟特效管理类似,就是播放的API不一样。操作完毕后一样进行回收。/// The sound source that will be played when the effect is started. /// public AudioSource soundSource; ////// The sound clips that will randomly be played if there is more than 1. /// public AudioClip[] soundClips; ////// The length of the effectin seconds. /// public float effectLength = 1f; ////// Should the effect be pooled after its completed. /// public bool poolAfterComplete = true; ////// Resets the effect. /// public virtual void ResetEffect () { if(poolAfterComplete) { ObjectPool.instance.PoolObject(gameObject); } else { Destroy(gameObject); } } ////// Starts the effect. /// 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(); } }
那这个池子是怎么写的呢?
using UnityEngine;using System.Collections;using System.Collections.Generic; public class ObjectPool : MonoBehaviour{ public static ObjectPool instance; ///事实上这个Pool有多重写法,我在项目里的写法就是写成单例类。以资源的文件位置为Key,比方一个特效名字是“hit/release/bloat”就存进去。/// The object prefabs which the pool can handle. /// public GameObject[] objectPrefabs; ////// The pooled objects currently available. /// public List[] pooledObjects; /// /// The amount of objects of each type to buffer. /// public int[] amountToBuffer; public int defaultBufferAmount = 3; ////// The container object that we will keep unused pooled objects so we dont clog up the editor with objects. /// 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[objectPrefabs.Length]; int i = 0; foreach ( GameObject objectPrefab in objectPrefabs ) { pooledObjects[i] = new List (); int bufferAmount; if(i < amountToBuffer.Length) bufferAmount = amountToBuffer[i]; else bufferAmount = defaultBufferAmount; for ( int n=0; n /// 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. /// ////// The object for type. /// /// /// Object type. /// /// /// If true, it will only return an object if there is one currently pooled. /// public GameObject GetObjectForType ( string objectType , bool onlyPooled ) { for(int i=0; i0) { 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; } /// /// Pools the object specified. Will not be pooled if there is no prefab of that type. /// /// /// Object to be pooled. /// public void PoolObject ( GameObject obj ) { for ( int i=0; i
这个帖子的启示点是播放一个时间段的特效用协程来完毕,比现有项目用一个Mgr管理高效多了。