本次在上一版牧师与恶魔的基础上,实现了动作分离,将各物体的运动行为从场景控制器中分离出来并集成为CCAction,提高了代码的可读性与维护性,使得控制器部分不至于过于臃肿
大体思路
UML图如下
新增内容
裁判类JudgeController
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class JudgeController : MonoBehaviour
{
public FirstController sceneController;
public Land rightLand;
public Land leftLand;
public Boat boat;
// Start is called before the first frame update
void Start()
{
this.sceneController = (FirstController)SSDirector.GetInstance().CurrentSceneController;
this.rightLand = sceneController.rightLandController.GetLand();
this.leftLand = sceneController.leftLandController.GetLand();
this.boat = sceneController.boatController.GetBoatModel();
}
// Update is called once per frame
void Update()
{
if(sceneController.isRunning == false){
return;
}
this.gameObject.GetComponent<UserGUI>().gameMessage = "";
if(rightLand.priestCount == 3){
// win, callback
sceneController.JudgeResultCallBack("You win!!");
return;
}
else{
int leftPriestCount, rightPriestCount, leftDevilCount, rightDevilCount;
leftPriestCount = leftLand.priestCount + (boat.isRight ? 0 : boat.priestCount);
rightPriestCount = 3 - leftPriestCount;
leftDevilCount = leftLand.devilCount + (boat.isRight ? 0: boat.devilCount);
rightDevilCount = 3 - leftDevilCount;
if((leftPriestCount != 0 && leftPriestCount < leftDevilCount) || (rightPriestCount != 0 && rightPriestCount < rightDevilCount)){
// lose
sceneController.JudgeResultCallBack("Game over!!");
return;
}
}
裁判类其实就是将原先判断游戏是否结束的check函数重写成一个类,通过消息传递使得check函数从场景控制器中分离出去,这样减少了场景控制器的冗余,维护起来也更便捷
SSAction动作基类
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();
}
}
该接口定义了两个虚函数Start和Update,若子类中未进行实现则会抛出异常
ISSActionCallback
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public enum SSActionEventType : int { Started, Competeted }
public interface ISSActionCallback
{
// Start is called before the first frame update
// void Start()
// {
// }
// // Update is called once per frame
// void Update()
// {
// }
public void SSActionEvent(SSAction source,
SSActionEventType events = SSActionEventType.Competeted,
int intParam = 0, string strParam = null, Object objectParam = null);
}
该接口的SSActionEvent为回调函数,动作结束后通知主控制器进行其他操作
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>();
// Start is called before the first frame update
protected void Start()
{
}
// Update is called once per frame
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);
Destroy(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();
}
}
该类是管理游戏动作的类,将动作添加到字典中,在每一帧进行动作的添加与删除,通过回调函数来通知控制器动作完成
CCMoveToAction
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CCMoveToAction : SSAction
{
public Vector3 target; // 移动后的目标位置
public float speed;
private CCMoveToAction(){
}
// Start is called before the first frame update
public override void Start()
{
}
// Update is called once per frame
public override void Update()
{
this.transform.localPosition = Vector3.MoveTowards(this.transform.localPosition, target, speed * Time.deltaTime);
// 如果游戏对象不存在或者当前位置已在目标位置上,则不移动
if(this.transform.localPosition == target || this.gameobject == null){
this.destroy = true; // 标记为销毁
this.callback.SSActionEvent(this); // 回调函数
return;
}
}
public static CCMoveToAction GetSSAction(Vector3 target, float speed){
CCMoveToAction action = ScriptableObject.CreateInstance<CCMoveToAction>();
action.target = target;
action.speed = speed;
return action;
}
}
该类里实现了简单的运动,当物体运动到目标位置时,销毁该动作,并通知控制器动作完成,进行下一步操作
CCSequenceAction
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;
// Start is called before the first frame update
public override void Start()
{
// 初始化列表中的动作
foreach(SSAction action in sequence){
action.gameobject = this.gameobject;
action.transform = this.transform;
action.callback = this;
action.Start();
}
}
// Update is called once per frame
public override void Update()
{
if(sequence.Count <= 0){
return;
}
if(sequence.Count > 0 && start < sequence.Count){
sequence[start].Update();
}
else{
return;
}
}
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 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(this.repeat > 0){
this.repeat--;
}
else{
this.destroy = true;
this.callback.SSActionEvent(this);
}
}
}
void OnDestroy(){
}
}
该类实现了组合运动,使得简单运动按序进行
CCActionManager
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CCActionManager : SSActionManager, ISSActionCallback
{
public CCMoveToAction boatMovement;
public CCSequenceAction roleMovement;
public FirstController controller;
private bool isMoving = false;
protected new void Start()
{
controller = (FirstController)SSDirector.GetInstance().CurrentSceneController;
controller.actionManager = this;
}
public bool CheckMoving()
{
return isMoving;
}
public void MoveBoat(GameObject boat, Vector3 target, float speed)
{
if (isMoving)
return;
isMoving = true;
boatMovement = CCMoveToAction.GetSSAction(target, speed);
this.RunAction(boat, boatMovement, this);
}
public void MoveRole(GameObject role, Vector3 middle_pos, Vector3 target, float speed)
{
if (isMoving)
return;
isMoving = true;
SSAction ac1 = CCMoveToAction.GetSSAction(middle_pos, speed);
SSAction ac2 = CCMoveToAction.GetSSAction(target, speed);
roleMovement = CCSequenceAction.GetSSAction(0, 0, new List<SSAction> {CCMoveToAction.GetSSAction(middle_pos, speed), CCMoveToAction.GetSSAction(target, speed)});
this.RunAction(role, roleMovement, this);
}
public void SSActionEvent(SSAction source,
SSActionEventType events = SSActionEventType.Competeted,
int intParam = 0,
string strParam = null,
Object objectParam = null)
{
isMoving = false;
}
}
该类管理船体与人物的运动,船运动对应CCMoveToAction,人运动对应CCSequenceAction