Unity3D基于帧管理器的移动管理器

本文介绍了一种在Unity中实现的移动管理器设计方法,通过该方法可以有效地管理场景中多个对象的移动行为,确保所有对象的移动更新同步进行。

转载自:http://www.omuying.com/article/127.aspx

在前一篇中,我们定义了 FrameManager 来管理场景中所有对象的 Update 函数,在这篇文章中,我们定义一个 MoveManager 类来管理场景中所有移动的对象,因为场景中所有的对象的 Update 函数是由一个 Update 函数驱动的,这样我们可以保证每个移动的对象具有统一的代码执行步伐,即便帧会有卡顿,也能保证场景中的移动对象能在一个 Update 函数中顺序执行,保证步骤的统一。

最终效果如图,场景中有 500 个对象随机移动:


主要的类如下,MoveManager.cs 代码:

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

/// <summary>
/// 移动管理
/// </summary>
public class MoveManager
{
	/// <summary>
	/// 缓存对象
	/// </summary>
	public static Dictionary<object, MoveItem> moveList = new Dictionary<object, MoveItem>();

	/// <summary>
	/// 移动事件注册
	/// </summary>
	/// <param name="baseRoleItem">Base role item.</param>
	/// <param name="targetPosition">Target position.</param>
	/// <param name="callback">Callback.</param>
	public static void Register(GameObject gameObject, Vector3 targetPosition, float speed, Action<GameObject> callback)
	{
		if(!moveList.ContainsKey(gameObject))
		{
			MoveItem moveItem = new MoveItem();
			moveItem.Run(gameObject, targetPosition, speed, callback);
			
			moveList.Add(gameObject, moveItem);
		}
	}

	/// <summary>
	/// 移动事件取消注册
	/// </summary>
	/// <param name="baseRoleItem">Base role item.</param>
	/// <param name="destory">If set to <c>true</c> destory.</param>
	public static void UnRegister(GameObject gameObject)
	{
		if(moveList.ContainsKey(gameObject))
		{
			moveList[gameObject].Stop();
			moveList.Remove(gameObject);
		}
	}
}
MoveItem.cs 代码:

using UnityEngine;
using System;
using System.Collections;

/// <summary>
/// 移动对象,不用继续自 MonoBehaviour
/// </summary>
public class MoveItem
{
	/// <summary>
	/// 角色
	/// </summary>
	private GameObject gameObject;

	/// <summary>
	/// 目标位置
	/// </summary>
	private Vector3 targetPosition;

	/// <summary>
	/// 回调函数
	/// </summary>
	private Action<GameObject> callback;

	/// <summary>
	/// 移动向量
	/// </summary>
	private Vector3 direction;

	/// <summary>
	/// 结束状态
	/// </summary>
	private bool finishStatus;

	/// <summary>
	/// 数据初始化
	/// </summary>
	/// <param name="baseRoleItem">Base role item.</param>
	/// <param name="targetPosition">Target position.</param>
	/// <param name="callback">Callback.</param>
	public void Run(GameObject gameObject, Vector3 targetPosition, float speed, Action<GameObject> callback)
	{
		this.finishStatus = false;

		this.gameObject = gameObject;
		this.targetPosition = targetPosition;
		this.callback = callback;

		this.direction = (targetPosition - this.gameObject.transform.localPosition).normalized * speed;

		// 注册帧事件
		FrameManager.Register (this, this.FrameUpdateCallback);
	}

	/// <summary>
	/// 停止
	/// </summary>
	public void Stop()
	{
		// 注册帧事件
		FrameManager.UnRegister (this);
	}

	/// <summary>
	/// 每帧执行
	/// </summary>
	public void FrameUpdateCallback()
	{
		if (!this.IsArrivePosition ()) 
		{
			// Y 值根据地形高度需要时时获取
			this.gameObject.transform.localPosition += this.direction * Time.deltaTime;
		} else 
		{
			// Y 值根据地形高度需要时时获取地
			this.gameObject.transform.localPosition = this.targetPosition;

			// 这儿保证只执行回调函数一次
			if(!this.finishStatus)
			{
				if(this.callback != null)
				{
					// 取消注册帧事件
					FrameManager.UnRegister(this);
					this.callback(this.gameObject);
				}
				this.finishStatus = true;
			}
		}
	}

