Unity实战之牧师与魔鬼(动作分离版)
Unity实战之牧师与魔鬼(动作分离版)
项目链接
整体描述
本次项目在第一版牧师与魔鬼的基础上,将动作从场记中分离出来,并设计一个裁判类实时监测游戏进行的情况。这样改进的优点有很多:
- 降低了不同功能之间的耦合性,代码的复用性更好。
- 通过门面模式的设计,程序更加容易进行维护。
设计思路
本次改进参考了上课时介绍的cocos2d方案,它的UML图如下:
接下来将逐个介绍其中的类
动作管理(Action)
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 {// 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); }
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
简单移动实现。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
组合动作实现通过一个列表存储组合动作中的各个子动作,并按存放的顺序依次执行。根据参数
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(){} }
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)
JudgeController
裁判类(新增)裁判类的作用是判断游戏进行的状态并通知主控制器,简单来说就是将原来
FirstController
中的Check
抽离出来用一个类单独实现。需要在每一帧判断当前游戏的输赢,因此需要输入相关的变量进行判断。在
Update
中实现判断的逻辑,以达到实施监测的效果。这里有一个问题,我们需要利用到判断的结果在主控制器中进行相应的处理(打印信息等等),但
Update
函数不返回任何值(void类型)。解决的方法是,在FirstCotroller
中设计一个回调函数JudgeResultCallBack
,在裁判类中调用JudgeResultCallBack
以返回判断结果。
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;}this.gameObject.GetComponent<UserGUI>().gameMessage = "";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
场景控制器与上一次项目的主要不同是,这次的场景控制器并不具体实现移动和检查的逻辑,而是交由相应的功能类实现。
using System.Collections; using System.Collections.Generic; using UnityEngine;public class FirstController : MonoBehaviour, ISceneController, IUserAction {public LandControl leftLandController, rightLandController;public River river;public BoatControl boatController;public RoleControl[] roleControllers;public MoveCtrl moveController;public bool isRunning;public float time;public CCActionManager actionManager;public float speed = 5;public void LoadResources() {roleControllers = new RoleControl[6];for (int i = 0; i < 6; ++i) {roleControllers[i] = new RoleControl();roleControllers[i].CreateRole(Position.role_land[i], i < 3 ? true : false, i);}leftLandController = new LandControl();leftLandController.CreateLand(Position.left_land);leftLandController.GetLand().land.name = "left_land";rightLandController = new LandControl();rightLandController.CreateLand(Position.right_land);rightLandController.GetLand().land.name = "right_land";foreach (RoleControl roleController in roleControllers){roleController.GetRoleModel().role.transform.localPosition = leftLandController.AddRole(roleController.GetRoleModel());}boatController = new BoatControl();boatController.CreateBoat(Position.left_boat);river = new River(Position.river);moveController = new MoveCtrl();isRunning = true;time = 60;}public void MoveBoat() {if (isRunning == false || actionManager.CheckMoving() == true) return;Vector3 target;if(boatController.GetBoatModel().isRight){target = Position.left_boat;}else{target = Position.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);}// if(roleModel.role.transform.localPosition.y > target.y){// middle_pos = new Vector3(target.x, roleModel.role.transform.localPosition.y, target.z);// }// else{// middle_pos = new Vector3(roleModel.role.transform.localPosition.x, target.y, target.z);// }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);// if(roleModel.role.transform.localPosition.y > target.y){// middle_pos = new Vector3(target.x, roleModel.role.transform.localPosition.y, target.z);// }// else{// middle_pos = new Vector3(roleModel.role.transform.localPosition.x, target.y, target.z);// }middle_pos = new Vector3(target.x, roleModel.role.transform.localPosition.y, target.z);actionManager.MoveRole(roleModel.role, middle_pos, target, speed);}}}public void Check() {}public void JudgeResultCallBack(string result){this.gameObject.GetComponent<UserGUI>().gameMessage = result;this.isRunning = false;}void Awake() {SSDirector.GetInstance().CurrentSceneController = this;LoadResources();this.gameObject.AddComponent<UserGUI>();this.gameObject.AddComponent<CCActionManager>();this.gameObject.AddComponent<JudgeController>();}void Update() {} }
以上完整介绍了牧师与魔鬼动作分离版本的主要变动和新增的函数,这一个版本和初始版本相比,增加了动作管理器,提升了代码的可复用性。
Unity实战之牧师与魔鬼(动作分离版)相关推荐
- unity编程实践-牧师与魔鬼动作分离版
作业要求 牧师与魔鬼 动作分离版 [2019开始的新要求]:设计一个裁判类,当游戏达到结束条件时,通知场景控制器游戏结束 目标:建立动作管理器,使动作抽象出来,可以应用到任何游戏对象上,以此提高代码复 ...
- 基于Unity开发的牧师与魔鬼动作分离版游戏设计
1 作业要求 牧师与魔鬼 动作分离版 设计一个裁判类,当游戏达到结束条件时,通知场景控制器游戏结束 2 实现细节 在原来代码的基础上,修改如下: 将UserGUI的sign成员变量和Controlle ...
- Unity实现牧师与魔鬼动作分离版
牧师与魔鬼动作分离版 项目地址 动作管理器的设计 程序设计框架: 为了用一组简单的动作组合成复杂的动作,我们采用 cocos2d 的方案,建立与 CCAtion 类似的类. 通过门面模式(控制器模式) ...
- 【3D游戏编程与设计】四 游戏对象与图形基础 : 构建游戏场景+牧师与魔鬼 动作分离版
[3D游戏编程与设计]四 游戏对象与图形基础 : 构建游戏场景+牧师与魔鬼 动作分离版 基本操作演练 下载 Fantasy Skybox FREE, 构建自己的游戏场景 下载 Fantasy Skyb ...
- 牧师与魔鬼——动作分离版
牧师与魔鬼--动作分离版 在上周的作业中,牧师与魔鬼游戏中的各个事件,都是写在Director中,并且都是继承Monobehavior的.在这周动作分离的设计中,我将上船.下船以及船的移动都分离出来. ...
- 牧师与魔鬼 动作分离版
1.基本操作演练 下载 Fantasy Skybox FREE 在unityAssetStore中下载Fantasy Skybox FREE 构建游戏场景: GameObject->3D Obj ...
- 牧师与魔鬼-动作分离版
源码传送门 视频展示传送门,展示效果与第三次作业相同 运行说明:将Controllor.cs挂载Main Camera上,然后点击运行即可 1. 动作分离 目的:将物体的动作与空间属性分开来,从而降低 ...
- 牧师与魔鬼动作分离版
本次游戏实现参照课件的框架,将动作管理与游戏场景分离. 完全按照课件的思路实现 动作基类SSAction 简单动作MoveToAction 组合动作SequenceAction 动作管理基类SSAct ...
- Unity3D游戏编程-牧师与恶魔 动作分离版
Unity3D游戏编程-牧师与恶魔 动作分离版 文章目录 Unity3D游戏编程-牧师与恶魔 动作分离版 作业要求 项目配置 项目演示 视频演示 项目下载 文字说明 项目截图 实现过程和方法(算法) ...
最新文章
- 将CVESUMMARY写成HTML文件
- 机器学习--CART分类回归树
- FastDFS集群部署
- 视网膜正常oct图_眼科经验贴:不同情况下的OCT读图
- Vmware迁移datastore注意事项
- 网易2016 实习研发工程师 [编程题]寻找第K大 and leetcode 215. Kth Largest Element in an Array...
- matlab中图像加噪函数imnoise
- 考研日语线上笔记(五):中级日语语法总结20课(11~20)
- 用python做生信_1 python生信入门
- vue实现更换背景图片_Vue实现背景更换颜色操作
- 2022-2028年中国直线电机行业市场现状分析及投资前景评估报告
- 南头中学2021年高考成绩查询,深圳新安中学和南头中学哪个好
- HTML正方体滚动特效
- C++ 的placement new和placement delete
- C# 控制台程序 打开窗体
- 虚拟产品之苹果内购支付/支付宝支付/微信支付的区别
- Nginx目录穿越漏洞
- 【机器学习】信息论基础(联合熵、条件熵、交叉熵、KL散度等)+ Python代码实现
- 使用libreoffice将office文档(word、ppt、excel)转pdf,实现在线预览
- 开发笔记 那些年追过的图片(七):屏幕截图