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 }