Unity优化之ObjectPool-关于初始化

1 篇文章 0 订阅
0 篇文章 0 订阅

在Unity制作游戏的使用过程中.发现在游戏中激活大量GameObject的时候会卡顿.还有偶尔没有任何瓶颈的时候也会莫名的卡顿一下.好在有Profiler可以方便的查看到卡顿的点.



帧率稍慢不是问题.但在某一时间点上的卡住就是个很不好的体验.查看发现主要卡顿点在两处地方.

一是Instantiate.尤其是类似Rigdbody的组件.在激活时会更新一些全局的状态.

二是GC.Collect.Unity的自动GC发生的时间是无法控制的.可能发生在任何时间点.


对于一来说可以通过分批激活的方式来解决.比如使用coroutine来挨个激活就可以避免卡顿.

但对于二来说只有避免GC.那就要避免Destory.

综上.如果想避免游戏中的卡顿.首先要使用对象池.重复使用对象.避免对象的销毁.二是在合适的时候手动GC.比如Loading界面.


ObjectPool是很容易实现的.但Unity的Particles和Trail初始化会有一些问题.


简单的ObjectPool : 

public class ObjectPool<T>
{
	private List<T> _objects = new List<T>();

	public T GetObject()
	{
		T item;
		if( _objects.Count > 0 )
		{
			item = _objects[0];
			_objects.RemoveAt( 0 );
			return item;
		}
		return default(T);
	}
	
	public void PutObject(T item)
	{
		_objects.Add(item);
	}

	public int GetObjectCount()
	{
		return _objects.Count;
	}
}


再包装一个ObjectManage.对于不同对象使用不同的Pool

public class ObjectManager 
{
Dictionary< int, Dictionary<int, ObjectPool<GameObject>> >	m_BulletsPool;	//子弹的对象池

	public GameObject GetBullet( int iBulletId, int iBulletDisplayId )
	{
		if( m_BulletsPool.ContainsKey( iBulletId ) == false )
			return null;
		if( m_BulletsPool[iBulletId].ContainsKey(iBulletDisplayId) == false )
			return null;

		return m_BulletsPool[iBulletId][iBulletDisplayId].GetObject();
	}


	public void RecycleBullet( int iBulletId, int iBulletDisplayId, GameObject objBullet )
	{
		if( m_bUseBulletPool == false )
			return;

		if( m_BulletsPool.ContainsKey( iBulletId ) == false )
		{
			m_BulletsPool.Add( iBulletId, new Dictionary<int, ObjectPool<GameObject>>() );
		}
		if( m_BulletsPool[iBulletId].ContainsKey(iBulletDisplayId) == false )
		{
			m_BulletsPool[iBulletId].Add( iBulletDisplayId, new ObjectPool<GameObject>() );
		}

		objBullet.SetActive( false );
		m_BulletsPool[iBulletId][iBulletDisplayId].PutObject( objBullet );
	}
}

这样在使用过程中会发现.带Trail的GameObject在重用后.由于没有重置.会在旧的位置和新的位置之间产生拖尾.而带有Particle System的GameObject再重用过程中参数会不断积累.还有其他一些组件.也都需要初始化.

对于Trail.关键是要在回收时将TrailRenderer.time 置为-1.

对于Particle System.应该是要重置所有的属性.

针对以上情况.写了一个组件.挂在每个要初始化的GameObject上



using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;

public class ObjectPoolComponent : MonoBehaviour {

	Dictionary<Component, Dictionary<PropertyInfo,object>>	m_FieldMap = new Dictionary<Component, Dictionary<PropertyInfo, object>>();

//	T CopyComponent<T>(T original, GameObject destination) where T : Component
//	{
//		System.Type type = original.GetType();
//		Component copy = destination.AddComponent(type);
//		System.Reflection.FieldInfo[] fields = type.GetFields();
//		foreach (System.Reflection.FieldInfo field in fields)
//		{
//			field.SetValue(copy, field.GetValue(original));
//		}
//		return copy as T;
//	}
//
//	void CopyField<T>( T original, T destination ) where T : Component
//	{
//		System.Type type = original.GetType();
//		System.Reflection.FieldInfo[] fields = type.GetFields();
//		foreach (System.Reflection.FieldInfo field in fields)
//		{
//			field.SetValue(destination, field.GetValue(original));
//		}
//	}

	NcCurveAnimation[]	m_CurveAnms = null;
	ParticleSystem[]	m_Particles = null;
	SpriteSheet[]		m_SpriteSheets = null;
	public TrailRenderer[] 	m_tcs	= null;
	//public float[]			m_tcTimes;

