Unity人工智能學習—具備條件邏輯處理的模式

上一篇講到了基本的模式,通過操作一系列預設的指令集來實現遊戲對象的智能,這一篇當然還是要實現太該有的追蹤功能,那麼這個涉及應該如何以下邏輯:1、當符合追蹤範圍,追蹤目標,反之繼續按照以前的模式繼續執行。當然還可以繼續深入的添加功能2、如果符合攻擊範圍則停止並保留當前的移動狀態而切換到攻擊動畫,如果不在攻擊範圍則恢復之前的移動狀態繼續追蹤或者模式運動。

實現這個功能只需要在前面的模式執行的基礎上在加一個if語句來判斷與追蹤目標的距離判斷就OK啦。代碼如下:

 void BasicPattern_AI()
    {
        if (m_currentPattern == null)
        {
            m_patternIndex = Random.Range(1, 5);
            // m_patternIndex = 3;
            m_currentPattern = TextToolControl.GetInstance().GetContent(m_patternIndex);

            m_currentOperand = 0;
            m_timeCounter = 0;
        }

        float vx = target.transform.position.x - this.transform.position.x;
        float vz = target.transform.position.z - this.transform.position.z;
        float length = PointDistance_2D(vx, vz);
      
        //如果達到距離就追蹤
        if (length < Min_Tracker_Dis)
        {
            if (length <= Min_Attack_Dis)
            {
                isMove = false;
                m_animation.CrossFade("orc_attack_repeatedly");
            }
            else
            {
                isMove = true;
                vx = vx / length;
                vz = vz / length;
                ChangeDirection(vx, vz);
            }
        }
        //否則繼續進行預設的模式序列
        else
        {
            //每個具體操作的操作時間到了就執行下一個操作
            m_timeCounter -= Time.deltaTime;
            if (m_timeCounter <= 0)
            {
                // moveDirection = MoveDirection.Random;
                moveDirection = (MoveDirection)m_currentPattern[m_currentOperand];
                m_timeCounter = m_currentPattern[m_currentOperand + 1];
                switch (moveDirection)
                {
                    case MoveDirection.East:
                        {
                            ChangeDirection(-1, 0);
                        } break;
                    case MoveDirection.North:
                        {
                            ChangeDirection(0, -1);
                        } break;
                    case MoveDirection.South:
                        {
                            ChangeDirection(0, 1);
                        } break;
                    case MoveDirection.West:
                        {
                            ChangeDirection(1, 0);
                        } break;
                    case MoveDirection.Stop:
                        {
                            ChangeDirection(0, 0);
                        }
                        break;
                    case MoveDirection.End:
                        {
                            ChangeDirection(0, 0);
                            m_patternIndex = Random.Range(1, 5);
                            m_currentPattern = TextToolControl.GetInstance().GetContent(m_patternIndex);
                            m_currentOperand = 0;
                            m_timeCounter = 0;
                        } break;
                    case MoveDirection.Random:
                        {
                            //隨機的改變方向
                            float x = Random.Range(-10.0f, 10.0f) / 10.0f;//一定是要是浮點型不是整數型不然得不到-1到1之間的小數,如果是整數等到的永遠是0
                            float z = Random.Range(-10.0f, 10.0f) / 10.0f;
                            //  Debug.Log("x:" + x + "z:" + z);
                            ChangeDirection(x, z);
                        } break;
                }
                if (m_currentOperand < m_currentPattern.Count - 1)
                {
                    m_currentOperand += 1;
                }
            }
        }
    }

當然前面的關於距離判斷就要改y爲z了,如下:

 float PointDistance_2D(float x, float z)
    {
        //使用了泰勒展開式來計算,有3.5%的誤差,直接使用開方計算會比較慢,但是測試了我的電腦好像沒有什麼變化可能是數據量不大體現不出來
        /*x = Mathf.Abs(x);
        y = Mathf.Abs(y);
        float mn = Mathf.Min(x, y);//獲取x,y中最小的數
        float result = x + y - (mn / 2) - (mn / 4) + (mn / 8);*/

        float result = Mathf.Sqrt(x * x + z *z );
        return result;
    }

完整的代碼如下:

 

 

using UnityEngine;
using System.Collections;
using System.Collections.Generic;
//移動模式枚舉
public enum MoveDirection:int
{
    East=0,South=1,West=2,North=3,Stop=4,Random=5,End=6
}
public class AIBasicPattern : MonoBehaviour {
    public Transform target;
    public float Min_Tracker_Dis;
    public float Min_Attack_Dis;
    public Animation m_animation;
    int m_patternIndex;
    int m_currentOperand;//當前模式中的方向索引
    MoveDirection moveDirection;
    float m_timeCounter;//當前模式的時間
    List<int> m_currentPattern;
    public float moveVx=0;
    public float moveVz=0;
    bool isMove = true;
	// Use this for initialization
	void Start () {
        
	}
	
