使用有限状态机(FSM)编写的敌人AI

时间:2022-07-07 00:19:06
 using UnityEngine;
using System.Collections; public class AttackState : FSMState
{
public AttackState()
{
stateID = FSMStateID.Attacking;
} public override void Reason(Transform player, Transform npc)
{
if (npc.GetComponent<AIController>().stateInfo.normalizedTime >= 1.0f && npc.GetComponent<AIController>().stateInfo.IsName("Attack"))
npc.GetComponent<AIController>().SetTransition(Transition.AttackOver);
} public override void Act(Transform player, Transform npc)
{
}
}
 using UnityEngine;
using System.Collections; public class DeadState : FSMState
{
public DeadState()
{
stateID = FSMStateID.Dead;
} public override void Reason(Transform player, Transform npc)
{ } public override void Act(Transform player, Transform npc)
{
Animation animComponent = npc.GetComponent<Animation>();
//animComponent.CrossFade("death");
}
}
 using UnityEngine;
using System.Collections; public class MoveState : FSMState
{
float rotateTime;
float curSpeed;
public MoveState()
{
stateID = FSMStateID.Move; curSpeed = 1.0f;
rotateTime = 0f;
} public override void Reason(Transform player, Transform npc)
{
float distance = Vector3.Distance(player.position, npc.position);
if (distance <= )
{
if (npc.GetComponent<AIController>().attackCd == 0f)
{
npc.GetComponent<AIController>().SetTransition(Transition.ReachPlayer);
npc.GetComponent<AIController>().attackCd = 5.0f;
}
}
} public override void Act(Transform player, Transform npc)
{
rotateTime -= Time.deltaTime;
if (rotateTime <= )
{
rotateTime = 3.0f;
Vector3 toTargetV3 = (player.position - npc.position).normalized;
float direction = Vector3.Dot(toTargetV3, npc.forward);
Vector3 u = Vector3.Cross(toTargetV3, npc.forward);
if (direction > ) { direction = 1f; }
if (direction < -) { direction = -1f; }
if (Mathf.Abs(direction) == 1f)
return;
direction = Mathf.Acos(direction) * Mathf.Rad2Deg;
npc.rotation = npc.rotation * Quaternion.Euler(new Vector3(, direction * (u.y >= ? - : ), ));
} float distance = Vector3.Distance(player.position, npc.position);
if (distance >= )
{
npc.GetComponent<AIController>().animator.SetFloat("Blend", 1f);
curSpeed = 2f;
}
else if (distance < && distance >= )
{
npc.GetComponent<AIController>().animator.SetFloat("Blend", 0.5f);
curSpeed = 1f;
}
else
{
npc.GetComponent<AIController>().animator.SetFloat("Blend", 0f);
curSpeed = ;
}
npc.Translate(npc.transform.forward * Time.deltaTime * curSpeed, Space.World);
}
}
 using UnityEngine;
