设计要求
- 游戏设计要求
- 创建一个地图和若干巡逻兵(使用动画)
- 每个巡逻兵走一个3~5个边的凸多边型,位置数据是相对地址。即每次确定下一个目标位置,用自己当前位置为原点计算
- 巡逻兵碰撞到障碍物,则会自动选下一个点为目标
- 巡逻兵在设定范围内感知到玩家,会自动追击玩家
- 失去玩家目标后,继续巡逻
- 计分:玩家每次甩掉一个巡逻兵计一分,与巡逻兵碰撞游戏结束
- 程序设计要求
- 必须使用订阅与发布模式传消息
- 工厂模式生产巡逻兵
- 游戏规则
- 使用方向键控制人物移动,60秒内
不被抓到即胜利
- 使用方向键控制人物移动,60秒内
预制
地图
九宫格地图,每个格子为一个巡逻兵的巡逻区域,每个区域挂在一个MyRegion脚本
public int num;//编号FirstSceneController sceneController;//场记 private void OnTriggerEnter(Collider other) {sceneController = SSDirector.GetInstance().CurrentSceneController as FirstSceneController;if (other.gameObject.tag == "Player") {sceneController.playerRegion = num;} }private void OnTriggerExit(Collider other) {if (other.gameObject.tag == "Patrol") {other.gameObject.GetComponent<Patrol>().isCollided = true;}}
此处用tag区分玩家和巡逻兵
Patrol
巡逻兵和玩家预制都从Asset Store获取,为巡逻兵的加上一个朝前的capsule collider,相当于他的视线范围,当玩家进入视线范围,collider发生碰撞,巡逻兵检测到玩家存在进而追捕
Animator Controller
巡逻兵的动画控制:
其中有参数bool类型的shoot和pause,触发器death,pause为true则停止(对应m_weapon_idle_A),否则run;当巡逻兵抓到玩家,shoot触发器控制巡逻兵射击
玩家的动画控制:
由bool类型的run,pause和触发器death控制
代码实现
巡逻兵
巡逻兵数据
public class Patrol : MonoBehaviour
{public int patrolRegion;//巡逻兵所在区域public bool isFollowing;//是否追捕玩家public GameObject player;//玩家 public bool isPlayerInRange;//玩家是否在侦擦范围 public bool isCollided;//是否碰撞 public int playerRegion;//玩家所在区域
}
工厂模式创建巡逻兵
public class PatrolFactory : MonoBehaviour
{public GameObject patrol = null;private List<Patrol> used = new List<Patrol>();public List<GameObject> GetPatrols() {List<GameObject> patrols = new List<GameObject>();float[] pos_x = { -4.5f, 1.5f, 7.5f };float[] pos_z = { 7.5f, 1.5f, -4.5f };//生成巡逻兵for (int i = 0; i < 3; i++) {for (int j = 0; j < 3; j++) {patrol = Instantiate(Resources.Load<GameObject>("Prefabs/Patrol"));patrol.transform.position = new Vector3(pos_x[j], 0, pos_z[i]);patrol.GetComponent<Patrol>().patrolRegion = i * 3 + j + 1;patrol.GetComponent<Patrol>().playerRegion = 4;patrol.GetComponent<Patrol>().isPlayerInRange = false;patrol.GetComponent<Patrol>().isFollowing = false;patrol.GetComponent<Patrol>().isCollided = false;patrol.GetComponent<Animator>().SetBool("pause", true);used.Add(patrol.GetComponent<Patrol>());patrols.Add(patrol);}}return patrols;}//暂停巡逻兵public void PausePatrol() {for (int i = 0; i < used.Count; i++) {used[i].gameObject.GetComponent<Animator>().SetBool("pause", true);}}//开始public void StartPatrol() {for (int i = 0; i < used.Count; i++) {used[i].gameObject.GetComponent<Animator>().SetBool("pause", false);}}
}
巡逻兵动作
public class PatrolAction : Action
{private float pos_x, pos_z;private bool turn = true;//是否转向private Patrol data;public static PatrolAction GetAction(Vector3 location) {PatrolAction action = CreateInstance<PatrolAction>();action.pos_x = location.x;action.pos_z = location.z;return action;}public override void Start() {data = this.gameObject.GetComponent<Patrol>();}public override void Update() {if (SSDirector.GetInstance().CurrentSceneController.getGameState().Equals(GameState.RUNNING)) {Patrol();if (!data.isFollowing && data.isPlayerInRange && data.patrolRegion == data.playerRegion && !data.isCollided) {this.destroy = true;this.enable = false;this.callback.ActionEvent(this);this.gameObject.GetComponent<Patrol>().isFollowing = true;Singleton<GameEventManager>.Instance.FollowPlayer(this.gameObject);}}}//巡逻void Patrol() {if (turn) {pos_x = this.transform.position.x + Random.Range(-5f, 5f);pos_z = this.transform.position.z + Random.Range(-5f, 5f);this.transform.LookAt(new Vector3(pos_x, 0, pos_z));this.gameObject.GetComponent<Patrol>().isCollided = false;turn = false;}float distance = Vector3.Distance(transform.position, new Vector3(pos_x, 0, pos_z));if (this.gameObject.GetComponent<Patrol>().isCollided) {this.transform.Rotate(Vector3.up, 180);GameObject temp = new GameObject();temp.transform.position = this.transform.position;temp.transform.rotation = this.transform.rotation;temp.transform.Translate(0, 0, Random.Range(0.5f, 3f));pos_x = temp.transform.position.x;pos_z = temp.transform.position.z;this.transform.LookAt(new Vector3(pos_x, 0, pos_z));this.gameObject.GetComponent<Patrol>().isCollided = false;Destroy(temp);} else if (distance <= 0.1) {turn = true;} else {this.transform.Translate(0, 0, Time.deltaTime);}}
}
public class PatrolFollowAction : Action
{private float speed = 1.5f; private GameObject player;private Patrol data; public static PatrolFollowAction GetAction(GameObject player) {PatrolFollowAction action = CreateInstance<PatrolFollowAction>();action.player = player;return action;}public override void Start() {data = this.gameObject.GetComponent<Patrol>();}public override void Update() {if (SSDirector.GetInstance().CurrentSceneController.getGameState().Equals(GameState.RUNNING)) {//追捕玩家transform.position = Vector3.MoveTowards(this.transform.position, player.transform.position, speed * Time.deltaTime);this.transform.LookAt(player.transform.position);if (data.isFollowing && (!(data.isPlayerInRange && data.patrolRegion == data.playerRegion) || data.isCollided)) {this.destroy = true;this.enable = false;this.callback.ActionEvent(this);this.gameObject.GetComponent<Patrol>().isFollowing = false;Singleton<GameEventManager>.Instance.PlayerEscape(this.gameObject);}}}
}
通过ActionManager控制动作开始结束
public class PatrolActionManager : ActionManager, ActionCallback
{public PatrolAction patrol;public PatrolFollowAction follow;//巡逻public void Patrol(GameObject ptrl) {this.patrol = PatrolAction.GetAction(ptrl.transform.position);this.RunAction(ptrl, patrol, this);}//追捕public void Follow(GameObject player, GameObject patrol) {this.follow = PatrolFollowAction.GetAction(player);this.RunAction(patrol, follow, this);}//停止public void DestroyAllActions() {DestroyAll();}public void ActionEvent(Action source, ActionEventType events = ActionEventType.Completed, int intParam = 0, string strParam = null, object objectParam = null){ }
}
巡逻兵捕获玩家
public class PatrolCollide : MonoBehaviour
{void OnCollisionEnter(Collision collision) {if (collision.gameObject.tag == "Player") {// 当玩家与巡逻兵相撞this.GetComponent<Animator>().SetTrigger("shoot");Singleton<GameEventManager>.Instance.OnPlayerCatched();} else {// 当巡逻兵碰到其他障碍物this.GetComponent<Patrol>().isCollided = true;}}
}
玩家
玩家移动
public void MovePlayer(float translationX, float translationZ) {if (translationX != 0 || translationZ != 0) {player.GetComponent<Animator>().SetBool("run", true);} else {player.GetComponent<Animator>().SetBool("run", false);}translationX *= Time.deltaTime;translationZ *= Time.deltaTime;player.transform.LookAt(new Vector3(player.transform.position.x + translationX, player.transform.position.y, player.transform.position.z + translationZ));if (translationX == 0)player.transform.Translate(0, 0, Mathf.Abs(translationZ) * 2);else if (translationZ == 0)player.transform.Translate(0, 0, Mathf.Abs(translationX) * 2);elseplayer.transform.Translate(0, 0, Mathf.Abs(translationZ) + Mathf.Abs(translationX));}
区域
public class MyRegion : MonoBehaviour
{public int num;//编号FirstSceneController sceneController;//场记 //标记玩家进入private void OnTriggerEnter(Collider other) {sceneController = SSDirector.GetInstance().CurrentSceneController as FirstSceneController;if (other.gameObject.tag == "Player") {sceneController.playerRegion = num;} }//巡逻兵在自己的区域private void OnTriggerExit(Collider other) {if (other.gameObject.tag == "Patrol") {other.gameObject.GetComponent<Patrol>().isCollided = true;}}}
订阅与发布模式
public class GameEventManager : MonoBehaviour
{//玩家逃离public delegate void EscapeEvent(GameObject patrol);public static event EscapeEvent OnGoalLost;//巡逻兵追踪public delegate void FollowEvent(GameObject patrol);public static event FollowEvent OnFollowing;public delegate void GameOverEvent();public static event GameOverEvent GameOver;public delegate void WinEvent();public static event WinEvent Win;public void PlayerEscape(GameObject patrol) {if (OnGoalLost != null) {OnGoalLost(patrol);}}public void FollowPlayer(GameObject patrol) {if (OnFollowing != null) {OnFollowing(patrol);}}public void OnPlayerCatched() {if (GameOver != null) {GameOver();}}public void TimeIsUP() {if (Win != null) {Win();} }
}
订阅者
void OnEnable() {// 订阅游戏事件GameEventManager.OnGoalLost += OnGoalLost;GameEventManager.OnFollowing += OnFollowing;GameEventManager.GameOver += GameOver;GameEventManager.Win += Win;}void OnDisable() {GameEventManager.OnGoalLost -= OnGoalLost;GameEventManager.OnFollowing -= OnFollowing;GameEventManager.GameOver -= GameOver;GameEventManager.Win -= Win;}public void OnGoalLost(GameObject patrol) {patrolActionManager.Patrol(patrol);scoreRecorder.Record();}public void OnFollowing(GameObject patrol) {patrolActionManager.Follow(player, patrol);}public void GameOver() {gameState = GameState.LOSE;StopAllCoroutines();patrolFactory.PausePatrol();player.GetComponent<Animator>().SetTrigger("death");patrolActionManager.DestroyAllActions();}public void Win() {gameState = GameState.WIN;StopAllCoroutines();patrolFactory.PausePatrol();}
镜头跟随
public class CameraFollowAction : MonoBehaviour
{public GameObject player; //相机跟随的物体public float smothing = 10f; //相机跟随的平滑速度Vector3 offset; //相机与物体相对偏移位置void Start() {offset = new Vector3(0, 5, -5);}void FixedUpdate() {// 设置摄像机目标位置Vector3 target = player.transform.position + offset;//摄像机自身位置到目标位置平滑过渡transform.position = Vector3.Lerp(transform.position, target, smothing * Time.deltaTime);}
}
GUI
public class UserGUI : MonoBehaviour
{private UserAction action;private SceneController controller;GUIStyle scoreStyle;GUIStyle buttonStyle;GUIStyle countDownStyle;GUIStyle finishStyle;void Start() {scoreStyle = new GUIStyle();scoreStyle.fontSize = 40;scoreStyle.normal.textColor = Color.white;buttonStyle = new GUIStyle("button");buttonStyle.fontSize = 15;buttonStyle.normal.textColor = Color.white;countDownStyle = new GUIStyle();countDownStyle.fontSize = 25;countDownStyle.normal.textColor = Color.white;finishStyle = new GUIStyle();finishStyle.fontSize = 40;finishStyle.normal.textColor = Color.white;}private void Update() {action = SSDirector.GetInstance().CurrentSceneController as UserAction;controller = SSDirector.GetInstance().CurrentSceneController as SceneController;if (controller.getGameState().Equals(GameState.RUNNING)) {// 获取键盘输入float translationX = Input.GetAxis("Horizontal");float translationZ = Input.GetAxis("Vertical");//移动玩家action.MovePlayer(translationX, translationZ);}}private void OnGUI() {controller = SSDirector.GetInstance().CurrentSceneController as SceneController;string buttonText = "";if (controller.getGameState().Equals(GameState.START) || controller.getGameState().Equals(GameState.PAUSE)) {buttonText = "Start";}if (controller.getGameState().Equals(GameState.LOSE)) {buttonText = "Restart";GUI.Label(new Rect(Screen.width / 2 - 110, Screen.height / 2 - 100, 200, 50), "Game Over!", finishStyle);}if (controller.getGameState().Equals(GameState.WIN)) {buttonText = "Restart";GUI.Label(new Rect(Screen.width / 2 - 80, Screen.height / 2 , 200, 50), "You Win!", finishStyle);}if (controller.getGameState().Equals(GameState.RUNNING)) {buttonText = "Pause";}GUI.Label(new Rect(Screen.width / 2 - 80 , Screen.height / 2 - 175, 100, 50),"Score: " + controller.GetScore().ToString(), scoreStyle);GUI.Label(new Rect(Screen.width / 2 - 50, Screen.height / 2 - 225, 100, 50),"Time: " + SSDirector.GetInstance().leftSeconds.ToString(), countDownStyle);if (GUI.Button(new Rect(Screen.width / 2 - 50, Screen.height / 2 + 100, 100, 50), buttonText, buttonStyle)) {// 按下按钮控制游戏状态if (buttonText == "Pause") {controller.Pause();} else if (buttonText == "Start") {controller.Begin();} else if (buttonText == "Restart") {controller.Restart();}}}
}
效果
视频链接
参考博客
Unity3d学习之路-简单巡逻兵