Unity —— 智能巡逻兵

游戏设计要求:

创建一个地图和若干巡逻兵(使用动画);
每个巡逻兵走一个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;
        }
    }
    
游戏截图:

请添加图片描述
请添加图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值