使用有限狀態機(FSM)編寫的敵人AI

 1 using UnityEngine;
 2 using System.Collections;
 3 
 4 public class AttackState : FSMState
 5 {
 6     public AttackState() 
 7     { 
 8         stateID = FSMStateID.Attacking;
 9     }
10 
11     public override void Reason(Transform player, Transform npc)
12     {
13         if (npc.GetComponent<AIController>().stateInfo.normalizedTime >= 1.0f && npc.GetComponent<AIController>().stateInfo.IsName("Attack"))
14             npc.GetComponent<AIController>().SetTransition(Transition.AttackOver);
15     }
16 
17     public override void Act(Transform player, Transform npc)
18     {
19     }
20 }
 1 using UnityEngine;
 2 using System.Collections;
 3 
 4 public class DeadState : FSMState
 5 {
 6     public DeadState() 
 7     {
 8         stateID = FSMStateID.Dead;
 9     }
10 
11     public override void Reason(Transform player, Transform npc)
12     {
13 
14     }
15 
16     public override void Act(Transform player, Transform npc)
17     {        
18                 Animation animComponent = npc.GetComponent<Animation>();
19                 //animComponent.CrossFade("death");
20     }
21 }
 1 using UnityEngine;
 2 using System.Collections;
 3 
 4 public class MoveState : FSMState
 5 {
 6     float rotateTime;
 7     float curSpeed;
 8     public MoveState()
 9     {
10         stateID = FSMStateID.Move;
11 
12         curSpeed = 1.0f;
13         rotateTime = 0f;
14     }
15 
16     public override void Reason(Transform player, Transform npc)
17     {
18         float distance = Vector3.Distance(player.position, npc.position);
19         if (distance <= 5)
20         {
21             if (npc.GetComponent<AIController>().attackCd == 0f)
22             {
23                 npc.GetComponent<AIController>().SetTransition(Transition.ReachPlayer);
24                 npc.GetComponent<AIController>().attackCd = 5.0f;
25             }
26         }
27     }
28 
29     public override void Act(Transform player, Transform npc)
30     {
31         rotateTime -= Time.deltaTime;
32         if (rotateTime <= 0)
33         {
34             rotateTime = 3.0f;
35             Vector3 toTargetV3 = (player.position - npc.position).normalized;
36             float direction = Vector3.Dot(toTargetV3, npc.forward);
37             Vector3 u = Vector3.Cross(toTargetV3, npc.forward);
38             if (direction > 1) { direction = 1f; }
39             if (direction < -1) { direction = -1f; }
40             if (Mathf.Abs(direction) == 1f)
41                 return;
42             direction = Mathf.Acos(direction) * Mathf.Rad2Deg;
43             npc.rotation = npc.rotation * Quaternion.Euler(new Vector3(0, direction * (u.y >= 0 ? -1 : 1), 0));
44         }
45 
46         float distance = Vector3.Distance(player.position, npc.position);
47         if (distance >= 20)
48         {
49             npc.GetComponent<AIController>().animator.SetFloat("Blend", 1f);
50             curSpeed = 2f;
51         }
52         else if (distance < 20 && distance >= 2)
53         {
54             npc.GetComponent<AIController>().animator.SetFloat("Blend", 0.5f);
55             curSpeed = 1f;
56         }
57         else
58         {
59             npc.GetComponent<AIController>().animator.SetFloat("Blend", 0f);
60             curSpeed = 0;
61         }
62         npc.Translate(npc.transform.forward * Time.deltaTime * curSpeed, Space.World);
63     }
64 }
  1 using UnityEngine;
  2 using System.Collections;
  3 using System.Collections.Generic;
  4 
  5 /// <summary>
  6 /// This class is adapted and modified from the FSM implementation class available on UnifyCommunity website
  7 /// The license for the code is Creative Commons Attribution Share Alike.
  8 /// It's originally the port of C++ FSM implementation mentioned in Chapter01 of Game Programming Gems 1
  9 /// You're free to use, modify and distribute the code in any projects including commercial ones.
 10 /// Please read the link to know more about CCA license @http://creativecommons.org/licenses/by-sa/3.0/
 11 /// </summary>
 12 
 13 //定義枚舉,爲可能的轉換分配編號
 14 public enum Transition
 15 {    
 16     SawPlayer = 0, //看到玩家
 17     ReachPlayer,   //接近玩家
 18     LostPlayer,    //玩家離開視線
 19     NoHealth,      //死亡
 20     AttackOver,    //結束攻擊
 21 }
 22 
 23 /// <summary>
 24 /// 定義枚舉,爲可能的狀態分配編號ID
 25 /// </summary>
 26 public enum FSMStateID
 27 {    
 28     Move = 0,
 29     Attacking,       //攻擊編號
 30     Patrolling ,  //巡邏編號
 31     Chasing,         //追逐編號
 32     Dead,           //死亡編號
 33 }
 34 
 35 public class AdvancedFSM : FSM 
 36 {
 37     //FSM中的全部狀態組成的列表
 38     private List<FSMState> fsmStates;
 39     //當前狀態的編號
 40     //The fsmStates are not changing directly but updated by using transitions
 41     private FSMStateID currentStateID;
 42     public FSMStateID CurrentStateID { get { return currentStateID; } }
 43     //當前狀態
 44     private FSMState currentState;
 45     public FSMState CurrentState { get { return currentState; } }
 46 
 47     public AdvancedFSM()
 48     {
 49         //新建一個空的狀態列表
 50         fsmStates = new List<FSMState>();
 51     }
 52 
 53     /// <summary>
 54     ///向狀態列表中加入一個新的狀態
 55     /// </summary>
 56     public void AddFSMState(FSMState fsmState)
 57     {
 58         //檢查要加入的新狀態是否爲空,若是空就報錯
 59         if (fsmState == null)
 60         {
 61             Debug.LogError("FSM ERROR: Null reference is not allowed");
 62         }
 63 
 64         // First State inserted is also the Initial state
 65         //   the state the machine is in when the simulation begins
 66         //若是插入的這個狀態時,列表仍是空的,那麼將它加入列表並返回
 67         if (fsmStates.Count == 0)
 68         {
 69             fsmStates.Add(fsmState);
 70             currentState = fsmState;
 71             currentStateID = fsmState.ID;
 72             return;
 73         }
 74 
 75         // 檢查要加入的狀態是否已經在列表裏,若是是,報錯返回
 76         foreach (FSMState state in fsmStates)
 77         {
 78             if (state.ID == fsmState.ID)
 79             {
 80                 Debug.LogError("FSM ERROR: Trying to add a state that was already inside the list");
 81                 return;
 82             }
 83         }
 84         //若是要加入的狀態不在列表中,將它加入列表
 85         fsmStates.Add(fsmState);
 86     }
 87 
 88 
 89     //從狀態中刪除一個狀態   
 90     public void DeleteState(FSMStateID fsmState)
 91     {
 92         // 搜索整個狀態列表,若是要刪除的狀態在列表中,那麼將它移除,不然報錯
 93         foreach (FSMState state in fsmStates)
 94         {
 95             if (state.ID == fsmState)
 96             {
 97                 fsmStates.Remove(state);
 98                 return;
 99             }
100         }
101         Debug.LogError("FSM ERROR: The state passed was not on the list. Impossible to delete it");
102     }
103 
104     /// <summary>
105     /// 根據當前狀態,和參數中傳遞的轉換,轉換到新狀態
106     /// </summary>
107     public void PerformTransition(Transition trans)
108     {  
109         // 根絕當前的狀態類,以Trans爲參數調用它的GetOutputState方法
110         //肯定轉換後的新狀態
111         FSMStateID id = currentState.GetOutputState(trans);        
112 
113         //  將當前狀態編號設置爲剛剛返回的新狀態編號        
114         currentStateID = id;
115 
116         //根絕狀態編號查找狀態列表,將當前狀態設置爲查找到的狀態
117         foreach (FSMState state in fsmStates)
118         {
119             if (state.ID == currentStateID)
120             {
121                 currentState = state;
122                 break;
123             }
124         }
125     }
126 }
 1 using UnityEngine;
 2 using System.Collections;
 3 
 4 public class FSM : MonoBehaviour 
 5 {
 6         //玩家位置
 7         protected Transform playerTransform;
 8 
 9         //下一個巡邏點
10         protected Vector3 destPos;
11 
12         //巡邏點表單
13         protected GameObject[] pointList;
14 
15         //子彈信息
16         protected float shootRate;
17         protected float elapsedTime;
18 
19         protected virtual void Initialize() {}
20         protected virtual void FSMUpdate() {}
21         protected virtual void FSMFixedUpdate() {}
22 
23         //初始化信息
24         void Start()
25         {
26                 Initialize();
27         }
28 
29     // 循環執行子類FSMUpdate方法
30         void Update () 
31         {
32                 FSMUpdate();
33         }
34 
35         void FixedUpdate()
36         {
37                 FSMFixedUpdate();
38         }
39 }
 1 using UnityEngine;
 2 using System.Collections;
 3 using System.Collections.Generic;
 4 
 5 /// <summary>
 6 /// This class is adapted and modified from the FSM implementation class available on UnifyCommunity website
 7 /// The license for the code is Creative Commons Attribution Share Alike.
 8 /// It's originally the port of C++ FSM implementation mentioned in Chapter01 of Game Programming Gems 1
 9 /// You're free to use, modify and distribute the code in any projects including commercial ones.
