Unity3D小游戏——牧师与魔鬼(动作分离版)
本篇博客是对https://www.cnblogs.com/LC32/p/15420714.html代码的进一步改进。
1.增加裁判
首先创建一个JudgeController作为裁判类,并且要求裁判需要通过导演才能得到对应的场景控制器。把原来写在FirstController中的GetAndSetGameState函数迁移到裁判类中,形成了裁判类的UpdateGameState方法。原来的场景控制器FirstController需要在Awake和初始化函数中初始化一个裁判类。将原来调用GetAndSetGameState函数的地方修改成请求裁判类裁决。修改后裁判类的代码如下
public class JudgeController{ FirstController firstCtrl; public JudgeController(){ firstCtrl = SSDirector.GetInstance().CurrentSceneController as FirstController; } //判断游戏状态 public int UpdadeGameState(){ if(firstCtrl.gameState != FirstController.PLAYING) return firstCtrl.gameState; //判断是否失败 int[,] rolePos = new int[2, 3]{{0, 0, 0}, {0, 0, 0}}; foreach(RoleController r in firstCtrl.RoleCtrl){ rolePos[r.roleType, r.roleState]++; } 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])){ return FirstController.FAILED; } //判断是否成功 foreach(RoleController r in firstCtrl.RoleCtrl){ if(r.roleType == 0 && r.roleState != FirstController.RIGHTLAND){ return FirstController.PLAYING; } } return FirstController.WIN; } }
2.动作分离
在上一篇文章中,游戏对象的移动由MoveController和Move共同管理,这样做的坏处是在FirstController中仍然保留有一小部分的用于管理对象运动的代码。在动作分离版的代码中,管理动作的代码被分解成以下三部分,CCActionManager用于管理所有动作,CCMoveAction用于管理“移动”这种动作,特殊的是在这个游戏中只有移动这个动作。Move则是移动这个动作的实体。
在CCActionManager中主要实现了MoveRole()和MoveBoat()两个函数。通过调用CCMoveAction中的MoveTo和MoveSeqcenceTo来实现两种不同形式的移动效果。
public class CCActionManager { public CCMoveAction moveBoatAction; public CCMoveAction moveRoleAction; public FirstController controller; public CCActionManager(){ controller = SSDirector.GetInstance().CurrentSceneController as FirstController; controller.actionManager = this; moveBoatAction = new CCMoveAction(); moveRoleAction = new CCMoveAction(); } public bool IsMoving(){ return moveRoleAction.IsMoving() || moveBoatAction.IsMoving(); } public void MoveRole(BoatController BoatCtrl, RoleController RoleCtrl, int destination, int seat){ Vector3 finalPos; if(destination == FirstController.RIGHTLAND){ finalPos = Position.roleRightPos[seat]; } else if(destination == FirstController.LEFTLAND){ finalPos = Position.roleLeftPos[seat]; } else{ if(BoatCtrl.onLeftside){ finalPos = Position.seatLeftPos[seat]; } else{ finalPos = Position.seatRightPos[seat]; } } moveRoleAction.MoveSequenceTo(RoleCtrl.GetModelGameObject(), finalPos); } public void MoveBoat(BoatController BoatCtrl, int destination){ if(destination == FirstController.RIGHTLAND){ moveBoatAction.MoveTo(BoatCtrl.GetModelGameObject(), Position.boatRightPos); for(int i = 0; i < 3; i++){ if(BoatCtrl.seat[i] != -1){ RoleController r = controller.RoleCtrl[controller.IDToNumber(BoatCtrl.seat[i])]; moveRoleAction.MoveTo(r.GetModelGameObject(), Position.seatRightPos[i]); } } } else{ moveBoatAction.MoveTo(BoatCtrl.GetModelGameObject(), Position.boatLeftPos); for(int i = 0; i < 3; i++){ if(BoatCtrl.seat[i] != -1){ RoleController r = controller.RoleCtrl[controller.IDToNumber(BoatCtrl.seat[i])]; moveRoleAction.MoveTo(r.GetModelGameObject(), Position.seatLeftPos[i]); } } } } }
而CCMoveAction最主要做的事情是给特定的gameobject添加Move脚本并且设置脚本的属性
public class CCMoveAction { GameObject moveObject; public bool IsMoving(){ return(this.moveObject != null && this.moveObject.GetComponent<Move>().isMoving == true); } public void MoveTo(GameObject moveObject, Vector3 destination){ Move test; this.moveObject = moveObject; if (!moveObject.TryGetComponent<Move>(out test)) { moveObject.AddComponent<Move>(); } this.moveObject.GetComponent<Move>().moveAction = this; this.moveObject.GetComponent<Move>().destination = destination; this.moveObject.GetComponent<Move>().moveMode = Move.single; } public void MoveSequenceTo(GameObject moveObject, Vector3 destination){ Move test; this.moveObject = moveObject; if (!moveObject.TryGetComponent<Move>(out test)) { moveObject.AddComponent<Move>(); } this.moveObject.GetComponent<Move>().moveAction = this; this.moveObject.GetComponent<Move>().destination = destination; this.moveObject.GetComponent<Move>().moveMode = Move.sequence; } }
Move相比上一版的改动主要在实现了多段移动。主要是拿一个数组存起所有移动阶段的目的地。
using System.Collections; using System.Collections.Generic; using UnityEngine; public class Move : MonoBehaviour { public static int single = 0; public static int sequence = 1; public bool isMoving; bool initialized; public int moveMode; public bool doneMoving; public float speed = 5; int n_seq; public Vector3[] desseq; public Vector3 destination; public CCMoveAction moveAction; public Move(){ n_seq = 0; isMoving = false; initialized = false; moveMode = -1; } void Update() { if(moveMode == -1) return; if(!initialized){ if(moveMode == single){ desseq = new Vector3[1]; desseq[0] = destination; } else if(moveMode == sequence){ desseq = new Vector3[3]; desseq[0] = transform.localPosition + new Vector3(0, 1, 0); desseq[1] = destination + new Vector3(0, 1, 0); desseq[2] = destination; } else{ Debug.Log("ERROR!"); } initialized = true; } isMoving = true; if(n_seq >= desseq.Length){ n_seq = 0; moveMode = -1; initialized = false; isMoving = false; return; } if(transform.localPosition == desseq[n_seq]){ n_seq += 1; return; } transform.localPosition = Vector3.MoveTowards(transform.localPosition, desseq[n_seq], speed * Time.deltaTime); } }
演示视频:https://www.bilibili.com/video/BV17v41137s1?spm_id_from=333.999.0.0
代码:https://gitee.com/GallonC/unityhomework/tree/master/homework3/Assets
运行方式:与上一版本完全相同,将链接中的Asset文件替换为Unity中一个新3D项目的Asset项目。将View中的UserGUI挂到主相机上,将FirstController挂到一个新创建的GameObject上。点击运行即可