• 【跟我一起学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坦克大战之各种各样的墙<<附上项目源代码>>

  • 相关阅读:
    [转].net批量导入数据
    IIS6 应用程序池配置详解附建议设置
    [转]SQLServer死锁问题
    VSS 版本管理
    非功能性需求介绍[转]
    javascript获取网页中HTML元素
    [转]如何做一流的研究
    多web并发测试问题解决
    jQuery 页面校验
    SqlServer排序规则错误致使不能查询
  • 原文地址:https://www.cnblogs.com/gccbuaa/p/6900247.html
Copyright © 2020-2023  润新知