Unity实战之牧师与魔鬼(动作分离版)

项目链接

整体描述

本次项目在第一版牧师与魔鬼的基础上,将动作从场记中分离出来,并设计一个裁判类实时监测游戏进行的情况。这样改进的优点有很多:

  • 降低了不同功能之间的耦合性,代码的复用性更好。
  • 通过门面模式的设计,程序更加容易进行维护。

设计思路

本次改进参考了上课时介绍的cocos2d方案,它的UML图如下:

接下来将逐个介绍其中的类

动作管理(Action)

  • SSAction 动作基类。

    定义了两个虚函数 StartUpdate,后续的动作类都继承动作基类并实现这两个虚函数。

    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 裁判类(新增)

    1. 裁判类的作用是判断游戏进行的状态并通知主控制器,简单来说就是将原来 FirstController 中的 Check 抽离出来用一个类单独实现。需要在每一帧判断当前游戏的输赢,因此需要输入相关的变量进行判断。

    2. Update 中实现判断的逻辑,以达到实施监测的效果。

    3. 这里有一个问题,我们需要利用到判断的结果在主控制器中进行相应的处理(打印信息等等),但 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实战之牧师与魔鬼(动作分离版)相关推荐

  1. unity编程实践-牧师与魔鬼动作分离版

    作业要求 牧师与魔鬼 动作分离版 [2019开始的新要求]:设计一个裁判类,当游戏达到结束条件时,通知场景控制器游戏结束 目标:建立动作管理器,使动作抽象出来,可以应用到任何游戏对象上,以此提高代码复 ...

  2. 基于Unity开发的牧师与魔鬼动作分离版游戏设计

    1 作业要求 牧师与魔鬼 动作分离版 设计一个裁判类,当游戏达到结束条件时,通知场景控制器游戏结束 2 实现细节 在原来代码的基础上,修改如下: 将UserGUI的sign成员变量和Controlle ...

  3. Unity实现牧师与魔鬼动作分离版

    牧师与魔鬼动作分离版 项目地址 动作管理器的设计 程序设计框架: 为了用一组简单的动作组合成复杂的动作,我们采用 cocos2d 的方案,建立与 CCAtion 类似的类. 通过门面模式(控制器模式) ...

  4. 【3D游戏编程与设计】四 游戏对象与图形基础 : 构建游戏场景+牧师与魔鬼 动作分离版

    [3D游戏编程与设计]四 游戏对象与图形基础 : 构建游戏场景+牧师与魔鬼 动作分离版 基本操作演练 下载 Fantasy Skybox FREE, 构建自己的游戏场景 下载 Fantasy Skyb ...

  5. 牧师与魔鬼——动作分离版

    牧师与魔鬼--动作分离版 在上周的作业中,牧师与魔鬼游戏中的各个事件,都是写在Director中,并且都是继承Monobehavior的.在这周动作分离的设计中,我将上船.下船以及船的移动都分离出来. ...

  6. 牧师与魔鬼 动作分离版

    1.基本操作演练 下载 Fantasy Skybox FREE 在unityAssetStore中下载Fantasy Skybox FREE 构建游戏场景: GameObject->3D Obj ...

  7. 牧师与魔鬼-动作分离版

    源码传送门 视频展示传送门,展示效果与第三次作业相同 运行说明:将Controllor.cs挂载Main Camera上,然后点击运行即可 1. 动作分离 目的:将物体的动作与空间属性分开来,从而降低 ...

  8. 牧师与魔鬼动作分离版

    本次游戏实现参照课件的框架,将动作管理与游戏场景分离. 完全按照课件的思路实现 动作基类SSAction 简单动作MoveToAction 组合动作SequenceAction 动作管理基类SSAct ...

  9. Unity3D游戏编程-牧师与恶魔 动作分离版

    Unity3D游戏编程-牧师与恶魔 动作分离版 文章目录 Unity3D游戏编程-牧师与恶魔 动作分离版 作业要求 项目配置 项目演示 视频演示 项目下载 文字说明 项目截图 实现过程和方法(算法) ...

最新文章

  1. 将CVESUMMARY写成HTML文件
  2. 机器学习--CART分类回归树
  3. FastDFS集群部署
  4. 视网膜正常oct图_眼科经验贴:不同情况下的OCT读图
  5. Vmware迁移datastore注意事项
  6. 网易2016 实习研发工程师 [编程题]寻找第K大 and leetcode 215. Kth Largest Element in an Array...
  7. matlab中图像加噪函数imnoise
  8. 考研日语线上笔记(五):中级日语语法总结20课(11~20)
  9. 用python做生信_1 python生信入门
  10. vue实现更换背景图片_Vue实现背景更换颜色操作
  11. 2022-2028年中国直线电机行业市场现状分析及投资前景评估报告
  12. 南头中学2021年高考成绩查询,深圳新安中学和南头中学哪个好
  13. HTML正方体滚动特效
  14. C++ 的placement new和placement delete
  15. C# 控制台程序 打开窗体
  16. 虚拟产品之苹果内购支付/支付宝支付/微信支付的区别
  17. Nginx目录穿越漏洞
  18. 【机器学习】信息论基础(联合熵、条件熵、交叉熵、KL散度等)+ Python代码实现
  19. 使用libreoffice将office文档(word、ppt、excel)转pdf,实现在线预览
  20. 开发笔记 那些年追过的图片(七):屏幕截图

热门文章

  1. 攻防世界逆向高手题之dmd-50
  2. RS485通信和Modbus协议
  3. webStrom 2018 激活破解(最新)
  4. PhotoSwipe-一个好用的图片放大缩小插件
  5. 比肩某应的顶级AI电销机器人无限部署
  6. 基于华为云人脸服务接口设计的人脸考勤打卡签到系统
  7. USACO美国信息学奥赛竞赛12月份开赛,中国学生备赛指南
  8. Delphi历史版本介绍(一)从Delphi1到Delphi7
  9. 2021年河南高考--各高校在河南录取分数线预测(本科一批——理科):
  10. “黄金薄膜” CPI膜 —— 折叠屏手机核心材料