	// Update is called once per frame
	void Update () {
        BasicPattern_AI();
        CheckBoundary();
        if (isMove)
        {
            Move();
        }
        Debug.Log("模式:"+m_patternIndex+"操作數:"+m_currentOperand+"Vx:"+moveVx+",VZ:"+moveVz);
	}
    void BasicPattern_AI()
    {
        if (m_currentPattern == null)
        {
            m_patternIndex = Random.Range(1, 5);
            // m_patternIndex = 3;
            m_currentPattern = TextToolControl.GetInstance().GetContent(m_patternIndex);

            m_currentOperand = 0;
            m_timeCounter = 0;
        }

        float vx = target.transform.position.x - this.transform.position.x;
        float vz = target.transform.position.z - this.transform.position.z;
        float length = PointDistance_2D(vx, vz);
      
        //如果達到距離就追蹤
        if (length < Min_Tracker_Dis)
        {
            if (length <= Min_Attack_Dis)
            {
                isMove = false;
                m_animation.CrossFade("orc_attack_repeatedly");
            }
            else
            {
                isMove = true;
                vx = vx / length;
                vz = vz / length;
                ChangeDirection(vx, vz);
            }
        }
        //否則繼續進行預設的模式序列
        else
        {
            //每個具體操作的操作時間到了就執行下一個操作
            m_timeCounter -= Time.deltaTime;
            if (m_timeCounter <= 0)
            {
                // moveDirection = MoveDirection.Random;
                moveDirection = (MoveDirection)m_currentPattern[m_currentOperand];
                m_timeCounter = m_currentPattern[m_currentOperand + 1];
                switch (moveDirection)
                {
                    case MoveDirection.East:
                        {
                            ChangeDirection(-1, 0);
                        } break;
                    case MoveDirection.North:
                        {
                            ChangeDirection(0, -1);
                        } break;
                    case MoveDirection.South:
                        {
                            ChangeDirection(0, 1);
                        } break;
                    case MoveDirection.West:
                        {
                            ChangeDirection(1, 0);
                        } break;
                    case MoveDirection.Stop:
                        {
                            ChangeDirection(0, 0);
                        }
                        break;
                    case MoveDirection.End:
                        {
                            ChangeDirection(0, 0);
                            m_patternIndex = Random.Range(1, 5);
                            m_currentPattern = TextToolControl.GetInstance().GetContent(m_patternIndex);
                            m_currentOperand = 0;
                            m_timeCounter = 0;
                        } break;
                    case MoveDirection.Random:
                        {
                            //隨機的改變方向
                            float x = Random.Range(-10.0f, 10.0f) / 10.0f;//一定是要是浮點型不是整數型不然得不到-1到1之間的小數,如果是整數等到的永遠是0
                            float z = Random.Range(-10.0f, 10.0f) / 10.0f;
                            //  Debug.Log("x:" + x + "z:" + z);
                            ChangeDirection(x, z);
                        } break;
                }
                if (m_currentOperand < m_currentPattern.Count - 1)
                {
                    m_currentOperand += 1;
                }
            }
        }
    }
    float PointDistance_2D(float x, float z)
    {
        //使用了泰勒展開式來計算,有3.5%的誤差,直接使用開方計算會比較慢,但是測試了我的電腦好像沒有什麼變化可能是數據量不大體現不出來
        /*x = Mathf.Abs(x);
        y = Mathf.Abs(y);
        float mn = Mathf.Min(x, y);//獲取x,y中最小的數
        float result = x + y - (mn / 2) - (mn / 4) + (mn / 8);*/

        float result = Mathf.Sqrt(x * x + z *z );
        return result;
    }
    void ChangeDirection(float vx,float vz)
    {
        moveVx = vx;
        moveVz = vz;
    }
    void Move()
    {   
        if(moveVz!=0||moveVx!=0)
        {
            m_animation.CrossFade("orc_walk");
        }
        else if(moveVx==0&&moveVz==0)
        {
            m_animation.CrossFade("orc_idle");
        }
        //位置的移動,此時是在X,Z平面
        this.transform.position += new Vector3(moveVx * Time.deltaTime, 0, moveVz * Time.deltaTime);

        //運動朝向的改變
        float yAngles = Mathf.Atan(moveVx / moveVz) * (180 / Mathf.PI);
        //此時的角度是與Z軸的夾角,但是模型對應的是轉Y軸
        if (moveVz == 0)
        {
            yAngles = moveVx >= 0 ? 90 : -90;
        } 
        if (moveVz < 0)
        {
            yAngles = yAngles - 180;
        }
        Debug.Log("角度轉動爲:"+yAngles);
        Vector3 tempAngles = new Vector3(0, yAngles,0);
        Quaternion tempQua = this.transform.rotation;
        tempQua.eulerAngles = tempAngles;
        this.transform.rotation = tempQua;
    }
   void CheckBoundary()
    {
        if (Mathf.Abs(this.transform.position.x) > 10 || (Mathf.Abs(this.transform.position.z) > 10))
        {
            if (isMove)
            {
                m_animation.CrossFade("orc_hit_front");
            }
            isMove = false;
            if (!m_animation.IsPlaying("orc_hit_front"))
            {
                //改變方向
                moveVx = -moveVx;
                moveVz = -moveVz;
                isMove = true;
            }

        }
    }
}

控制Player作爲追蹤目標的代碼如下:

 

 

using UnityEngine;
using System.Collections;

public class BasicPatternPlayerMove : MonoBehaviour {
    public float moveSpeed;
	// Use this for initialization
	void Start () {
	
	}
	
	// Update is called once per frame
	void Update () {
        Move();
        
	
	}
    void Move()
    {
        float x = -Input.GetAxis("Horizontal") * 100;
        float z = -Input.GetAxis("Vertical") * 100;
        //如果超出屏幕範圍則讓它出現在另一面
        transform.Translate(x * Time.deltaTime * moveSpeed, 0, z * Time.deltaTime * moveSpeed);
    }
}

有關模式的處理還是相對來說比較簡單的,只要預先設定好指令集,配合好代碼的Switch處理邏輯就能夠寫出比完全是隨機過程的行走巡邏代碼要看上去智能很多。
附上我的工程下載地址:點擊打開鏈接
 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章