设计要求
- 游戏设计要求
- 创建一个地图和若干巡逻兵(使用动画)
- 每个巡逻兵走一个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);
else
player.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();
}
}
}
}