1. 程式人生 > >Unity 內的敵人AI 或者 有限狀態機FSM實現AI

Unity 內的敵人AI 或者 有限狀態機FSM實現AI

孫廣東  2015.8.15

一、Enemy Aim Ai 
          目的: 這篇文章的主要目的是為了讓您瞭解有關如何使用 Enemy Aim Ai 。你會得到結果:


          Enemy aim AI是非常有用的,當你想要敵人一直監視player。適當爭取物件在真實世界的場景,需要時間,所以敵人會採取一些之前它鎖在目標系統上的時間量。

這種效應可以建立的 Lerping 旋轉角度對玩家的敵人。這種情況是在動作遊戲,敵人跟隨,目的是何地然後射球員的情況下非常有用。對敵人遵循概念是在早些時候釋出的部落格中已經討論過。在推行鍼對在遊戲的同時,理解四元數的概念是非常必要的。

四元數儲存物件的旋轉,還可以計算價值取向。一個可以直接玩尤拉角,但可能會出現 萬向鎖 的情況。

根據當地的座標系統,如果你旋轉模型X 軸,然後其 Y 和 Z 軸經驗萬向節鎖被"鎖定"在一起。

現在回到我們的主題,給出了下面是一個簡單的例子來解釋 Enemy aim AI

按照下面給出的步驟。

1、建立Cube將作用在player control

  • 應用適當的材料,根據要求向其網格渲染器
  • 將 TargetMovementScript 應用於遊戲物件
  • 將Cube 作為物件來將移動上 player'scommands。

TargetMovementScript.cs

public class TargetMovementScript : MonoBehaviour {
public float targetSpeed=9.0f;//Speed At Which the Object Should Move
void Update () 
transform.Translate (Input.GetAxis ("Horizontal")*Time.deltaTime*targetSpeed,Input.GetAxis ("Vertical")*Time.deltaTime*targetSpeed,0);
}
}

2、建立一個Enemy物件,由箭頭和Cube組成,

1、應用適當的材料對Cube 。

2、使箭頭Sprite作為一個不同的物件

3、其方向由 Cube指出 . 所以在這裡,箭頭將充當了敵人的槍。

4、此箭頭將指向目標物件和它看起來就好像它試圖鎖定目標

5、我們也可以操縱速度,敵人將能夠鎖定目標屬性 ;作為不同的敵人應該是不同的困難,以及不同的功能。
6、一輛坦克應採取更多的時間鎖定一名士兵用槍瞄準。所以在其中之一可以鎖定目標的速度應該是不同的。

EnemyAimScript.cs

public class EnemyAimScript : MonoBehaviour {
 
public Transform target; // Target Object
public float enemyAimSpeed=5.0f; // Speed at Which Enenmy locks on the Target
Quaternion newRotation;
float orientTransform;
float orientTarget;
void Update () {
orientTransform = transform.position.x;
orientTarget = target.position.x;
// To Check on which side is the target , i.e. Right or Left of this Object
if (orientTransform > orientTarget) {
// Will Give Rotation angle , so that Arrow Points towards that target
newRotation = Quaternion.LookRotation (transform.position - target.position, -Vector3.up);
}
else {
newRotation = Quaternion.LookRotation (transform.position - target.position,Vector3.up);
}
 
// Here we have to freeze rotation along X and Y Axis, for proper movement of the arrow
newRotation.x = 0.0f;
newRotation.y = 0.0f;
 
// Finally rotate and aim towards the target direction using Code below
transform.rotation = Quaternion.Lerp (transform.rotation,newRotation,Time.deltaTime * enemyAimSpeed);
 
// Another Alternative
// transform.rotation = Quaternion.RotateTowards(transform.rotation,newRotation, Time.deltaTime * enemyAimSpeed);
}
}

7、Hierarchy 和 Scene View 可能會像作為給定下面

備註:-

可以改變敵人目標並設定鎖定目標的速度。

可以指令碼通過允許X 或 Y 軸旋轉,為了解這一概念的 。

可以給敵人新增Follow 指令碼,以便敵人Follow 和 瞄準 玩家。


而不是使用 Quaternion.Lerp 你也可以使用 Quaternion.RotateTowards 達到相同的效果。

二、通過有限狀態機實現AI

Contents Objective Step -

1: Set up the scene Step -

2: Create and place Game Objects Step -

3: Implement the BoxMovement Script Step -

4: FSM Modelled Script Step -

5: AI Script for the Box -

目的: 這篇文章的主要目的是要給你一個關於如何使用有限狀態機模型在 實現 AI 。

