在上节课的牧师与魔鬼(MVC模式)的基础上,增加动作分离,将动作分离成一个独立的类,并为模型提供调用接口。
动作类
SSActionManager:
使用一个字典来存储当前正在运行的动作,将动作的生命周期分为添加、更新和删除三个阶段,,管理运行动作队列。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
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>();
protected void Update()
{
//将waitingAdd中的动作保存
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();
}
}
//销毁waitingDelete中的动作
foreach (int key in waitingDelete)
{
SSAction ac = actions[key];
actions.Remove(key);
Destroy(ac);
}
waitingDelete.Clear();
}
//准备运行一个动作,将动作初始化,并加入到waitingAdd
public void RunAction(GameObject gameObject, SSAction action, ISSActionCallback manager)
{
action.gameObject = gameObject;
action.transform = gameObject.transform;
action.callback = manager;
waitingAdd.Add(action);
action.Start();
}
// Start is called before the first frame update
protected void Start()
{
}
}
SSAction:
为具体的动作提供了基础结构,允许开发者通过继承该类来实现特定的动作逻辑。每个具体的动作类需要实现 Start
和 Update
方法,以定义动作的开始和持续行为。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SSAction : ScriptableObject
{
public bool enable = true;
public bool destroy = false;
public GameObject gameObject { get; set; }
public Transform transform { get; set; }
public ISSActionCallback callback { get; set; }
protected SSAction()
{
}
// Start is called before the first frame update
public virtual void Start()
{
throw new System.NotImplementedException();
}
// Update is called once per frame
public virtual void Update()
{
throw new System.NotImplementedException();
}
}
CCSequenceAction:
可以将多个 SSAction
组合成一个序列,方便地控制它们的执行顺序和重复次数。这种结构使得动作的组合和管理更加灵活,适合需要顺序执行多个动画或事件的场景。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CCSequenceAction : SSAction, ISSActionCallback
{
//动作序列
public List<SSAction> sequence;
//重复次数
public int repeat = -1;
//动作开始指针
public int start = 0;
//生产函数(工厂模式)
public static CCSequenceAction GetSSAction(int repeat, int start, List<SSAction> sequence)
{
CCSequenceAction action = ScriptableObject.CreateInstance<CCSequenceAction>();
action.repeat = repeat;
action.start = start;
action.sequence = sequence;
return action;
}
//对序列中的动作进行初始化
public override void Start()
{
foreach (SSAction action in sequence)
{
action.gameObject = this.gameObject;
action.transform = this.transform;
action.callback = this;
action.Start();
}
}
//运行序列中的动作
public override void Update()
{
if (sequence.Count == 0)
return;
if (start < sequence.Count)
{
sequence[start].Update();
}
}
//回调处理,当有动作完成时触发
public void SSActionEvent(SSAction source,
SSActionEventType events = SSActionEventType.Completed,
int Param = 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);
}
}
}
void OnDestroy()
{
}
}
CCMoveToAction:
提供位置和速度属性值,可通过通过工厂模式和 Update
方法实现对象移动。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CCMoveToAction : SSAction
{
//目的地
public Vector3 target;
//速度
public float speed;
private CCMoveToAction()
{
}
//生产函数(工厂模式)
public static CCMoveToAction GetSSAction(Vector3 target, float speed)
{
CCMoveToAction action = ScriptableObject.CreateInstance<CCMoveToAction>();
action.target = target;
action.speed = speed;
return action;
}
// Start is called before the first frame update
public override void Start()
{
}
// Update is called once per frame
public override void Update()
{
//判断是否符合移动条件
if (this.gameObject == null || this.transform.localPosition == target)
{
this.destroy = true;
this.callback.SSActionEvent(this);
return;
}
//移动
this.transform.localPosition = Vector3.MoveTowards(this.transform.localPosition, target, speed * Time.deltaTime);
}
}
CCActionManager:
实现牧师/魔鬼和船的具体移动。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CCActionManager : SSActionManager, ISSActionCallback
{
//是否正在运动
private bool isMoving = false;
//船移动动作类
public CCMoveToAction moveBoatAction;
//人移动动作类(需要组合)
public CCSequenceAction moveRoleAction;
//控制器
public FirstController controller;
protected new void Start()
{
controller = (FirstController)SSDirector.GetInstance().CurrentSceneController;
controller.actionManager = this;
}
public bool IsMoving()
{
return isMoving;
}
//移动船
public void MoveBoat(GameObject boat, Vector3 target, float speed)
{
if (isMoving)
return;
isMoving = true;
moveBoatAction = CCMoveToAction.GetSSAction(target, speed);
this.RunAction(boat, moveBoatAction, this);
}
//移动人
public void MoveRole(GameObject role, Vector3 mid_destination, Vector3 destination, int speed)
{
if (isMoving)
return;
isMoving = true;
moveRoleAction = CCSequenceAction.GetSSAction(0, 0, new List<SSAction> { CCMoveToAction.GetSSAction(mid_destination, speed), CCMoveToAction.GetSSAction(destination, speed) });
this.RunAction(role, moveRoleAction, this);
}
//回调函数
public void SSActionEvent(SSAction source,
SSActionEventType events = SSActionEventType.Completed,
int intParam = 0,
string strParam = null,
Object objectParam = null)
{
isMoving = false;
}
}
游戏效果:如图