zoukankan      html  css  js  c++  java
  • 【跟我一起学Unity3D】做一个2D的90坦克大战之AI系统

    对于AI,我的初始想法非常easy,首先他要能动,而且是在地图里面动。 懂得撞墙后转弯,然后懂得射击,其它的没有了,基于这个想法,我首先创建了一个MyTank类,用于管理玩家的坦克的活动,然后创建AITank类,AITank类继承MyTank类。这种话。在AITank类上,仅仅须要添加AI就能够了。详细的状态机实现,就放到MyTank类上就可以。

    首先来分析一下MyTank这个类,就从有限状态机開始吧。

    一辆坦克的状态有以下几个:

        protected enum State
        {
            Idle,
            LeftWalk,
            RightWalk,
            UpWalk,
            DownWalk,
            Fire
        };

    各自是:站立。向左走,向右走,向上走,向下走,开火。

    然后就是分别实现每一个状态,首先是转向:

        protected void UpdateRotate(State state)
        {
            switch (state)
            {
                case State.LeftWalk:
                    this.transform.rotation = Quaternion.identity;
                    this.transform.Rotate(Vector3.forward * m_fAngle);
                    m_curAngle = Vector3.left;
                    break;
                case State.RightWalk:
                    this.transform.rotation = Quaternion.identity;
                    this.transform.Rotate(Vector3.forward * -m_fAngle);
                    m_curAngle = Vector3.right;
                    break;
                case State.UpWalk:
                    this.transform.rotation = Quaternion.identity;
                    m_curAngle = Vector3.up;
                    break;
                case State.DownWalk:
                    this.transform.rotation = Quaternion.identity;
                    this.transform.Rotate(Vector3.forward * m_fAngle * 2);
                    m_curAngle = Vector3.down;
                    break;
            }
        }

    然后是行走和开火:

        public virtual void UpdateState()
        {
            Debug.Log(m_sName + ":UpdateState()");
            switch (m_curState)
            {
                case State.Idle:
                    break;
                case State.LeftWalk:
                    this.transform.position += Vector3.left * Time.deltaTime * m_fMoveSpeed;
                    break;
                case State.UpWalk:
                    this.transform.position += Vector3.up * Time.deltaTime * m_fMoveSpeed;
                    break;
                case State.DownWalk:
                    this.transform.position += Vector3.down * Time.deltaTime * m_fMoveSpeed;
                    break;
                case State.RightWalk:
                    this.transform.position += Vector3.right * Time.deltaTime * m_fMoveSpeed;
                    break;
                case State.Fire:
                    doFire();
                    break;
            }
        }
    当中,doFire的函数实现例如以下:

        protected virtual void doFire()
        {
            GameObject bullet = Instantiate(m_gBullet) as GameObject;
            bullet.name = m_sName + "Bullet";
            bullet.GetComponent<CBullet>().m_vDirection = m_curAngle;
            bullet.GetComponent<CBullet>().m_fMoveSpeed = m_fMoveSpeed + 1.0f;
            bullet.transform.position = new Vector3(this.transform.position.x, this.transform.position.y,0);
            Destroy(bullet, 5.0f);
        }

    设计为虚函数的原因是。AITank须要重写这个函数。
    然后就须要监听各个按键,我设定为,按下A,W,S,D为方向键。松开就停止移动。然后按下K则开火,所以在Update函数里面应该这么实现:

        void Update()
        {
            SetCurState(State.Idle);
            //转向
            if (Input.GetKey(KeyCode.A))
            {
                UpdateRotate(State.LeftWalk);
                SetCurState(State.LeftWalk);
            }
            if (Input.GetKey(KeyCode.W))
            {
                UpdateRotate(State.UpWalk);
                SetCurState(State.UpWalk);
            }
            if (Input.GetKey(KeyCode.S))
            {
                UpdateRotate(State.DownWalk);
                SetCurState(State.DownWalk);
            }
            if (Input.GetKey(KeyCode.D))
            {
                UpdateRotate(State.RightWalk);
                SetCurState(State.RightWalk);
            }
            if (Input.GetKeyDown(KeyCode.K))
            {
                SetCurState(State.Fire);
            }
            UpdateState();
        }

    当中SetCurState这个函数是用来设置当前状态的,而且把上一次的状态也存储起来,方便使用。

        protected void SetCurState(State curState)
        {
            if (curState != m_curState)
                m_lastState = m_curState;
            m_curState = curState;
        }

    最后就到了一些碰撞检測的函数了,比如,碰到敌人的子弹就生命值减一,生命值为0了就宣布游戏结束。

        void OnTriggerEnter2D(Collider2D other)
        {
            Debug.Log(m_sName + " OnTriggerEnter : " + other.gameObject.name);
            //被打中了
            if (other.gameObject.name == "AIBullet")
            {
                Destroy(other.gameObject);
                if (Camera.main.GetComponent<CCamera>().ReduceMyLeft() > 0)
                {
                    GameObject temp = Instantiate(m_BoomAnimation, this.gameObject.transform.position, Quaternion.identity) as GameObject;
                    Destroy(temp, 0.5f);
                    string name = this.gameObject.name;
                    GameObject temp_tank = Instantiate(this.gameObject, m_initPosition, Quaternion.identity) as GameObject;
                    temp_tank.name = name;
                }
                else
                {
                    //你输了
                    Camera.main.GetComponent<CCamera>().m_bIsLose = true;
                }
                Destroy(this.gameObject);
    
            }
        }

    上面就是整个MyTank的实现过程了

    ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

    以下到了AITank的实现,AITank主要是加入一些触发器,比如检測到碰撞就转弯,每隔两秒就发射子弹等等,作为一个很easy的AI,就不须要考虑那么多。首先是碰撞检測:

        void OnCollisionEnter2D(Collision2D coll)
        {
            Debug.Log(m_sName + " OnCollisionEnter2D : " + coll.gameObject.name);
            ChangeStateFromWall();
        }
    遇到碰撞就转向,然后ChangeStateFromWall的详细实现例如以下:

        void ChangeStateFromWall()
        {
            switch (m_curState)
            {
                case State.Idle:
                    SetCurState(State.DownWalk);
                    break;
                case State.LeftWalk:
                    SetCurState(State.UpWalk);
                    break;
                case State.UpWalk:
                    SetCurState(State.RightWalk);
                    break;
                case State.DownWalk:
                    SetCurState(State.LeftWalk);
                    break;
                case State.RightWalk:
                    SetCurState(State.DownWalk);
                    break;
                case State.Fire:
                    SetCurState(m_lastState);
                    break;
            }
            this.UpdateRotate(m_curState);
        }
    }

    这是一个循环转向的函数,假设当前方向是左边,就往上走,假设当前方向是上边。就往右走....;假设在开火状态的话。就返回上一状态。最后更新AI的角度。

    然后到开火,开火的设定很easy,调用到一个函数:InvokeRepeating。这个是一个反复定时器。函数原型为:

    void InvokeRepeating (string methodName,float time,float repeatRate) 

    第一个參数是传入的方法。第二个參数是在几秒后開始。第三个參数是開始后每隔几秒反复运行。

    我们须要的是在AI坦克生成后一秒发射子弹 然后周期是每隔两秒发射一次。所以在Start函数中应该这么写:

            //每隔2秒开一次火
            InvokeRepeating("doFire", 1, 2);
    然后,我们须要重写doFire这个函数:

        protected override void doFire()
        {
            base.doFire();
        }
    

    这样,就完毕了每两秒发射一发子弹的功能了。

    上面就是AI的基本功能了,以后能够慢慢优化它。使它成为一个强大的AI。

    项目源代码:【跟我一起学Unity3D】做一个2D的90坦克大战之各种各样的墙<<附上项目源代码>>

  • 相关阅读:
    解决SharePoint 文档库itemadded eventhandler导致的上传完成后,编辑页面保持报错的问题,错误信息为“该文档已经被编辑过 the file has been modified by...”
    解决SharePoint 2013 designer workflow 在发布的报错“负载平衡没有设置”The workflow files were saved but cannot be run.
    随机实例,随机值
    Spring4笔记
    struts2笔记(3)
    struts2笔记(2)
    获取文本的编码类型(from logparse)
    FileUtil(from logparser)
    DateUtil(SimpleDateFormat)
    struts2笔记
  • 原文地址:https://www.cnblogs.com/gccbuaa/p/6900247.html
Copyright © 2011-2022 走看看