游戏设计要求:
创建一个地图和若干巡逻兵(使用动画);每个巡逻兵走一个3~5个边的凸多边型,位置数据是相对地址。即每次确定下一个目标位置,用自己当前位置为原点计算;巡逻兵碰撞到障碍物,则会自动选下一个点为目标;巡逻兵在设定范围内感知到玩家,会自动追击玩家;失去玩家目标后,继续巡逻;计分:玩家每次甩掉一个巡逻兵计一分,与巡逻兵碰撞游戏结束;程序设计要求:
必须使用订阅与发布模式传消息 subject:OnLostGoalPublisher: ?Subscriber: ?工厂模式生产巡逻兵 友善提示1:生成 3~5个边的凸多边型 随机生成矩形在矩形每个边上随机找点,可得到 3 - 4 的凸多边型5 ?友善提示2:参考以前博客,给出自己新玩法参考之前博客的UML类图,如下:
接下来我们看订阅和发布模式。订阅和发布模式是指发布者,即消息的发送者不会将消息直接发送给订阅者,即消息的接受者。 发布者和订阅者不知道彼此的存在。具体的订阅和发布模式的模型为:发布者publisher为时间或消息的拥有者,主题subject表示消息的发布媒体,接收器handle表示处理消息的方法,订阅者subscriber表示对主题感兴趣的人。
订阅与发布模式:
然后我们看代码的具体实现。
首先,我们需要实现一个GameEventManager,从而实现对游戏事件消息的传递。若玩家成功逃脱一次,则分数加一;若玩家被逮到,则游戏结束,具体如下:
public class GameEventManager : MonoBehaviour { //分数变化 public delegate void ScoreEvent(); public static event ScoreEvent ScoreChange; //游戏结束变化 public delegate void GameoverEvent(); public static event GameoverEvent GameoverChange; //玩家逃脱 public void PlayerEscape() { if (ScoreChange != null) { ScoreChange(); } } //玩家被捕 public void PlayerGameover() { if (GameoverChange != null) { GameoverChange(); } } }然后,用OnEnable和OnDisable表示事件的开始和结束。
void OnEnable(){ GameEventManager.addScore += ScoreChange; GameEventManager.GameOver += GameoverChange; } void OnDisable(){ GameEventManager.addScore -= ScoreChange; GameEventManager.GameOver -= GameoverChange; }接下来我们来看玩家预设,如下:
对于玩家最重要的就是都对玩家的控制。其中首先需要实现的是玩家的移动和旋转。具体如下:
public void PlayerMove(float pos_X, float pos_Z){ if(!game_over && player != null){ if (pos_X != 0 || pos_Z != 0){ player.GetComponent<Animator>().SetBool("run", true); } else{ player.GetComponent<Animator>().SetBool("run", false); } player.transform.Translate(0, 0, pos_Z * 5f * Time.deltaTime); player.transform.Rotate(0, pos_X*1.5f, 0); player.transform.Translate(pos_X * 5f * Time.deltaTime, 0, 0); if (player.transform.localEulerAngles.x != 0 || player.transform.localEulerAngles.z != 0){ player.transform.localEulerAngles = new Vector3(0, player.transform.localEulerAngles.y, 0); } if (player.transform.position.y != 0){ player.transform.position = new Vector3(player.transform.position.x, 0, player.transform.position.z); } } }然后是检测玩家与巡逻兵碰撞的PlayCollide,来触发游戏结束的相应条件,具体如下:
public class PlayerCollide : MonoBehaviour { void OnCollisionEnter(Collision other) { //当玩家与侦察兵相撞 if (other.gameObject.tag == "Player") { other.gameObject.GetComponent<Animator>().SetTrigger("death"); this.GetComponent<Animator>().SetTrigger("shoot"); Singleton<GameEventManager>.Instance.PlayerGameover(); } } }接下来我们看巡逻兵的预设。同样需要添加一个刚体,并需要添加一个碰撞器来检测与玩家的碰撞。
我们通过工厂来产生巡逻兵,通过工厂模式实现巡逻兵的初始位置、状态等的设定:
public class PropFactory : MonoBehaviour { private GameObject patrol = null; //巡逻兵 private List<GameObject> used = new List<GameObject>(); //正在被使用的巡逻兵 private Vector3[] vec = new Vector3[9]; //保存每个巡逻兵的初始位置 public FirstSceneController sceneControler; //场景控制器 public List<GameObject> GetPatrols() { int[] pos_x = { -6, 4, 13 }; int[] pos_z = { -4, 6, -13 }; int index = 0; //生成不同的巡逻兵初始位置 for(int i=0;i < 3;i++) { for(int j=0;j < 3;j++) { vec[index] = new Vector3(pos_x[i], 0, pos_z[j]); index++; } } for(int i=0; i < 9; i++) { patrol = Instantiate(Resources.Load<GameObject>("Prefabs/Patrol")); patrol.transform.position = vec[i]; patrol.GetComponent<PatrolData>().sign = i + 1; patrol.GetComponent<PatrolData>().start_position = vec[i]; used.Add(patrol); } return used; } public void StopPatrol() { //切换所有侦查兵的动画 for (int i = 0; i < used.Count; i++) { used[i].gameObject.GetComponent<Animator>().SetBool("run", false); } } }然后是巡逻兵的追随玩家的动作的实现:
public class PatrolFollowAction : SSAction { private float speed = 2f; //跟随玩家的速度 private GameObject player; //玩家 private PatrolData data; //侦查兵数据 private PatrolFollowAction() { } public static PatrolFollowAction GetSSAction(GameObject player) { PatrolFollowAction action = CreateInstance<PatrolFollowAction>(); action.player = player; return action; } public override void Update() { if (transform.localEulerAngles.x != 0 || transform.localEulerAngles.z != 0) { transform.localEulerAngles = new Vector3(0, transform.localEulerAngles.y, 0); } if (transform.position.y != 0) { transform.position = new Vector3(transform.position.x, 0, transform.position.z); } Follow(); //如果侦察兵没有跟随对象,或者需要跟随的玩家不在侦查兵的区域内 if (!data.follow_player || data.wall_sign != data.sign) { this.destroy = true; this.callback.SSActionEvent(this,1,this.gameobject); } } public override void Start() { data = this.gameobject.GetComponent<PatrolData>(); } void Follow() { transform.position = Vector3.MoveTowards(this.transform.position, player.transform.position, speed * Time.deltaTime); this.transform.LookAt(player.transform.position); } }最后是巡逻兵的碰撞检测:
public class PatrolCollide : MonoBehaviour { void OnTriggerEnter(Collider collider) { if (collider.gameObject.tag == "Player") { //玩家进入侦察兵追捕范围 this.gameObject.transform.parent.GetComponent<PatrolData>().follow_player = true; this.gameObject.transform.parent.GetComponent<PatrolData>().player = collider.gameObject; } } void OnTriggerExit(Collider collider) { if (collider.gameObject.tag == "Player") { this.gameObject.transform.parent.GetComponent<PatrolData>().follow_player = false; this.gameObject.transform.parent.GetComponent<PatrolData>().player = null; } } }游戏运行界面如下:
