本文主要是介绍unity小游戏-牧师与魔鬼-动作分离版,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
一、回顾
上一次实现了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();}//准备运行一个动作,将动作初始化,并加入到waitingAddpublic 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 updateprotected 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);elsemid_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 updatepublic virtual void Start(){throw new System.NotImplementedException();}// Update is called once per framepublic 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 updatepublic override void Start(){}// Update is called once per framepublic 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(){//如果有,则释放原有的GameObjectfor(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: // LEFTLANDif(!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: // RIGHTLANDif(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: //BOATif(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; //上面的情况都不符合,则说明所有角色都安全到达右岸,winreturn;}}
三、运行
首先新建一个空物体。
与上一个不同,需要将:JudgeController、CCActionManager、FirstController都拖到空物体上。
然后将View模块的UserGUI拖到场景Camara中。
视频链接:unity小游戏牧师与魔鬼-动作分离版_哔哩哔哩bilibili
这篇关于unity小游戏-牧师与魔鬼-动作分离版的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!