这次的作业是实现一个巡逻兵自动巡逻追人的游戏,游戏规则是躲避追踪+1分,收集水晶,如果地图上所有的水晶全部收集,那么游戏结束。
游戏效果:
代码和大致解释:
下面定义了在程序之所有使用的借口,ISceneController是SenceController使用的接口定义。IUserAction定义了用户使用的接口。最后两个接口分别是指动作回调和游戏状态设置。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public interface ISceneController
{//加载场景资源void LoadResources();
}public interface IUserAction
{//移动玩家void MovePlayer(float translationX, float translationZ);//得到分数int GetScore();//得到水晶数量int GetCrystalNumber();//得到游戏结束标志bool GetGameover();//重新开始void Restart();
}public interface ISSActionCallback
{void SSActionEvent(SSAction source, int intParam = 0, GameObject objectParam = null);
}public interface IGameStatusOp
{void PlayerEscape();void PlayerGameover();
}
单例模式模版。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class Singleton<T> : MonoBehaviour where T : MonoBehaviour
{protected static T instance;public static T Instance{get{if (instance == null){instance = (T)FindObjectOfType(typeof(T));if (instance == null){Debug.LogError("An instance of " + typeof(T)+ " is needed in the scene, but there is none.");}}return instance;}}
}
游戏的导演,用来控制场记的切换,还有一些基础内容的定义(比如说帧数)。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class SSDirector : System.Object
{private static SSDirector _instance; //导演类的实例public ISceneController CurrentScenceController { get; set; }public static SSDirector GetInstance(){if (_instance == null){_instance = new SSDirector();}return _instance;}
}
PatrolData是Patrol的数据信息。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class PatrolData : MonoBehaviour
{public int sign; //标志巡逻兵在哪一块区域public bool follow_player = false; //是否跟随玩家public int wall_sign = -1; //当前玩家所在区域标志public GameObject player; //玩家游戏对象public Vector3 start_position; //当前巡逻兵初始位置
}
PlayerCollider是玩家的碰撞体, 当和Patrol碰撞后,触发死亡动作。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class PlayerCollider : 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();}}
}
PatrolFactory利用工厂模式产生巡逻兵。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class PropFactory : MonoBehaviour
{private GameObject patrol = null; //巡逻兵private List<GameObject> used = new List<GameObject>(); //正在被使用的巡逻兵private GameObject crystal = null; //水晶private List<GameObject> usedcrystal = new List<GameObject>(); //正在被使用的水晶private float range = 12; //水晶生成的坐标范围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"));ansform.position = vec[i];patrol.GetComponent<PatrolData>().sign = i + 1;patrol.GetComponent<PatrolData>().start_position = vec[i];used.Add(patrol);}return used;}public List<GameObject> GetCrystal(){for (int i = 0; i < 12; i++){crystal = Instantiate(Resources.Load<GameObject>("Prefabs/Crystal"));float ranx = Random.Range(-range, range);float ranz = Random.Range(-range, range);ansform.position = new Vector3(ranx, 0, ranz);usedcrystal.Add(crystal);}return usedcrystal;}public void StopPatrol(){//切换所有侦查兵的动画for (int i = 0; i < used.Count; i++){used[i].gameObject.GetComponent<Animator>().SetBool("run", false);}}
}
AreaCollider是每个巡逻兵可以行走的范围,也就是一个快,其中的wall_sign变量制定了快的标号。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class AreaCollider : MonoBehaviour
{public int sign = 0;FirstSceneController sceneController;private void Start(){sceneController = SSDirector.GetInstance().CurrentScenceController as FirstSceneController;}void OnTriggerEnter(Collider collider){//标记玩家进入自己的区域if (collider.gameObject.tag == "Player"){sceneController.wall_sign = sign;}}
}
PatrolCollider是巡逻兵的碰撞脚本,当player进入的时候,设置follw_player布尔值。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class PatrolCollider : MonoBehaviour
{void OnTriggerEnter(Collider collider){if (collider.gameObject.tag == "Player"){//print("enter");//玩家进入侦察兵追捕范围ansform.parent.GetComponent<PatrolData>().follow_player = true;ansform.parent.GetComponent<PatrolData>().player = collider.gameObject;}}private void OnGUI(){GUIStyle style = new GUIStyle();style.fontSize = al.textColor = d;if(ansform.parent.GetComponent<PatrolData>().follow_player == true){GUI.Label(new Rect(Screen.width / 2 - 150, Screen.height / 2 - 200, 400, 500), "!!You Have Been Found!!", style);}}void OnTriggerExit(Collider collider){if (collider.gameObject.tag == "Player"){ansform.parent.GetComponent<PatrolData>().follow_player = false;ansform.parent.GetComponent<PatrolData>().player = null;}}
}
CrystalCollider是水晶的碰撞对象,当触发器进入,destory掉水晶对象。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class CrystalCollider : MonoBehaviour
{void OnTriggerEnter(Collider collider){if (collider.gameObject.tag == "Player" && this.gameObject.activeSelf){this.gameObject.SetActive(false);//减少水晶数量Singleton<GameEventManager>.Instance.ReduceCrystalNum();}}
}
SSAction是所有动作的基类。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class SSAction : ScriptableObject
{public bool enable = true; //是否正在进行此动作public bool destroy = false; //是否需要被销毁public GameObject gameobject; //动作对象public Transform transform; //动作对象的transformpublic ISSActionCallback callback; //动作完成后的消息通知者protected SSAction() { }//子类可以使用下面这两个函数public virtual void Start(){throw new System.NotImplementedException();}public virtual void Update(){throw new System.NotImplementedException();}
}
GoPatrolAction制定了Patrol四处巡逻的动作。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class GoPatrolAction : SSAction
{private enum Dirction { EAST, NORTH, WEST, SOUTH };private float pos_x, pos_z; //移动前的初始x和z方向坐标private float move_length; //移动的长度private float move_speed = 1.2f; //移动速度private bool move_sign = true; //是否到达目的地private Dirction dirction = Dirction.EAST; //移动的方向private PatrolData data; //侦察兵的数据private GoPatrolAction() { }public static GoPatrolAction GetSSAction(Vector3 location){GoPatrolAction action = CreateInstance<GoPatrolAction>();action.pos_x = location.x;action.pos_z = location.z;//设定移动矩形的边长ve_length = Random.Range(4, 7);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);}//侦察移动Gopatrol();//如果侦察兵需要跟随玩家并且玩家就在侦察兵所在的区域,侦查动作结束if (data.follow_player && data.wall_sign == data.sign){this.destroy = true;this.callback.SSActionEvent(this, 0, this.gameobject);}}public override void Start(){this.gameobject.GetComponent<Animator>().SetBool("run", true);data = this.gameobject.GetComponent<PatrolData>();}void Gopatrol(){if (move_sign){//不需要转向则设定一个目的地,按照矩形移动switch (dirction){case Dirction.EAST:pos_x -= move_length;break;case Dirction.NORTH:pos_z += move_length;break;case Dirction.WEST:pos_x += move_length;break;case Dirction.SOUTH:pos_z -= move_length;break;}move_sign = false;}ansform.LookAt(new Vector3(pos_x, 0, pos_z));float distance = Vector3.Distance(transform.position, new Vector3(pos_x, 0, pos_z));//当前位置与目的地距离浮点数的比较if (distance > 0.9){transform.position = Vector3.ansform.position, new Vector3(pos_x, 0, pos_z), move_speed * Time.deltaTime);}else{dirction = dirction + 1;if (dirction > Dirction.SOUTH){dirction = Dirction.EAST;}move_sign = true;}}
}
PatrolFollowAction定义了巡逻兵追人朝着人走的动作。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;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.ansform.position, ansform.position, speed * Time.deltaTime);ansform.ansform.position);}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;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.ansform.position, ansform.position, speed * Time.deltaTime);ansform.ansform.position);}
}
CCActionManager是动作处理的管理器。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class SSActionManager : MonoBehaviour, ISSActionCallback
{private Dictionary<int, SSAction> actions = new Dictionary<int, SSAction>(); //将执行的动作的字典集合private List<SSAction> waitingAdd = new List<SSAction>(); //等待去执行的动作列表private List<int> waitingDelete = new List<int>(); //等待删除的动作的key protected 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);DestroyObject(ac);}waitingDelete.Clear();}public void RunAction(GameObject gameobject, SSAction action, ISSActionCallback manager){action.gameobject = ansform = ansform;action.callback = manager;waitingAdd.Add(action);action.Start();}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);//玩家逃脱Singleton<GameEventManager>.Instance.PlayerEscape();}}public void DestroyAll(){foreach (KeyValuePair<int, SSAction> kv in actions){SSAction ac = kv.Value;ac.destroy = true;}}
}
PatrolActionManager是Patrol的动作管理器。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class PatrolActionManager : SSActionManager
{private GoPatrolAction go_patrol; //巡逻兵巡逻public void GoPatrol(GameObject patrol){go_patrol = GoPatrolAction.ansform.position);this.RunAction(patrol, go_patrol, this);}//停止所有动作public void DestroyAllAction(){DestroyAll();}
}
ScoreRecorder是游戏的记分器,当逃离追捕的时候➕分。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class ScoreRecorder : MonoBehaviour
{public FirstSceneController sceneController;public int score = 0; //分数public int crystal_number = 12; //水晶数量void AddScore(){score++;}// Use this for initializationvoid Start(){sceneController = (FirstSceneController)SSDirector.GetInstance().der = this;}
}
摄像机跟踪CameraHelpness,之所以不直接把摄像机变成子对象,是因为跟踪摄像机还有一个前摇和后摇的波动过程,比较好看。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class CameraHelpness : MonoBehaviour
{public GameObject follow; //跟随的物体public float smothing = 5f; //相机跟随的速度public Vector3 offset; //相机与物体相对偏移位置public FirstSceneController first = (FirstSceneController)SSDirector.GetInstance().CurrentScenceController;void Start(){offset = transform.position - ansform.position;}void FixedUpdate(){Vector3 target = ansform.position + offset;//摄像机自身位置到目标位置平滑过渡transform.position = Vector3.Lerp(transform.position, target, smothing * Time.deltaTime);}
}
FirstSceneController是游戏的场控,主要负责资源的加载
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class CrystalCollider : MonoBehaviour
{void OnTriggerEnter(Collider collider){if (collider.gameObject.tag == "Player" && this.gameObject.activeSelf){this.gameObject.SetActive(false);//减少水晶数量Singleton<GameEventManager>.Instance.ReduceCrystalNum();}}
}
GameEventManager定义了游戏中玩家逃脱、玩家被捕、减少水晶数量的事件。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class GameEventManager : MonoBehaviour
{//分数变化public delegate void ScoreEvent();public static event ScoreEvent ScoreChange;//游戏结束变化public delegate void GameoverEvent();public static event GameoverEvent GameoverChange;//水晶数量变化public delegate void CrystalEvent();public static event CrystalEvent CrystalChange;//玩家逃脱public void PlayerEscape(){if (ScoreChange != null){ScoreChange();}}//玩家被捕public void PlayerGameover(){if (GameoverChange != null){GameoverChange();}}//减少水晶数量public void ReduceCrystalNum(){if (CrystalChange != null){CrystalChange();}}
}
用户交互界面,用于告诉用户现在的分数,还剩几个水晶,GameStart,GameOver,reBegin按钮显示。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class UserGUI : MonoBehaviour
{private IUserAction action;private GUIStyle score_style = new GUIStyle();private GUIStyle text_style = new GUIStyle();private GUIStyle over_style = new GUIStyle();private int show_time = 8; //展示提示的时间长度public FirstSceneController firstScene;void Start(){firstScene = (FirstSceneController)SSDirector.GetInstance().CurrentScenceController;action = SSDirector.GetInstance().CurrentScenceController as IUserAction;al.textColor = new Color(0, 0, 0, 1);text_style.fontSize = 16;al.textColor = new Color(1, 0.92f, 0.016f, 1);score_style.fontSize = 16;over_style.fontSize = 25;//展示提示StartCoroutine(ShowTip());}void Update(){//获取方向键的偏移量float translationX = Input.GetAxis("Horizontal");float translationZ = Input.GetAxis("Vertical");//移动玩家action.MovePlayer(translationX, translationZ);}private void OnGUI(){GUI.Label(new Rect(10, 5, 200, 50), "分数:", text_style);GUI.Label(new Rect(55, 5, 200, 50), action.GetScore().ToString(), score_style);GUI.Label(new Rect(Screen.width - 170, 5, 50, 50), "剩余水晶数:", text_style);GUI.Label(new Rect(Screen.width - 80, 5, 50, 50), action.GetCrystalNumber().ToString(), score_style);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;}}else if (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 (show_time > 0){GUI.Label(new Rect(Screen.width / 2 - 80, 10, 100, 100), "按WSAD或方向键移动", text_style);GUI.Label(new Rect(Screen.width / 2 - 87, 30, 100, 100), "成功躲避巡逻兵追捕加1分", text_style);GUI.Label(new Rect(Screen.width / 2 - 90, 50, 100, 100), "采集完所有的水晶即可获胜", text_style);}}public IEnumerator ShowTip(){while (show_time >= 0){yield return new WaitForSeconds(1);show_time--;}}
}
最后项目在传送门,希望可以在潘老师的课上学到更多东西。
本文发布于:2024-01-27 18:56:35,感谢您对本站的认可!
本文链接:https://www.4u4v.net/it/17063529952017.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
留言与评论(共有 0 条评论) |