使用有限状态机(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 }

 

posted @ 2016-04-28 20:37  bzyzhang  阅读(2920)  评论(0编辑  收藏  举报