游戏设计要求:
创建一个地图和若干巡逻兵(使用动画);
每个巡逻兵走一个3~5个边的凸多边型,位置数据是相对地址。即每次确定下一个目标位置,用自己当前位置为原点计算;
巡逻兵碰撞到障碍物,则会自动选下一个点为目标;
巡逻兵在设定范围内感知到玩家,会自动追击玩家;
失去玩家目标后,继续巡逻;
计分:玩家每次甩掉一个巡逻兵计一分,与巡逻兵碰撞游戏结束;程序设计要求:
必须使用订阅与发布模式传消息
工厂模式生产巡逻兵
-
PatrolData
巡逻兵的数据。
public class PatrolData : MonoBehaviour { public bool follow_player = false; //是否跟随玩家 public GameObject player; //玩家游戏对象 public Vector3 start_position; //巡逻兵初始位置 public bool alive = true; //巡逻兵是否存活 }
-
PropFactory
用工厂模式生产巡逻兵,并用数组记录巡逻兵的当前位置。
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>().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); } } }
-
SSActionManager 动作管理类
管理巡逻兵是巡逻状态还是追踪状态。关键代码:
public void SSActionEvent(SSAction source, int intParam = 0, GameObject objectParam = null) { if(intParam == 0) { //侦查兵跟随玩家 PatrolFollowAction follow = PatrolFollowAction.GetSSAction(objectParam.gameObject.GetComponent<PatrolData>().player); this.RunAction(objectParam, follow, this); } else { //侦察兵按照初始位置开始继续巡逻 GoPatrolAction move = GoPatrolAction.GetSSAction(objectParam.gameObject.GetComponent<PatrolData>().start_position); this.RunAction(objectParam, move, this); } }
-
GoPatrolAction
控制巡逻兵的运动逻辑。当发现附近存在玩家时,会调用回调函数,使得巡逻兵改为追踪状态,追踪玩家。
if (data.follow_player) { this.destroy = true; this.callback.SSActionEvent(this,0,this.gameobject); }
-
UserGUI
玩家与键盘的交互逻辑
void Update() { //获取方向键的偏移量 float translationX = Input.GetAxis("Horizontal"); float translationZ = Input.GetAxis("Vertical"); //移动玩家,获取鼠标点击 action.MovePlayer(translationX, translationZ, Input.GetMouseButton(0)); action.RotatePlayer(); }
-
FirstSceneController
当获取到鼠标点击,即flag为true时,将从玩家身体往前发射一条射线,若该射线触碰到巡逻兵,将会将其击倒,播放巡逻兵死亡动画。
public void MovePlayer(float translationX, float translationZ, bool flag) { if(!game_over) { if (flag){ player.GetComponent<Animator>().SetTrigger("shoot"); Ray ray = new Ray(player.transform.position, player.transform.forward); RaycastHit hit; if (Physics.Raycast (ray, out hit)) { //print("hit:"+hit.collider.gameObject.name); if (hit.collider.gameObject.tag == "Patrol"){ hit.collider.gameObject.GetComponent<Animator>().SetTrigger("death"); if(hit.collider.gameObject.GetComponent<PatrolData>().alive == true){ AddScore(); patrol--; } hit.collider.gameObject.GetComponent<PatrolData>().alive = false; } } } if (translationX != 0 || translationZ != 0) { player.GetComponent<Animator>().SetBool("run", true); } else { player.GetComponent<Animator>().SetBool("run", false); } //移动和旋转 player.transform.Translate(0, 0, translationZ * player_speed * Time.deltaTime); player.transform.Rotate(0, translationX * rotate_speed * Time.deltaTime, 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); } } }
-
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(); } } }
-
游戏结束条件的判断
if(action.GetGameover() /*&& action.GetCrystalNumber() != 0*/) { GUI.Label(new Rect(Screen.width / 2 - 50, Screen.width / 2 - 250, 100, 100), "游戏结束", over_style); if (GUI.Button(new Rect(Screen.width / 2 - 50, Screen.width / 2 - 150, 100, 50), "重新开始")) { action.Restart(); return; } } if(action.GetPatrols()==0){ GUI.Label(new Rect(Screen.width / 2 - 50, Screen.width / 2 - 250, 100, 100), "恭喜获胜", over_style); if (GUI.Button(new Rect(Screen.width / 2 - 50, Screen.width / 2 - 150, 100, 50), "重新开始")) { action.Restart(); return; } }
游戏截图: