七、智能巡逻兵

1.智能巡逻兵

  • 提交要求:
  • 游戏设计要求:
    • 创建一个地图和若干巡逻兵(使用动画);
    • 每个巡逻兵走一个3~5个边的凸多边型,位置数据是相对地址。即每次确定下一个目标位置,用自己当前位置为原点计算;
    • 巡逻兵碰撞到障碍物,则会自动选下一个点为目标;
    • 巡逻兵在设定范围内感知到玩家,会自动追击玩家;
    • 失去玩家目标后,继续巡逻;
    • 计分:玩家每次甩掉一个巡逻兵计一分,与巡逻兵碰撞游戏结束;
  • 程序设计要求:
    • 必须使用订阅与发布模式传消息
      • subject:OnLostGoal
      • Publisher: ?
      • Subscriber: ?
    • 工厂模式生产巡逻兵
  • 友善提示1:生成 3~5个边的凸多边型
    • 随机生成矩形
    • 在矩形每个边上随机找点,可得到 3 - 4 的凸多边型
    • 5 ?
  • 友善提示2:参考以前博客,给出自己新玩法

2.场景构建

地面Plane

  • 新建对象(地面)
  • 新建对象(Cube)围墙
    在这里插入图片描述
  • 构建用于区分9个区域的障碍物,栅栏预制是资源商店(Assert Store)下载的,然后用4个栅栏组成。
    在这里插入图片描述
  • 地面搭建完成
    Image

3.预制动画

  • 巡逻兵
    巡逻兵在游戏开始时进入run状态,当游戏按下暂停时,动作会切换idle状态。这两个状态切换通过控制bool类型参数pause实现。当巡逻兵碰撞到玩家时,通过控制触发器类型参数shoot进入shoot状态,然后退出。
    在这里插入图片描述

  • 玩家
    玩家在游戏开始时进入idle状态,当键盘输入进行移动时,控制bool类型参数run进入run状态,没有移动会切换idle状态。游戏暂停时也会进入idle状态,当游戏失败即玩家被巡逻兵捕获时,控制触发器类型参数death进入death状态。
    在这里插入图片描述

4.实现

巡逻兵数据类

public class PatrolData : MonoBehaviour
{   
    public GameObject player;   // 巡逻兵追击的玩家
    public bool isHitObstacle;  // 是否碰撞到障碍物
    public bool isFollowing;     // 是否正在追击
    public int patrolRegion;    // 巡逻兵所在区域
    public int playerRegion;    // 玩家所在区域
    public bool isPlayerFound;  // 是否感知到玩家
}

巡逻兵工厂类

public class PatrolFactory : MonoBehaviour
{
    public GameObject patrol;
    private List<PatrolData> used = new List<PatrolData>(); // 正在使用的巡逻兵

    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<PatrolData>().patrolRegion = i * 3 + j + 1;
                patrol.GetComponent<PatrolData>().playerRegion = 4;
                patrol.GetComponent<PatrolData>().isPlayerFound = false;
                patrol.GetComponent<PatrolData>().isFollowing = false;
                patrol.GetComponent<PatrolData>().isHitObstacle = false;
                patrol.GetComponent<Animator>().SetBool("pause", true);
                used.Add(patrol.GetComponent<PatrolData>());
                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 : SSAction
{
    private float pos_x, pos_z;                 // 移动前的初始x和z方向坐标
    private float moveSpeed = 1.2f;             // 移动速度
    private bool isChooseNewDir = true;         // 是否选择新方向
    private PatrolData data;              // 巡逻兵的数据

    private PatrolAction() { }

    public static PatrolAction GetSSAction(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<PatrolData>(); 
    }

    public override void Update()
    {
        if (SSDirector.GetInstance().currentScenceController.getGameState().Equals(GameState.RUNNING))
        {
            // 巡逻兵巡逻
            Patrol();

            // 切换追击模式
            if (!data.isFollowing && data.isPlayerFound && data.patrolRegion == data.playerRegion && !data.isHitObstacle)
            {
                this.enable = false;
                this.destroy = true;
                this.callback.SSActionEvent(this);
                this.gameobject.GetComponent<PatrolData>().isFollowing = true;
                Singleton<GameEventManager>.Instance.FollowPlayer(this.gameobject);
            }
        }
    }

    void Patrol()
    {
        // 碰撞后寻找新位置
        if (isChooseNewDir)
        {
            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<PatrolData>().isHitObstacle = false;
            isChooseNewDir = false;
        }

        // 计算所在位置与目标位置的距离,用于判断是否碰撞
        float distance = Vector3.Distance(transform.position, new Vector3(pos_x, 0, pos_z));

        if (this.gameobject.GetComponent<PatrolData>().isHitObstacle)
        {
            // 碰撞,则向后转,寻找新位置
            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<PatrolData>().isHitObstacle = false;
            Destroy(temp);
        }
        else if (distance <= 0.1)
        {
            isChooseNewDir = true;
        }
        else
        {
            // 向前移动巡逻兵
            this.transform.Translate(0, 0, Time.deltaTime);
        }
    }
}
  • 巡逻兵追击动作
public class PatrolFollowAction : SSAction
{
    private float speed = 1.5f;          // 跟随玩家的速度
    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 Start()
    {
        data = this.gameobject.GetComponent<PatrolData>();
    }

