一、回顾
上一次实现了MVC版本,也就是model、view、controller分离的版本。在MVC版本的controller里面,实现了对model的动作控制。在动作种类增多、单一动作需要被复用、某个动作由多个动作联合实现 等等情况下,仍旧在controller里面实现动作逻辑就显得冗余且难以管理。因此,将动作的具体实现逻辑从controller中提取出来,放到Action模块里面定义和管理,同时controller增加对Action模块内的动作管理器的调用,可以较好的解决上述困扰。顾名思义,这个船新版本就是“动作分离版”。
二、实现
旧的scrpits结构:
新的scripts结构:
1、Action:
SSActionManager.cs:
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()
{
}
}
对应的继承于SSActionManager的是 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()
{
UnityEngine.Debug.Log("CCActionManager");
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 destination, int speed)
{
Vector3 mid_destination;
if (role.transform.localPosition.y > destination.y)
mid_destination = new Vector3(destination.x, role.transform.localPosition.y, destination.z);
else
mid_destination = new Vector3(role.transform.localPosition.x, destination.y, destination.z);
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;
}
}
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();
}
}
对应的继承于SSAction的是CCMoveToAction(单个动作)、CCSequenceAction(组合动作)。 --(动作定义)
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);
}
}
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()
{
}
}
为了实现controller(FirstController)与动作管理器之间的交互(后者告诉前者一些信息),需要实现回调函数:(ISSActionCallback.cs)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public enum SSActionEventType:int {Started, Completed}
public interface ISSActionCallback
{
//回调函数
void SSActionEvent(SSAction source,
SSActionEventType events = SSActionEventType.Completed,
int intParam = 0,
string strParam = null,
Object objectParam = null);
}
2、FirstController、JudgeController
首先需要对firstcontroller进行修改:
把原来使用MoveController的部分去掉,因为move(动作)已经从controller里面分离出来了。取而代之的是:1、CCActionManager。2、一个裁判JudgeController(下面讲)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class FirstController : MonoBehaviour, ISceneController, IUserAction
{
public static int LEFTLAND = 0;
public static int RIGHTLAND = 1;
public static int BOAT = 2;
public static int PRIEST = 0;
public static int DEVIL = 1;
public static int PLAYING = 0;
public static int WIN = 1;
public static int FAILED = 2;
public CCActionManager actionManager;
public JudgeController JudgeCtrl;
public BoatController BoatCtrl;
public RoleController[] RoleCtrl = new RoleController[6];
public LandController[] LandCtrl = new LandController[2];
public MoveController MoveCtrl;
int[] rolesID = new int[6]{0,1,2,3,4,5};
public int gameState;
void Awake(){
SSDirector director = SSDirector.GetInstance();
director.CurrentSceneController = this;
director.CurrentSceneController.Initialize();
this.gameObject.AddComponent<UserGUI>();
this.gameObject.AddComponent<CCActionManager>();
this.gameObject.AddComponent<JudgeController>();
}
public void JudgeCallback(int gameState){
this.gameState=gameState;
}
public void Initialize(){
//如果有,则释放原有的GameObject
for(int i = 0; i < 6; i++){
if(RoleCtrl[i] != null){
Destroy(RoleCtrl[i].GetModelGameObject());
}
}
for(int i = 0; i < 2; i++){
if(LandCtrl[i] != null){
Destroy(LandCtrl[i].GetModelGameObject());
}
}
if(BoatCtrl != null){
Destroy(BoatCtrl.GetModelGameObject());
}
// 加载控制器和模型
BoatCtrl = new BoatController();
BoatCtrl.CreateModel();
JudgeCtrl = new JudgeController();
for(int i = 0; i < 6; i++){
int roleType = (i < 3) ? PRIEST : DEVIL;
RoleCtrl[i] = new RoleController(roleType, rolesID[i]);
RoleCtrl[i].CreateModel();
}
LandCtrl[0] = new LandController(LEFTLAND, rolesID);
LandCtrl[1] = new LandController(RIGHTLAND, rolesID);
LandCtrl[0].CreateModel();
LandCtrl[1].CreateModel();
//MoveCtrl = new MoveController();
//开始游戏
gameState = PLAYING;
}
//将角色的ID转换成数组的下标
int IDToNumber(int ID){
for(int i = 0; i < 6; i++){
if(rolesID[i] == ID){
return i;
}
}
return -1;
}
//点击船时执行
public void MoveBoat(){
if(gameState != PLAYING || actionManager.IsMoving() || BoatCtrl.isEmpty()) return;//MoveCtrl.IsMoving()
//CheckAndSetGameState();
if(BoatCtrl.onLeftside){
//MoveCtrl.SetMove(BoatCtrl.GetModelGameObject(), Position.boatRightPos);
for(int i = 0; i < BoatCtrl.seatNum; i++){
if(BoatCtrl.seat[i] != -1){
RoleController r = RoleCtrl[IDToNumber(BoatCtrl.seat[i])];
UnityEngine.Debug.Log(r.GetModelGameObject().transform.parent==null);
r.GetModelGameObject().transform.parent = BoatCtrl.GetModelGameObject().transform;
//MoveCtrl.SetMove(r.GetModelGameObject(), Position.seatRightPos[i]);
//actionManager.MoveRole(r.GetModelGameObject(),Position.seatRightPos[i],5);
}
}
actionManager.MoveBoat(BoatCtrl.GetModelGameObject(),Position.boatRightPos,5);
}
else{
actionManager.MoveBoat(BoatCtrl.GetModelGameObject(),Position.boatLeftPos,5);
//MoveCtrl.SetMove(BoatCtrl.GetModelGameObject(), Position.boatLeftPos);
for(int i = 0; i < BoatCtrl.seatNum; i++){
if(BoatCtrl.seat[i] != -1){
RoleController r = RoleCtrl[IDToNumber(BoatCtrl.seat[i])];
r.GetModelGameObject().transform.parent = BoatCtrl.GetModelGameObject().transform;
//actionManager.MoveRole(r.GetModelGameObject(),Position.seatLeftPos[i],5);
//MoveCtrl.SetMove(r.GetModelGameObject(), Position.seatLeftPos[i]);
}
}
}
BoatCtrl.onLeftside = !BoatCtrl.onLeftside;
}
//点击角色时执行
public void MoveRole(int id){
int num = IDToNumber(id);
if(gameState != PLAYING || actionManager.IsMoving()) return;
int seat;
switch(RoleCtrl[num].roleState){
case 0: // LEFTLAND
if(!BoatCtrl.onLeftside || (BoatCtrl.getEmptySeat() == -1)) return;//增加判断getEmptySeat :
LandCtrl[0].LeaveLand(id); //如果上不了船,就直接返回,而
seat = BoatCtrl.embark(id); //不用改动role、land、boat的位置参数
RoleCtrl[num].GoTo(BOAT);
if(seat == -1) return; //增加判断getEmptySeat后,这句应该可以略去
actionManager.MoveRole(RoleCtrl[num].GetModelGameObject(),Position.seatLeftPos[seat],5);
//MoveCtrl.SetMove(RoleCtrl[num].GetModelGameObject(), Position.seatLeftPos[seat]);
break;
case 1: // RIGHTLAND
if(BoatCtrl.onLeftside || (BoatCtrl.getEmptySeat() == -1)) return;//同上
LandCtrl[1].LeaveLand(id);
seat = BoatCtrl.embark(id);
RoleCtrl[num].GoTo(BOAT);
if(seat == -1) return;
actionManager.MoveRole(RoleCtrl[num].GetModelGameObject(),Position.seatRightPos[seat],5);
//MoveCtrl.SetMove(RoleCtrl[num].GetModelGameObject(), Position.seatRightPos[seat]);
break;
case 2: //BOAT
if(BoatCtrl.onLeftside){
RoleCtrl[num].GetModelGameObject().transform.parent=null;
seat = LandCtrl[0].getEmptySeat();
BoatCtrl.disembark(id);
LandCtrl[0].GoOnLand(id);
RoleCtrl[num].GoTo(LEFTLAND);
actionManager.MoveRole(RoleCtrl[num].GetModelGameObject(),Position.roleLeftPos[seat],5);
//MoveCtrl.SetMove(RoleCtrl[num].GetModelGameObject(), Position.roleLeftPos[seat]);
}
else{
//UnityEngine.Debug.Log("mid_d");
RoleCtrl[num].GetModelGameObject().transform.parent=null;//LandCtrl[1].GetModelGameObject().transform;
seat = LandCtrl[1].getEmptySeat();
BoatCtrl.disembark(id);
LandCtrl[1].GoOnLand(id);
RoleCtrl[num].GoTo(RIGHTLAND);
//CheckAndSetGameState();//新添加一个在这里,在最后一个角色上右岸之后判定成功
actionManager.MoveRole(RoleCtrl[num].GetModelGameObject(),Position.roleRightPos[seat],5);
//MoveCtrl.SetMove(RoleCtrl[num].GetModelGameObject(), Position.roleRightPos[seat]);
}
break;
default: break;
}
}
//判断游戏状态
// public void CheckAndSetGameState(){
// if(gameState != PLAYING) return;
// //判断是否失败
// int[,] rolePos = new int[2, 3]{{0, 0, 0}, {0, 0, 0}};
// foreach(RoleController r in RoleCtrl){
// rolePos[r.roleType, r.roleState]++; //roletype: 0天使、1魔鬼
// } //rolestate:0左岸、1右岸、2船上
// if((rolePos[0,0]>0 && rolePos[0,0]<rolePos[1,0]) || //左岸存在天使 && 左岸的天使数量少于左岸魔鬼的数量
// (rolePos[0,1]>0 && rolePos[0,1]<rolePos[1,1]) || //右岸存在天使 && 右岸的天使数量少于右岸魔鬼的数量
// (rolePos[0,2]>0 && rolePos[0,2] < rolePos[1,2])){ //船上最多只能承载两个角色时,这行可以删去
// gameState = FAILED;
// return;
// }
// //判断是否成功
// foreach(RoleController r in RoleCtrl){
// if(r.roleState != RIGHTLAND){//存在角色未到达右岸,则直接返回 //r.roleType == 0 &&
// UnityEngine.Debug.Log("这里");
// return;
// }
// }
// UnityEngine.Debug.Log("其实是这里");
// gameState = WIN; //上面的情况都不符合,则说明所有角色都安全到达右岸,win
// return;
// }
//Reset按钮执行的功能
public void Restart(){
Initialize();
gameState = PLAYING;
}
//获取游戏当前状态
public int GetGameState(){
return gameState;
}
}
JudgeController:
JudgeController的作用其实是原来FirstController对游戏状态的判断的那一部分逻辑的实现。
与动作分离类似,将这部分逻辑实现(游戏什么时候Win、Fail等等)分离出来,放到裁判类JudgeController里面,减少firstcontroller的“负担”
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class JudgeController : MonoBehaviour{
public FirstController firstCtrl;
void Start(){
UnityEngine.Debug.Log("judgeControl here start");
firstCtrl = (FirstController)SSDirector.GetInstance().CurrentSceneController;
}
//判断游戏状态
void Update(){
if(firstCtrl.gameState != FirstController.PLAYING) return;
//判断是否失败
int[,] rolePos = new int[2, 3]{{0, 0, 0}, {0, 0, 0}};
foreach(RoleController r in firstCtrl.RoleCtrl){
rolePos[r.roleType, r.roleState]++; //roletype: 0天使、1魔鬼
} //rolestate:0左岸、1右岸、2船上
if((rolePos[0,0]>0 && rolePos[0,0]<rolePos[1,0]) || //左岸存在天使 && 左岸的天使数量少于左岸魔鬼的数量
(rolePos[0,1]>0 && rolePos[0,1]<rolePos[1,1]) || //右岸存在天使 && 右岸的天使数量少于右岸魔鬼的数量
(rolePos[0,2]>0 && rolePos[0,2] < rolePos[1,2])){ //船上最多只能承载两个角色时,这行可以删去
firstCtrl.JudgeCallback(FirstController.FAILED);
//firstCtrl.gameState = FAILED;
return;
}
//判断是否成功
foreach(RoleController r in firstCtrl.RoleCtrl){
if(r.roleState != FirstController.RIGHTLAND){//存在角色未到达右岸,则直接返回 //r.roleType == 0 &&
UnityEngine.Debug.Log("这里");
return;
}
}
UnityEngine.Debug.Log("其实是这里");
firstCtrl.JudgeCallback(FirstController.WIN);
//firstCtrl.gameState = WIN; //上面的情况都不符合,则说明所有角色都安全到达右岸,win
return;
}
}
三、运行
首先新建一个空物体。
与上一个不同,需要将:JudgeController、CCActionManager、FirstController都拖到空物体上。
然后将View模块的UserGUI拖到场景Camara中。