using System.Collections;
using System.Collections.Generic; /// <summary>
/// This class is adapted and modified from the FSM implementation class available on UnifyCommunity website
/// The license for the code is Creative Commons Attribution Share Alike.
/// It's originally the port of C++ FSM implementation mentioned in Chapter01 of Game Programming Gems 1
/// You're free to use, modify and distribute the code in any projects including commercial ones.
/// Please read the link to know more about CCA license @http://creativecommons.org/licenses/by-sa/3.0/
/// </summary> //定义枚举,为可能的转换分配编号
public enum Transition
{
SawPlayer = , //看到玩家
ReachPlayer, //接近玩家
LostPlayer, //玩家离开视线
NoHealth, //死亡
AttackOver, //结束攻击
} /// <summary>
/// 定义枚举,为可能的状态分配编号ID
/// </summary>
public enum FSMStateID
{
Move = ,
Attacking, //攻击编号
Patrolling , //巡逻编号
Chasing, //追逐编号
Dead, //死亡编号
} public class AdvancedFSM : FSM
{
//FSM中的所有状态组成的列表
private List<FSMState> fsmStates;
//当前状态的编号
//The fsmStates are not changing directly but updated by using transitions
private FSMStateID currentStateID;
public FSMStateID CurrentStateID { get { return currentStateID; } }
//当前状态
private FSMState currentState;
public FSMState CurrentState { get { return currentState; } } public AdvancedFSM()
{
//新建一个空的状态列表
fsmStates = new List<FSMState>();
} /// <summary>
///向状态列表中加入一个新的状态
/// </summary>
public void AddFSMState(FSMState fsmState)
{
//检查要加入的新状态是否为空,如果空就报错
if (fsmState == null)
{
Debug.LogError("FSM ERROR: Null reference is not allowed");
} // First State inserted is also the Initial state
// the state the machine is in when the simulation begins
//如果插入的这个状态时,列表还是空的,那么将它加入列表并返回
if (fsmStates.Count == )
{
fsmStates.Add(fsmState);
currentState = fsmState;
currentStateID = fsmState.ID;
return;
} // 检查要加入的状态是否已经在列表里,如果是,报错返回
foreach (FSMState state in fsmStates)
{
if (state.ID == fsmState.ID)
{
Debug.LogError("FSM ERROR: Trying to add a state that was already inside the list");
return;
}
}
//如果要加入的状态不在列表中,将它加入列表
fsmStates.Add(fsmState);
} //从状态中删除一个状态
public void DeleteState(FSMStateID fsmState)
{
// 搜索整个状态列表,如果要删除的状态在列表中,那么将它移除,否则报错
foreach (FSMState state in fsmStates)
{
if (state.ID == fsmState)
{
fsmStates.Remove(state);
return;
}
}
Debug.LogError("FSM ERROR: The state passed was not on the list. Impossible to delete it");
} /// <summary>
/// 根据当前状态,和参数中传递的转换,转换到新状态
/// </summary>
public void PerformTransition(Transition trans)
{
// 根绝当前的状态类,以Trans为参数调用它的GetOutputState方法
//确定转换后的新状态
FSMStateID id = currentState.GetOutputState(trans); // 将当前状态编号设置为刚刚返回的新状态编号
currentStateID = id; //根绝状态编号查找状态列表,将当前状态设置为查找到的状态
foreach (FSMState state in fsmStates)
{
if (state.ID == currentStateID)
{
currentState = state;
break;
}
}
}
}
 using UnityEngine;
using System.Collections; public class FSM : MonoBehaviour
{
//玩家位置
protected Transform playerTransform; //下一个巡逻点
protected Vector3 destPos; //巡逻点表单
protected GameObject[] pointList; //子弹信息
protected float shootRate;
protected float elapsedTime; protected virtual void Initialize() {}
protected virtual void FSMUpdate() {}
protected virtual void FSMFixedUpdate() {} //初始化信息
void Start()
{
Initialize();
} // 循环执行子类FSMUpdate方法
void Update ()
{
FSMUpdate();
} void FixedUpdate()
{
FSMFixedUpdate();
}
}
 using UnityEngine;