	/// <summary>
	/// 验证是否到达终点
	/// </summary>
	/// <returns><c>true</c> if this instance is arrive position; otherwise, <c>false</c>.</returns>
	private bool IsArrivePosition()
	{
		Vector3 currentDirection = (this.targetPosition - (this.gameObject.transform.localPosition + this.direction * Time.deltaTime)).normalized;
		if (this.CalculateNormalized (currentDirection) == this.CalculateNormalized (this.direction) * -1) 
		{
			return true;
		}
		return false;
	}


	/// <summary>
	/// 规格化向量
	/// </summary>
	/// <returns>The normalized.</returns>
	/// <param name="data">Data.</param>
	private Vector3 CalculateNormalized(Vector3 data)
	{
		Vector3 position = Vector3.zero;
		if(data.x != 0) position.x = (data.x > 0 ? 1 : -1);
		if(data.y != 0) position.y = (data.y > 0 ? 1 : -1);
		return position;
	}
}

需要引用前一篇中的 FrameManager.cs 以及 FrameItem.cs 类,可以查看源代码文件。

测试用例代码如下, RoleItem.cs 代码:

using UnityEngine;
using System.Collections;

public class RoleItem : MonoBehaviour 
{
	private float xPosition;
	private float yPosition;
	private float minRange;
	private float maxRange;
	private float speed;

	public void Init(float xPosition, float yPosition, float minRange, float maxRange, float speed)
	{
		this.xPosition = xPosition;
		this.yPosition = yPosition;
		this.minRange = minRange;
		this.maxRange = maxRange;
		this.speed = speed;

		this.transform.localPosition = new Vector3 (xPosition, 0f, yPosition);
	}

	public void MoveTo (Vector3 targetPosition, System.Action callback)
	{
		this.renderer.material.color = Color.white;
		MoveManager.Register (this.gameObject, targetPosition, speed, (GameObject gameObject) =>
		{
			this.renderer.material.color = Color.red;

			MoveManager.UnRegister(this.gameObject);
			if(callback != null) callback();
		});
	}


	public void Walk()
	{
		// 获取随机范围
		Vector3 targetPosition = this.GetWalkPosition(this.xPosition, this.yPosition, this.minRange, this.maxRange);
		// 调用 移动 AI
		this.MoveTo (targetPosition, () =>
		{
			this.StartCoroutine(this.WalkEnumerator());
		});
	}
	
	private IEnumerator WalkEnumerator()
	{
		yield return new WaitForSeconds (1f);
		// 调用 散步 AI
		this.Walk ();
	}

	public Vector3 GetWalkPosition(float xPosition, float yPosition, float minRange, float maxRange)
	{
		float xOdds = UnityEngine.Random.Range (-1f, 1f);
		float yOdds = UnityEngine.Random.Range (-1f, 1f);
		float xRange = UnityEngine.Random.Range (minRange, maxRange) * (xOdds > 0 ? 1 : -1);
		float yRange = UnityEngine.Random.Range (minRange, maxRange) * (yOdds > 0 ? 1 : -1);
		
		return new Vector3(xPosition + xRange, 0f, yPosition + yRange);
	}
}
Demo.cs 代码:

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

public class Demo : MonoBehaviour 
{
	public GameObject roleObject;
	private List<RoleItem> itemList;

	void Awake()
	{
		this.itemList = new List<RoleItem> ();

		GameObject objectItem = null;
		RoleItem roleItem = null;

		for(int index = 0; index < 500; index ++)
		{
			objectItem = GameObject.Instantiate(this.roleObject) as GameObject;
			roleItem = objectItem.AddComponent<RoleItem>();
			roleItem.Init(Random.Range(-50, 50), Random.Range(-50, 50), Random.Range(5, 50), Random.Range(5, 50), Random.Range(5, 15));

			this.itemList.Add(roleItem);
		}

		this.StartCoroutine (this.AwakeEnumerator());
	}

	private IEnumerator AwakeEnumerator()
	{
		yield return new WaitForSeconds (3f);

		int length = this.itemList.Count;
		for(int index = 0; index < length; index ++)
		{
			this.itemList[index].Walk();
		}
	}

	void Update()
	{
		FrameManager.Run ();
	}
}
例子中,我们定义了 500 个对象随机移动,他们每次移动都是由一个 Update 函数驱动,这样就确保了移动步伐的统一,虽然他们的移动步长会有所不同,可能移动得快,可能移动得慢,但是它们代码执行的顺序都是相同的。并且当移动对象停止的时候,这个移动对象的 Update 函数就不会被调用了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值