本文主要是介绍lab06:牧师与魔鬼游戏 动作分离版,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
这里写自定义目录标题
- 整体描述
- UML图
- 代码介绍
- action part
- controller part
- view part
- 代码地址
整体描述
本次项目在第一版牧师与魔鬼的基础上,将动作从场记中分离出来,并设计一个裁判类监测游戏进行的。
这样改进的优点:
1.降低了不同功能之间的耦合性,代码的复用性更好。
2.通过门面模式,程序更加容易进行修改。
UML图
参考了经典的cocos2d方案
代码介绍
action part
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 updatepublic virtual void Start(){throw new System.NotImplementedException();}// Update is called once per framepublic virtual void Update(){throw new System.NotImplementedException();}
}
ISSActionCallback 接口类,定义了一个回调函数。
SSActionEvent 是回调函数,当动作完成后需要通知主控制器。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public enum SSActionEventType : int { Started, Competeted }
public interface ISSActionCallback
{public void SSActionEvent(SSAction source,SSActionEventType events = SSActionEventType.Competeted,int intParam = 0, string strParam = null, Object objectParam = null);
}
SSActionManager 动作管理基类
在Update 中实现所有动作的基本管理。
思路是遍历动作字典中的所有动作,查看它们的信息,进行销毁或更新操作。
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 updateprotected void Start(){}// Update is called once per frameprotected 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 继承了SSAction基类
完成了通用的单个GameObject移动实现。
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 updatepublic override void Start(){}// Update is called once per framepublic 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 继承了SSAction基类,作用是组合动作实现
思路是通过一个list存储组合动作中的各个子动作,并按存放的顺序依次执行。
根据参数 repeat 判断是否要重复执行组合动作。
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 updatepublic 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 framepublic 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(){}
}
SSActionManager 动作管理基类
动作对象管理器的基类,实现了所有动作的基本管理
update:每一帧 添加、删除等复杂集合对象的使用。
提供了添加新动作的方法 RunAction。该方法把游戏对象与动作绑定,并绑定该动作事件的消息接收者。
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 updateprotected void Start(){}// Update is called once per frameprotected 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();}
}
CCActionManager 动作组合
MoveBoat 移动船,简单动作,对应为 CCMoveToAction。
MoveRole 移动人物,组合动作,对应为 CCSequenceAction
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;}
}
controller part
JudgeController 裁判类
裁判类的作用是在每一帧update里判断游戏进行的状态并通知主控制器
就是将原来 FirstController 中的 判断游戏是否结束的逻辑分离出来用一个类实现。
在 Update 中实现判断的逻辑。
当游戏结束,需要通知firstcontroller时,我们在firstCotroller 中添加一个回调函数,在裁判类中调用该回调函数通知firstcontroller。
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 updatevoid 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 framevoid Update(){if (sceneController.isRunning == false){return;}if (rightLand.priestCount == 3){// win, callbacksceneController.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)){// losesceneController.JudgeResultCallBack("Game over!!");return;}}}
}
FirstController 场景主控制器
与v1版本主要不同:不实现移动和检查的逻辑,交由CCActionManager与JudgeController类实现。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class FirstController : MonoBehaviour, ISceneController, IUserAction
{// 控制器public LandController leftLandController, rightLandController;public BoatController boatController;public RoleController[] roleControllers;// 比较简单不单独创建控制器River river;// 场景是否正在运行public bool isRunning;public float time;public CCActionManager actionManager;public float speed = 5;public void LoadResources(){// 场景是否正在运行isRunning = true;// 实例化role ControllersroleControllers = new RoleController[6];for (int i = 0; i < 6; ++i){roleControllers[i] = new RoleController();roleControllers[i].CreateRole(DefaultPosition.role_land[i], i < 3 ? true : false, i);}// 实例化land controllerleftLandController = new LandController();leftLandController.CreateLand(DefaultPosition.left_land);leftLandController.GetLand().land.name = "left_land";rightLandController = new LandController();rightLandController.CreateLand(DefaultPosition.right_land);rightLandController.GetLand().land.name = "right_land";// 添加人物并放置在左岸 foreach (RoleController roleController in roleControllers){roleController.GetRoleModel().role.transform.localPosition =leftLandController.AddRole(roleController.GetRoleModel());}// 添加船只控制器boatController = new BoatController();boatController.CreateBoat(DefaultPosition.left_boat);// 创建河流对象river = new River(DefaultPosition.river);time = 60;}public void MoveBoat(){if (isRunning == false || actionManager.CheckMoving() == true) return;Vector3 target;if (boatController.GetBoatModel().isRight){target = DefaultPosition.left_boat;}else{target = DefaultPosition.right_boat;}actionManager.MoveBoat(boatController.GetBoatModel().boat, target, speed);boatController.GetBoatModel().isRight = !boatController.GetBoatModel().isRight;}public void MoveRole(Role roleModel){if (isRunning == false || actionManager.CheckMoving() == true) return;Vector3 middle_pos;Vector3 target;if (roleModel.inBoat){if (boatController.GetBoatModel().isRight){target = rightLandController.AddRole(roleModel);}else{target = leftLandController.AddRole(roleModel);}middle_pos = new Vector3(roleModel.role.transform.localPosition.x, target.y, target.z);actionManager.MoveRole(roleModel.role, middle_pos, target, speed);roleModel.onRight = boatController.GetBoatModel().isRight;boatController.RemoveRole(roleModel);}else{if (boatController.GetBoatModel().isRight == roleModel.onRight){if (roleModel.onRight){rightLandController.RemoveRole(roleModel);}else{leftLandController.RemoveRole(roleModel);}target = boatController.AddRole(roleModel);middle_pos = new Vector3(target.x, roleModel.role.transform.localPosition.y, target.z);actionManager.MoveRole(roleModel.role, middle_pos, target, speed);}}}public void Restart(){SSDirector.ReloadCurrentScene();}public void JudgeResultCallBack(string result){this.gameObject.GetComponent<UserGUI>().SetMessage(result);this.gameObject.GetComponent<UserGUI>().IsGameEnd = true;this.isRunning = false;}void Awake(){SSDirector.GetInstance().CurrentSceneController = this;LoadResources();this.gameObject.AddComponent<UserGUI>();this.gameObject.AddComponent<CCActionManager>();this.gameObject.AddComponent<JudgeController>();}void Update(){}
}
view part
基本没有改动,逻辑参考v1版本的博客说明。
代码地址
github地址
这篇关于lab06:牧师与魔鬼游戏 动作分离版的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!