	void Awake()
	{
		m_CurveAnms = GetComponentsInChildren<NcCurveAnimation>();
		m_Particles = GetComponentsInChildren<ParticleSystem>();
		m_SpriteSheets = GetComponentsInChildren<SpriteSheet>();

		if( m_Particles != null )
		{
			for( int i = 0; i < m_Particles.Length; ++i )
			{
				Dictionary<PropertyInfo,object> mapPro = new Dictionary<PropertyInfo, object>();

				PropertyInfo[] propertyInfos = m_Particles[i].GetType().GetProperties();
				for( int j = 0; j < propertyInfos.Length; ++j )
				{
					if( propertyInfos[j].CanWrite && propertyInfos[j].PropertyType.IsPublic )
					{
						mapPro.Add( propertyInfos[j], propertyInfos[j].GetValue( m_Particles[i], null ) );
						propertyInfos[0].GetValue( m_Particles[i], null );
					}
				}

				if( mapPro.Count > 0 )
				{	
					m_FieldMap.Add( m_Particles[i], mapPro );
				}
			}
		}

		m_tcs = GetComponentsInChildren<TrailRenderer>();
		if( m_tcs != null )
		{
			for( int i = 0; i < m_tcs.Length; ++i )
			{
				Dictionary<PropertyInfo,object> mapPro = new Dictionary<PropertyInfo, object>();
				
				PropertyInfo[] propertyInfos = m_tcs[i].GetType().GetProperties();
				for( int j = 0; j < propertyInfos.Length; ++j )
				{
					if( propertyInfos[j].CanWrite && propertyInfos[j].PropertyType.IsPublic )
					{
						mapPro.Add( propertyInfos[j], propertyInfos[j].GetValue( m_tcs[i], null ) );
						propertyInfos[0].GetValue( m_tcs[i], null );
					}
				}
				
				if( mapPro.Count > 0 )
				{
					m_FieldMap.Add( m_tcs[i], mapPro );
				}
			}

//			m_tcTimes = new float[m_tcs.Length];
//			
//			for( int i = 0; i < m_tcs.Length; ++i )
//			{
//				m_tcTimes[i] = m_tcs[i].time;
//			}
		}
	}

	void ResetTrails()
	{
		if( m_tcs != null )
		{
			for( int i = 0; i < m_tcs.Length; ++i )
			{
				if( m_FieldMap.ContainsKey( m_tcs[i] ) == false )
				{
					continue;
				}
				PropertyInfo[] propertyInfos = m_tcs[i].GetType().GetProperties();
				for( int j = 0; j < propertyInfos.Length; ++j )
				{
					if( m_FieldMap[m_tcs[i]].ContainsKey( propertyInfos[j] ) )
					{
						propertyInfos[j].SetValue( m_tcs[i], m_FieldMap[m_tcs[i]][propertyInfos[j]], null );
					}
				}
			}
//			for( int i = 0; i < m_tcs.Length; ++i )
//				m_tcs[i].time = m_tcTimes[i];
		}
	}

	void RestoreParticles()
	{
		if( m_Particles != null )
		{
			for( int i = 0; i < m_Particles.Length; ++i )
			{
				if( m_FieldMap.ContainsKey( m_Particles[i] ) == false )
				{
					continue;
				}
				PropertyInfo[] propertyInfos = m_Particles[i].GetType().GetProperties();
				for( int j = 0; j < propertyInfos.Length; ++j )
				{
					if( m_FieldMap[m_Particles[i]].ContainsKey( propertyInfos[j] ) )
					{
						propertyInfos[j].SetValue( m_Particles[i], m_FieldMap[m_Particles[i]][propertyInfos[j]], null );
					}
				}
				
				m_Particles[i].renderer.enabled = true;
				m_Particles[i].time = 0;
				m_Particles[i].Clear( true );
				m_Particles[i].Play( true );
			}
		}
	}

	void OnEnable()
	{
		if( m_CurveAnms != null )
		{
			for( int i = 0; i < m_CurveAnms.Length; ++i )
			{
				m_CurveAnms[i].ResetAnimation();
			}
		}

		if( m_SpriteSheets != null )
		{
			for( int i = 0; i < m_SpriteSheets.Length; ++i )
			{
				m_SpriteSheets[i].ResetPlay();
			}
		}

		//在Disable时将粒子发射器和Trail关闭.但unenable时并未进行刷新.所以需要等一帧再重置回来
		//ResetTrails();
		//RestoreParticles();
		Invoke( "ResetTrails", 0.001f );
		Invoke( "RestoreParticles", 0.001f );
	}

	void OnDisable()
	{
		if( m_Particles != null )
		{
			for( int i = 0; i < m_Particles.Length; ++i )
			{
				m_Particles[i].Stop();
				m_Particles[i].time = 0;
				m_Particles[i].Clear( true );
				m_Particles[i].renderer.enabled = false;
			}
		}

		if( m_tcs != null )
		{
			for( int i = 0; i < m_tcs.Length; ++i )
			{
				if( m_tcs[i] != null )
				{
					m_tcs[i].time = -1;
				}
			}
		}

	}

}






  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值