关于牧师与魔鬼动作分离版,这里只介绍动作管理器的部分,有需要者请结合我的上一篇博客:牧师与魔鬼,加深理解。
动作管理器的设计:
一开始,我们先创建一下几个类:
1. 动作基类:public class SSAction : ScriptableObject{}
2. 简单动作实现类: public class SSMoveToAction : SSAction {}
3. 组合动作实现类: public class SequenceAction: SSAction, ISSActionCallback{}
4. 动作管理基类:public class SSActionManager: MonoBehaviour, ISSActionCallback{}
以及,我们还要建立一个动作事件接口:public interface ISSActionCallback{}
动作管理器解析:
动作基类:
public class SSAction : ScriptableObject
{
public bool enable = true;
public bool destroy = false;
public GameObject gameobject;
public Transform transform;
public ISSActionCallback callback;
protected SSAction() {}
public virtual void Start()
{
throw new System.NotImplementedException();
}
public virtual void Update()
{
throw new System.NotImplementedException();
}
}
基类创建了动作管理器的基本元素,它继承了Unity的ScriptableObject类,该类是不需要绑定GameObject对象的可编程类。其中的属性包括enable(动作是否进行),destroy(动作是否该被销毁)。同时利用接口实现游戏的通信:callback。
简单动作实现类:
public class SSMoveToAction : SSAction
{
public Vector3 target; //目的地
public float speed; //速度
private SSMoveToAction(){}
public static SSMoveToAction GetSSAction(Vector3 target, float speed)
{
SSMoveToAction action = ScriptableObject.CreateInstance<SSMoveToAction>();
action.target = target;
action.speed = speed;
return action;
}
public override void Update()
{
this.transform.position = Vector3.MoveTowards(this.transform.position, target, speed*Time.deltaTime);
if (this.transform.position == target)
{
this.destroy = true;
this.callback.SSActionEvent(this);
}
}
public override void Start()
{
}
}
顾名思义,该类实现单个动作,实现物体的移动。其中GetSSAction函数通过获取物体运动的目的地和速度,让unity自己建立一个动作类。当动作完成时销毁该动作并告诉管理者动作已经完成,这以功能在UpDate中实现,毕竟要通过每一帧来判断是否该结束动作。
组合动作实现类:
public class SequenceAction: SSAction, ISSActionCallback
{
public List<SSAction> sequence; //动作的列表
public int repeat = -1; //-1就是无限循环做组合中的动作
public int start = 0; //当前做的动作的索引
public static SequenceAction GetSSAcition(int repeat, int start, List<SSAction> sequence)
{
SequenceAction action = ScriptableObject.CreateInstance<SequenceAction>();//让unity自己创建一个SequenceAction实例
action.repeat = repeat;
action.sequence = sequence;
action.start = start;
return action;
}
public override void Update()
{
if (sequence.Count == 0) return;
if (start < sequence.Count)
{
sequence[start].Update();
}
}
public void SSActionEvent(SSAction source, SSActionEventType events = SSActionEventType.Competeted,
int intParam = 0, string strParam = null, Object objectParam = null)
{
source.destroy = false; //先保留这个动作,如果是无限循环动作组合之后还需要使用
this.start++;
if (this.start >= sequence.Count)
{
this.start = 0;
if (repeat > 0) repeat--;
if (repeat == 0)
{
this.destroy = true;
this.callback.SSActionEvent(this);
}
}
}
public override void Start()
{
foreach(SSAction action in sequence)
{
action.gameobject = this.gameobject;
action.transform = this.transform;
action.callback = this;
action.Start();
}
}
void OnDestroy()
{
}
}
这个类先像简单动作类一样创建一个动作类,有所不同的是这里创建的是动作序列,因为要实现的是组合动作嘛。在UpDate中每一帧完成当前动作,当这一序列的动作完成时,停止并通过回调函数告诉管理者该序列动作已完成,并删除序列。在执行动作前,我们需要为每一个动作注入游戏对象,在Start中实现。
动作管理基类:
public class SSActionManager: MonoBehaviour, ISSActionCallback
{
private Dictionary<int, SSAction> actions = new Dictionary<int, SSAction>(); //将执行的动作的字典集合,int为key,SSAction为value
private List<SSAction> waitingAdd = new List<SSAction>(); //等待去执行的动作列表
private List<int> waitingDelete = new List<int>(); //等待删除的动作的key
protected void Update()
{
foreach(SSAction ac in waitingAdd)
{
actions[ac.GetInstanceID()] = ac; //获取动作实例的ID作为key
}
waitingAdd.Clear();
foreach(KeyValuePair<int, SSAction> kv in actions)
{
SSAction ac = kv.Value;
if (ac.destroy)
{
waitingDelete.Add(ac.GetInstanceID());
}
else if (ac.enable)
{
ac.Update();
}
}
foreach(int key in waitingDelete)
{
SSAction ac = actions[key];
actions.Remove(key);
DestroyObject(ac);
}
waitingDelete.Clear();
}
public void RunAction(GameObject gameobject, SSAction action, ISSActionCallback manager)
{
action.gameobject = gameobject;
action.transform = gameobject.transform;
action.callback = manager;
waitingAdd.Add(action);
action.Start();
}
public void SSActionEvent(SSAction source, SSActionEventType events = SSActionEventType.Competeted,
int intParam = 0, string strParam = null, Object objectParam = null)
{
//实现事件处理的空接口。
}
}
首先建立MonoBehavior管理动作,动作做完自动回收,在这之中必然有需要等待的动作和已经做完的动作,显然,需要等待的动作加入等待列表,需要删除的动作加入删除列表。提供了运行一个新动作的方法RunAction。该方法把游戏对象与动作绑定,并绑定该动作事件的消息接收者。
动作事件接口:
public enum SSActionEventType : int { Started, Competeted }
public interface ISSActionCallback
{
void SSActionEvent(SSAction source, SSActionEventType events = SSActionEventType.Competeted,
int intParam = 0, string strParam = null, Object objectParam = null);
}
定义了事件处理接口,所有事件管理者都必须实现这个接口,来实现
事件调度。所以,组合事件需要实现它,事件管理器也必须实现它。
实战管理:
public class MySceneActionManager:SSActionManager
{
private SSMoveToAction move_boat;
private SequenceAction move_role;
public MySceneController sceneController;
protected new void Start()
{
sceneController = (MySceneController)Director.get_Instance().curren;
sceneController.actionManager = this;
}
public void moveBoat(GameObject boat, Vector3 target, float speed)
{
move_boat = SSMoveToAction.GetSSAction(target, speed);
this.RunAction(boat, move_boat, this);
}
public void moveRole(GameObject role, Vector3 middle_pos, Vector3 end_pos,float speed)
{
SSAction action1 = SSMoveToAction.GetSSAction(middle_pos, speed);
SSAction action2 = SSMoveToAction.GetSSAction(end_pos, speed);
move_role = SequenceAction.GetSSAcition(1, 0, new List<SSAction>{action1, action2});
this.RunAction(role, move_role, this);
}
}
实现之前moveable类中的船运动和角色运动的两个函数。
完整代码github地址:wangld5