主要的代码结构为:
动作分离主要是将动作通过门面模式交给动作控制管理器来管理,外界可以通过调用动作管理器中的函数来控制动作的发生,将管理和实现分离。
回调函数接口
这里采用接口来表示,每一个动作完成后,调用者会进行的操作,这一操作被放置在了回调函数里。
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); }
这里使用了一个枚举来表示动作事件的类型:开始、完成。
动作基类(SSAction)
public class SSAction : ScriptableObject { public bool enable = true; //是否可进行 public bool destroy = false; //是否已完成 public GameObject gameobject; //动作对象 public Transform transform; //动作对象的transform public ISSActionCallback callback; //回调函数 /*防止用户自己new对象*/ protected SSAction() { } public virtual void Start() { throw new System.NotImplementedException(); } public virtual void Update() { throw new System.NotImplementedException(); } }
动作分为单个动作和组合动作
单个动作
单个动作主要有两个操作,一个是对对象的初始化,另一个是动作完成的通知动作管理者或者动作组合。
具体代码如下:
//这里利用Unity自带的构造函数创建对象,并初始化 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 List<SSAction> sequence; //动作的列表 public int repeat = -1; //-1就是无限循环 public int start = 0; //当前做的动作的标号 //让序列中的每一个动作都执行 public override void Update() { if (sequence.Count == 0) return; if (start < sequence.Count) { sequence[start].Update(); //执行之后,通过回调函数让start值递增 } } //回调函数 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); //通知管理者 } } }
动作管理器基类(SSActionManager)
该类可以自己实现对于动作的控制,并且可以提供接口给外部函数调用。
这里有两个队列,一个是等待添加队列,一个是等待删除的队列,因为每次update的过程中不可能立刻对新增动作和已执行动作进行处理,因此需要一个队列进行缓冲。
public class SSActionManager : MonoBehaviour { private Dictionary<int, SSAction> actions = new Dictionary<int, SSAction>(); //动作字典 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; } 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); Object.Destroy(ac);//让Unity帮着删除 } waitingDelete.Clear(); } //外界只需要调用动作管理类的RunAction函数即可完成动作。 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 moveBoat(GameObject boat, Vector3 end, float speed) { boatMove = SSMoveToAction.GetSSAction(end, speed); this.RunAction(boat, boatMove, this); } public void moveRole(GameObject role, Vector3 middle, Vector3 end, float speed) { //两段移动 SSAction action1 = SSMoveToAction.GetSSAction(middle, speed); SSAction action2 = SSMoveToAction.GetSSAction(end, speed); //两个动作,都只重复一次 roleMove = SequenceAction.GetSSAcition(1, 0, new List<SSAction> { action1, action2 }); this.RunAction(role, roleMove, this); }
GameCheck(游戏结束判断)
该类主要用于判断游戏是否结束。
using System.Collections; using System.Collections.Generic; using UnityEngine; using GhostBoatGame; public class GameCheck : MonoBehaviour { public Controller sceneController; protected void Start() { sceneController = (Controller)SSDirector.GetInstance().CurrentScenceController; } public int GameJudge() { int src_priest = sceneController.src_land.GetTotal(0); int src_ghost = sceneController.src_land.GetTotal(1); int des_priest = sceneController.des_land.GetTotal(0); int des_ghost = sceneController.des_land.GetTotal(1); if (des_priest == 3) { //全部到终点,获胜 return 1; } if (sceneController.boat.GetBoatMark() == 1)//由于在这一边船还没开,因此只需检测另一边的数量即可。 { if (des_priest < des_ghost && des_priest > 0) {//失败 return -1; } } else { if (src_priest < src_ghost && src_priest > 0) {//失败 return -1; } } return 0;//游戏继续进行 } }
从判断方程看出,只需要在终点牧师数量为3即可。