FSM 的基礎知識:
           在遊戲中實施 AI  Finite State Machine Framework是完美的, 產生偉大的結果,而無需複雜的程式碼。   它是一個模型,由一個或多個狀態。可以在一段時間只有一個單一的狀態處於活動狀態, 所以這臺機器必須從一個狀態到另一種的轉換 為執行不同的操作。


            有限狀態機框架通常用於管理、 組織和 代表不同的狀態、執行流,在遊戲中實現人工智慧是非常有用的。"brain"是敵人,例如,可以使用有限狀態機來實現:   每個狀態表示一個動作,例如 巡邏、 Chase、 逃避或拍攝或任何其他種類的行動。


          AI FSMs 的工作與Unity的的Animation FSMs,在那裡一個動畫狀態將更改為另一個符合的要求。可以通過使用第三方外掛像行為實施 AI FSM 或它可以直接由指令碼以及執行。


若要獲取有限狀態機框架的基本理念,我們將執行一個教程最簡單的方法,使用 switch 語句。然後我們將學習如何使用一個框架,使 AI 實現易於管理和擴充套件。


請按照下面的步驟來執行簡單的有限狀態機。


在這裡我們將會有兩個Box,在那裡將由玩家控制一個Box和 對方將由 AI 控制Box。此 AI 控制的Box會在chasing追逐狀態或patrolling巡邏狀態,即Box中將開始追逐Player Box,一旦Player Box進來的接近 AI 控制Box。它將切換回 巡邏狀態,如果玩家足夠遠逃避  未未的定義的願景。

Step - 1: Set up the scene

以一個平面和兩個Box在場景中,如下圖所示的設定。

Step - 2: Create and place Game Objects

          建立空的遊戲物件並將其作為遊子點Wanderer Points。放置這些空的遊戲物件下,為您選擇平面周圍。在這裡藍色的Cube是 AI Cube和紅一個是玩家控制的Cube。將它們放在距離不夠遠。

Step - 3: Implement the BoxMovement Script

        實現 BoxMovement 指令碼來控制玩家的Cube的移動:如下:

public class BoxMovementScript : MonoBehaviour
{
		public float speed = 0.1f;
		private Vector3 positionVector3;
		void Update ()
		{		
				InitializePosition ();
				if (Input.GetKey (KeyCode.LeftArrow)) {
						GoLeft ();
				}
				if (Input.GetKey (KeyCode.RightArrow)) {
						GoRight ();
				}
				if (Input.GetKey (KeyCode.UpArrow)) {
						GoTop ();
				}
				if (Input.GetKey (KeyCode.DownArrow)) {
						GoDown ();
				}
				RotateNow ();
		}
		private void InitializePosition ()
		{
				positionVector3 = transform.position;
		}
		private void RotateNow ()
		{
				Quaternion targetRotation = Quaternion.LookRotation (transform.position - positionVector3);
				transform.rotation = targetRotation;
		}
		private void GoLeft ()
		{
				transform.position = transform.position + new Vector3 (-speed, 0, 0);			
		}
		private void GoRight ()
		{
				transform.position = transform.position + new Vector3 (speed, 0, 0);
		}
		private void GoTop ()
		{
				transform.position = transform.position + new Vector3 (0, 0, speed);
		}
		private void GoDown ()
		{
				transform.position = transform.position + new Vector3 (0, 0, -speed);
		}
}

Step - 4: FSM Modelled Script

構建FSM模型指令碼:

public class FSM : MonoBehaviour
{
		//Player Transform
		protected Transform playerTransform;
		//Next destination position of the Box
		protected Vector3 destPos;
		//List of points for patrolling
		protected GameObject[] pointList;
		protected virtual void Initialize (){
		}
		protected virtual void FSMUpdate (){
		}
		protected virtual void FSMFixedUpdate (){
		}
		void Start ()
		{
				Initialize ();
		}
		void Update ()
		{
				FSMUpdate ();
		}
		void FixedUpdate ()
		{
				FSMFixedUpdate ();
		}
}

Step - 5: AI Script for the Box

   構建 AI 指令碼作為Box擴充套件.

public class BoxFSM : FSM
{
		public enum FSMState
		{
				None,
				Patrol,
				Chase,
		}
		//Current state that the Box is in
		public FSMState curState;
		//Speed of the Box
		private float curSpeed;
		//Box Rotation Speed
		private float curRotSpeed;