10 /// Please read the link to know more about CCA license @http://creativecommons.org/licenses/by-sa/3.0/
11 
12 /// This class represents the States in the Finite State System.
13 /// Each state has a Dictionary with pairs (transition-state) showing
14 /// which state the FSM should be if a transition is fired while this state
15 /// is the current state.
16 /// Reason method is used to determine which transition should be fired .
17 /// Act method has the code to perform the actions the NPC is supposed to do if it磗 on this state.
18 /// </summary>
19 public abstract class FSMState
20 {
21     //字典,字典中每一項都記錄了一個「轉換-狀態」對 的信息
22     protected Dictionary<Transition, FSMStateID> map = new Dictionary<Transition, FSMStateID>();
23     //狀態編號ID
24     protected FSMStateID stateID;
25     public FSMStateID ID { get { return stateID; } }
26 
27     /// <summary>
28     /// 向字典添加項,每項是一個"轉換--狀態"對
29     /// </summary>
30     /// <param name="transition"></param>
31     /// <param name="id"></param>
32     public void AddTransition(Transition transition, FSMStateID id)
33     {
34         //檢查這個轉換(能夠看作是字典的關鍵字)是否在字典裏
35         if (map.ContainsKey(transition))
36         {
37             //一個轉換隻能對應一個新狀態
38             Debug.LogWarning("FSMState ERROR: transition is already inside the map");
39             return;
40         }
41         //若是不在字典,那麼將這個轉換和轉換後的狀態做爲一個新的字典項,加入字典
42         map.Add(transition, id);
43     }
44 
45     /// <summary>
46     /// 從字典中刪除項
47     /// </summary>
48     /// <param name="trans"></param>
49     public void DeleteTransition(Transition trans)
50     {
51         // 檢查是否在字典中,若是在,移除
52         if (map.ContainsKey(trans))
53         {
54             map.Remove(trans);
55             return;
56         }
57         //若是要刪除的項不在字典中,報告錯誤
58         Debug.LogError("FSMState ERROR: Transition passed was not on this State List");
59     }
60 
61 
62     /// <summary>
63     /// 經過查詢字典,肯定在當前狀態下,發生trans轉換時,應該轉換到新的狀態編號並返回
64     /// </summary>
65     /// <param name="trans"></param>
66     /// <returns></returns>
67     public FSMStateID GetOutputState(Transition trans)
68     {
69         return map[trans];
70     }
71 
72     /// <summary>
73     /// 用來肯定是否須要轉換到其餘狀態,應該發生哪一個轉換
74     /// </summary>
75     /// <param name="player"></param>
76     /// <param name="npc"></param>
77     public abstract void Reason(Transform player, Transform npc);
78 
79     /// <summary>
80     /// 定義了在本狀態的角色行爲,移動,動畫等
81     /// </summary>
82     /// <param name="player"></param>
83     /// <param name="npc"></param>
84     public abstract void Act(Transform player, Transform npc);
85 }
  1 using UnityEngine;
  2 using System.Collections;
  3 
  4 public class AIController : AdvancedFSM 
  5 {
  6     private int health;
  7     public Animator animator;
  8     public AnimatorStateInfo stateInfo;
  9     public float attackCd;
 10     //Initialize the Finite state machine for the NPC tank
 11     protected override void Initialize()
 12     {
 13         health = 100;
 14 
 15         elapsedTime = 0.0f;
 16         shootRate = 0.5f;
 17         attackCd = 0f;
 18 
 19         //Get the target enemy(Player)
 20         GameObject objPlayer = GameObject.FindGameObjectWithTag("Player");
 21         playerTransform = objPlayer.transform;
 22 
 23         if (!playerTransform)
 24             print("Player doesn't exist.. Please add one with Tag named 'Player'");        
 25         animator = this.GetComponentInChildren<Animator>();
 26         //Start Doing the Finite State Machine
 27         ConstructFSM();
 28     }
 29 
 30     //Update each frame
 31     protected override void FSMUpdate()
 32     {
 33         //Check for health
 34         elapsedTime += Time.deltaTime;
 35         if (attackCd > 0)
 36         {
 37             attackCd -= Time.deltaTime;
 38             if (attackCd <= 0)
 39                 attackCd = 0;
 40         }
 41     }
 42 
 43     protected override void FSMFixedUpdate()
 44     {
 45         stateInfo = animator.GetCurrentAnimatorStateInfo(0);
 46         CurrentState.Reason(playerTransform, transform);
 47         CurrentState.Act(playerTransform, transform);
 48     }
 49 
 50     public void SetTransition(Transition t) 
 51     { 
 52         PerformTransition(t);
 53         animator.SetInteger("StateI", (int)CurrentStateID);
 54     }
 55 
 56     private void ConstructFSM()
 57     {
 58         //Get the list of points
 59         pointList = GameObject.FindGameObjectsWithTag("PatrolPoint");
 60 
 61         Transform[] waypoints = new Transform[pointList.Length];
 62         int i = 0;
 63         foreach(GameObject obj in pointList)
 64         {
 65             waypoints = obj.transform;
 66             i++;
 67         }
 68 
 69         MoveState move = new MoveState();
 70         move.AddTransition(Transition.ReachPlayer,FSMStateID.Attacking);
 71 
 72         AttackState attack = new AttackState();
 73         attack.AddTransition(Transition.AttackOver,FSMStateID.Move);
 74 
 75         AddFSMState(move);
 76         AddFSMState(attack);
 77     }
 78 
 79     /// <summary>
 80     /// Check the collision with the bullet
 81     /// </summary>
 82     /// <param name="collision"></param>
 83     void OnCollisionEnter(Collision collision)
 84     {
 85         //Reduce health
 86         if (collision.gameObject.tag == "Bullet")
 87         {
 88             health -= 50;
 89 
 90             if (health <= 0)
 91             {
 92                 Debug.Log("Switch to Dead State");
 93                 SetTransition(Transition.NoHealth);                
 94             }
 95         }
 96     }
 97 
 98 
 99     
100     // Shoot the bullet    
101     public void ShootBullet()
102     {
103         if (elapsedTime >= shootRate)
104         {            
105             elapsedTime = 0.0f;
106         }
107     }
108 }
 1 using UnityEngine;
 2 using System.Collections;
 3 
 4 public class CameraFollow : MonoBehaviour
 5 {
 6     public Transform target;
 7     public Vector3 offest;
 8 
 9     void LateUpdate()
10     {
11         transform.position = target.position + offest;
12     }
13 }
相關文章
相關標籤/搜索