    public override void Update()
    {
        if (SSDirector.GetInstance().currentScenceController.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.isPlayerFound && data.patrolRegion == data.playerRegion) || data.isHitObstacle))
            {
                this.destroy = true;
                this.enable = false;
                this.callback.SSActionEvent(this);
                this.gameobject.GetComponent<PatrolData>().isFollowing = false;
                Singleton<GameEventManager>.Instance.PlayerEscape(this.gameobject);
            }
        }
    }
}

巡逻兵碰撞检测

  • 给巡逻兵添加组件Capsule Collider,用于检测碰撞,注意选项Is Trigger不要勾选。
    在这里插入图片描述
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<PatrolData>().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();
        }
    }
}

游戏场景控制器

public class FirstSceneController : MonoBehaviour, SceneController, IUserAction
{
    public UserGUI userGUI;
    private GameState gameState = GameState.START;                  // 游戏状态

    public PatrolActionManager patrolActionManager;                 // 巡逻兵动作管理器
    public PatrolFactory patrolFactory;                             // 巡逻兵工厂
    private List<GameObject> patrols;                               // 场景中巡逻兵列表
    public ScoreRecorder scoreRecorder;                             // 计分器
    
    public GameObject player;                                       // 游戏玩家
    public int playerRegion;                                        // 玩家所在区域

    
    // 场景初始化
    void Start() {
        SSDirector director = SSDirector.GetInstance();
        director.CurrentSceneController = this;
        director.SetFPS(30);
        director.leaveSeconds = 60;
        
        scoreRecorder = Singleton<ScoreRecorder>.Instance;
        patrolFactory = Singleton<PatrolFactory>.Instance;
        userGUI = gameObject.AddComponent<UserGUI>() as UserGUI;
        playerRegion = 5;
        
        patrolActionManager = gameObject.AddComponent<PatrolActionManager>();
        LoadResources();
        // 巡逻兵开始巡逻
        for (int i = 0; i < patrols.Count; i++) {
            patrolActionManager.Patrol(patrols[i]);
        }
    }

    // 加载游戏资源
    public void LoadResources() {
        Instantiate(Resources.Load<GameObject>("Prefabs/Plane"));
        player = Instantiate(Resources.Load("Prefabs/Player"), new Vector3(-1.5f, 0, -1.5f), Quaternion.identity) as GameObject;
        patrols = patrolFactory.GetPatrols();
        // 镜头跟随
        Camera.main.GetComponent<CameraFollowAction>().player = player;
    }

    private void Update() {
        // 通知巡逻兵更新玩家所在区域信息
        for (int i = 0; i < patrols.Count; i++) {
            patrols[i].GetComponent<PatrolData>().playerRegion = playerRegion;
        }
    }

    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 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;

        float playerPosX = player.transform.position.x;
        float playerPosY = player.transform.position.y;
        float playerPosZ = player.transform.position.z;

        player.transform.LookAt(new Vector3(playerPosX + translationX, playerPosY, playerPosZ + translationZ));
        player.transform.Translate(0, 0, Mathf.Abs(translationZ) + Mathf.Abs(translationX));
    }

    // 失去目标, 巡逻兵切换巡逻模式,玩家得分
    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 int GetScore() {
        return scoreRecorder.score;
    }

    // 重新开始
    public void Restart() {
        SceneManager.LoadScene("Scenes/new");
    }

    // 暂停游戏
    public void Pause() {
        gameState = GameState.PAUSE;
        patrolFactory.PausePatrol();
        player.GetComponent<Animator>().SetBool("pause", true);
        StopAllCoroutines();
    }

    // 开始游戏
    public void Begin() {
        gameState = GameState.RUNNING;
        patrolFactory.StartPatrol();
        player.GetComponent<Animator>().SetBool("pause", false);
        StartCoroutine(SSDirector.GetInstance().CountDown());
    }

    // 获取游戏状态
    public GameState getGameState() {
        return gameState;
    }
}

5. 项目地址和演示视频

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值