转载自: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 函数就不会被调用了。