文章名字是我杜撰的,之前一直在做服务器开发,上周有机会接触了客户端,发现很多资源没有有效管理起来,甚至有资源泄露的发生,我就先针对特效做了pool,结果在一定程度上纠正之前一直很难解决的位置同步问题。
总结上来客户端的资源有:模型、特效、音频、动画等。
音频怎么管理起来呢,http://answers.unity3d.com/questions/482218/best-practices-for-playing-a-lot-of-audio.html这个链接的撸主也提出同样问题:大概是项目有大量音频,如果都是运行时加载,开销太大,怎么解决呢?
这个是下周我的一个私人课题,哈哈,毕竟是业余客户端,首先了解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);
}
/// <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/:
其中还有代码,针对特效的管理:
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里。
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不一样,操作完成后一样进行回收。
那这个池子是怎么写的呢?
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管理高效多了。
原文:http://blog.csdn.net/boyxiaolong/article/details/38180941