using System.Collections;
using System.Collections.Generic; /// <summary>
/// This class is adapted and modified from the FSM implementation class available on UnifyCommunity website
/// The license for the code is Creative Commons Attribution Share Alike.
/// It's originally the port of C++ FSM implementation mentioned in Chapter01 of Game Programming Gems 1
/// You're free to use, modify and distribute the code in any projects including commercial ones.
/// Please read the link to know more about CCA license @http://creativecommons.org/licenses/by-sa/3.0/ /// This class represents the States in the Finite State System.
/// Each state has a Dictionary with pairs (transition-state) showing
/// which state the FSM should be if a transition is fired while this state
/// is the current state.
/// Reason method is used to determine which transition should be fired .
/// Act method has the code to perform the actions the NPC is supposed to do if it磗 on this state.
/// </summary>
public abstract class FSMState
{
//字典,字典中每一项都记录了一个“转换-状态”对 的信息
protected Dictionary<Transition, FSMStateID> map = new Dictionary<Transition, FSMStateID>();
//状态编号ID
protected FSMStateID stateID;
public FSMStateID ID { get { return stateID; } } /// <summary>
/// 向字典添加项,每项是一个"转换--状态"对
/// </summary>
/// <param name="transition"></param>
/// <param name="id"></param>
public void AddTransition(Transition transition, FSMStateID id)
{
//检查这个转换(可以看做是字典的关键字)是否在字典里
if (map.ContainsKey(transition))
{
//一个转换只能对应一个新状态
Debug.LogWarning("FSMState ERROR: transition is already inside the map");
return;
}
//如果不在字典,那么将这个转换和转换后的状态作为一个新的字典项,加入字典
map.Add(transition, id);
} /// <summary>
/// 从字典中删除项
/// </summary>
/// <param name="trans"></param>
public void DeleteTransition(Transition trans)
{
// 检查是否在字典中,如果在,移除
if (map.ContainsKey(trans))
{
map.Remove(trans);
return;
}
//如果要删除的项不在字典中,报告错误
Debug.LogError("FSMState ERROR: Transition passed was not on this State List");
} /// <summary>
/// 通过查询字典,确定在当前状态下,发生trans转换时,应该转换到新的状态编号并返回
/// </summary>
/// <param name="trans"></param>
/// <returns></returns>
public FSMStateID GetOutputState(Transition trans)
{
return map[trans];
} /// <summary>
/// 用来确定是否需要转换到其他状态,应该发生哪个转换
/// </summary>
/// <param name="player"></param>
/// <param name="npc"></param>
public abstract void Reason(Transform player, Transform npc); /// <summary>
/// 定义了在本状态的角色行为,移动,动画等
/// </summary>
/// <param name="player"></param>
/// <param name="npc"></param>
public abstract void Act(Transform player, Transform npc);
}
 using UnityEngine;
using System.Collections; public class AIController : AdvancedFSM
{
private int health;
public Animator animator;
public AnimatorStateInfo stateInfo;
public float attackCd;
//Initialize the Finite state machine for the NPC tank
protected override void Initialize()
{
health = ; elapsedTime = 0.0f;
shootRate = 0.5f;
attackCd = 0f; //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'");
animator = this.GetComponentInChildren<Animator>();
//Start Doing the Finite State Machine
ConstructFSM();
} //Update each frame
protected override void FSMUpdate()
{
//Check for health
elapsedTime += Time.deltaTime;
if (attackCd > )
{
attackCd -= Time.deltaTime;
if (attackCd <= )
attackCd = ;
}
} protected override void FSMFixedUpdate()
{
stateInfo = animator.GetCurrentAnimatorStateInfo();
CurrentState.Reason(playerTransform, transform);
CurrentState.Act(playerTransform, transform);
} public void SetTransition(Transition t)
{
PerformTransition(t);
animator.SetInteger("StateI", (int)CurrentStateID);
} private void ConstructFSM()
{
//Get the list of points
pointList = GameObject.FindGameObjectsWithTag("PatrolPoint"); Transform[] waypoints = new Transform[pointList.Length];
int i = ;
foreach(GameObject obj in pointList)
{
waypoints = obj.transform;
i++;
} MoveState move = new MoveState();
move.AddTransition(Transition.ReachPlayer,FSMStateID.Attacking); AttackState attack = new AttackState();
attack.AddTransition(Transition.AttackOver,FSMStateID.Move); AddFSMState(move);
AddFSMState(attack);
} /// <summary>
/// Check the collision with the bullet
/// </summary>
/// <param name="collision"></param>
void OnCollisionEnter(Collision collision)
{
//Reduce health
if (collision.gameObject.tag == "Bullet")
{
health -= ; if (health <= )
{
Debug.Log("Switch to Dead State");
SetTransition(Transition.NoHealth);
}
}
} // Shoot the bullet
public void ShootBullet()
{
if (elapsedTime >= shootRate)
{
elapsedTime = 0.0f;
}
}
}
 using UnityEngine;
using System.Collections; public class CameraFollow : MonoBehaviour
{
public Transform target;
public Vector3 offest; void LateUpdate()
{
transform.position = target.position + offest;
}
}