• 天下性能 唯池不破


    文章名字是我杜撰的。之前一直在做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);
        }
    
        /// <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管理高效多了。


  • 相关阅读:
    python接口自动化(十三)--cookie绕过验证码登录(详解)
    python接口自动化(十二)--https请求(SSL)(详解)
    python接口自动化(十一)--发送post【data】(详解)
    python接口自动化(十)--post请求四种传送正文方式(详解)
    python接口自动化(九)--python中字典和json的区别(详解)
    python接口自动化(八)--发送post请求的接口(详解)
    python接口自动化(七)--状态码详解对照表(详解)
    python接口自动化(六)--发送get请求接口(详解)
    python接口自动化(五)--接口测试用例和接口测试报告模板(详解)
    Redis的简介
  • 原文地址:https://www.cnblogs.com/lcchuguo/p/5211173.html
Copyright © 2020-2023  润新知