Patrol

设计要求

  • 游戏设计要求
    • 创建一个地图和若干巡逻兵(使用动画)
    • 每个巡逻兵走一个3~5个边的凸多边型,位置数据是相对地址。即每次确定下一个目标位置,用自己当前位置为原点计算
    • 巡逻兵碰撞到障碍物,则会自动选下一个点为目标
    • 巡逻兵在设定范围内感知到玩家,会自动追击玩家
    • 失去玩家目标后,继续巡逻
    • 计分:玩家每次甩掉一个巡逻兵计一分,与巡逻兵碰撞游戏结束
  • 程序设计要求
    • 必须使用订阅与发布模式传消息
    • 工厂模式生产巡逻兵
  • 游戏规则
    • 使用方向键控制人物移动,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();
            }
        }
    }
}

效果

在这里插入图片描述
视频链接

参考博客

Unity3d学习之路-简单巡逻兵

猜你喜欢

转载自blog.csdn.net/lydsera/article/details/128361435