		//Initialize the Finite state machine for the  AI Driven Box
		protected override void Initialize ()
		{
				curState = FSMState.Patrol;
				curSpeed = 5.0f;
				curRotSpeed = 1.5f;				
				
				//Get the list of points
				pointList = GameObject.FindGameObjectsWithTag ("WandarPoint");
				
				//Set Random destination point for the patrol state first
				FindNextPoint ();
				
				//Get the target enemy(Player)
				GameObject objPlayer = GameObject.FindGameObjectWithTag ("Player");
				playerTransform = objPlayer.transform;
				
				if (!playerTransform)
						print ("Player doesn't exist.. Please add one " + "with Tag named 'Player'");
				
		}

		//Update each frame
		protected override void FSMUpdate ()
		{
				switch (curState) {
				case FSMState.Patrol:
						UpdatePatrolState ();
						break;
				case FSMState.Chase:
						UpdateChaseState ();
						break;
				}
		}

		protected void UpdatePatrolState ()
		{
				//Find another random patrol point on reaching the current Point 
				//point is reached
				if (Vector3.Distance (transform.position, destPos) <= 2.5f) {
						print ("Reached to the destination point\n" + "calculating the next point");
						FindNextPoint ();
				}

				//Check the distance with player Box
				//When the distance is near, transition to chase state
				else if (Vector3.Distance (transform.position, playerTransform.position) <= 15.0f) {
						print ("Switch to Chase State");
						curState = FSMState.Chase;
				}
				
				//Rotate to the target point
				Quaternion targetRotation = Quaternion.LookRotation (destPos - transform.position);
				transform.rotation = Quaternion.Slerp (transform.rotation, targetRotation, Time.deltaTime * curRotSpeed);
				
				//Go Forward
				transform.Translate (Vector3.forward * Time.deltaTime * curSpeed);
		}

		protected void FindNextPoint ()
		{
				print ("Finding next point");
				int rndIndex = Random.Range (0, pointList.Length);
				float rndRadius = 5.0f;
				Vector3 rndPosition = Vector3.zero;
				destPos = pointList [rndIndex].transform.position + rndPosition;
				
				//Check Range to Move and decide the random point
				//as the same as before
				if (IsInCurrentRange (destPos)) {
						rndPosition = new Vector3 (Random.Range (-rndRadius, rndRadius), 0.0f, Random.Range (-rndRadius, rndRadius));
						destPos = pointList [rndIndex].transform.position + rndPosition;
				}
		}

		protected bool IsInCurrentRange (Vector3 pos)
		{
				float xPos = Mathf.Abs (pos.x - transform.position.x);
				float zPos = Mathf.Abs (pos.z - transform.position.z);
				if (xPos <= 8 && zPos <= 8)
						return true;
				return false;
		}

		protected void UpdateChaseState ()
		{
				//Set the target position as the player position
				destPos = playerTransform.position;
				//Check the distance with player Box When
				float dist = Vector3.Distance (transform.position, playerTransform.position);
				
				//Go back to patrol as player is now too far
				if (dist >= 15.0f) {
						curState = FSMState.Patrol;
						FindNextPoint ();
				}
				//Rotate to the target point
				Quaternion targetRotation = Quaternion.LookRotation (destPos - transform.position);
				transform.rotation = Quaternion.Slerp (transform.rotation, targetRotation, Time.deltaTime * curRotSpeed);
				//Go Forward
				transform.Translate (Vector3.forward * Time.deltaTime * curSpeed);
		}
}

       此指令碼適用於Cube是要跟隨玩家,不要忘記把player標記Tag為 Player 和 Tag 為 WandarPoint,現在如 FSMUpdate() 所示的指令碼將呼叫方法,這在子類中重寫和它將在每個 update () 上執行。         在這裡switch case被實施 將用於執行的當前狀態的操作。 因此擴充套件 AI 是很簡單的僅僅通過新增新的state。Initialize() 方法也重寫,並將在 start () 方法中呼叫執行。UpdatePatrolState() 將在每次更新上執行,噹噹前狀態是patrol 周圍巡邏,也將會發生在 UpdateChaseState(),當玩家在接近度 AI Box。如果當處於巡邏,玩家進來的 AI Box中,狀態將更改為 巡邏,相同 型別的檢查仍在  追逐模式檢查如果球員已遠離其視野範圍, 然後切換回巡邏狀態, 在每個更新,檢查狀態更改。結論:       FSM 的很容易理解和實現,Fsm 可以用於執行復雜的 AI 。他們也可以表示使用圖,允許開發人員很容易理解,因此開發人員可以調整、 改變和優化的最終結果。有限狀態機使用的函式或方法來代表狀態執行是簡單、 功能強大、 易於擴充套件。  可以使用基於堆疊的狀態機,確保易於管理和穩定的 執行流,而不會產生消極影響的程式碼應用甚至更復雜的 AI。 所以讓你的敵人更聰明使用有限狀態機,讓您的遊戲的成功。