有限状态机可以为敌人提供一个简单的AI系统,它能够允许我们手动的去操作单独编写每个状态,并为其播放动画,但是需要玩家控制的player并不太适合有限状态机,玩家的输入事件太过复杂
编写一个IState接口,让所有的状态都去实现这三个接口,
接口中定义三个抽象函数,分别是:
OnEnter() :在每次进入该状态时会调用一次,
OnExecute() : 每帧执行一次
OnExit(): 退出状态时执行一次
public interface IState{
void onEnter();
void OnExecute();
void OnExit();
}
完善有限状态机脚本
1.定义枚举类型的所需状态
2.定义我们所需要的类,并且时期能够被序列化[Serliazeable]
using System; //使用[Serializable]所需要的命名空间
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public enum StateType{
Idle, Attack, Patrol
}
[Serializable]
public class Parameter{
public float idleTime;
public Animation animator;
public Transform target;
}
public class FSM : MonoBevirour{
public Parameter parameter;
public IState currentState ;
private Dictionary<StateType, IState> dict = new Dictionart<StateType, IState>();
void Start(){
dict.Add(StateType.Idle, new IdleState(this));
dict.Add(StateType.Attack, new AttackState(this));
dict.Add(StateType.Patrol, new PatrolState(this));
TransitionState(StateType.Idle);
}
//物理运动建议使用FixedUpdate 函数来更新
void FixedUpdate(){
currentState.OnExecute();
}
//提供一个公共函数来转换状态
void TransitionState(StateType targetState){
if(currentState != null){
currentState.OnExit();
}
currentState = dict[targetState];
currentState.OnEnter();
}
}
接下来让具体状态类继承接口并且去实现抽象函数
可以把所有的具体状态放在一个文件中修改
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class IdleState : IState{
private FSM manager;
private Parameter parameter;
private float timer = 0;
public IdleState(FSM manager)
{
this.manager = manager;
this.parameter = manager.parameter;
}
public void OnEnter()
{
parameter.animator.SetTrigger("idle");
}
public void OnExecute()
{
// parameter.animator.SetTrigger("idle");
timer += Time.deltaTime;
if(timer > parameter.idleTime){
manager.TransitionState(StateType.Patrol);
}
if(parameter.target)
{
manager.TransitionState(StateType.Attack);
}
}
public void OnExit()
{
timer = 0;
}
}
//巡逻
public class PatrolState : IState
{
private FSM manager;
private Parameter parameter;
private int patrolPosition = 0;
public PatrolState(FSM manager)
{
this.manager = manager;
this.parameter = manager.parameter;
}
public void OnEnter()
{
parameter.animator.SetBool("isRun",true);
}
public void OnExecute()
{
//如果发现了玩家,并追击范围
//note : 应该主动撤销target的方式来节俭判定
if(parameter.target)
{
manager.TransitionState(StateType.Chase);
}
//使敌人持续朝向下一个巡逻点
manager.FlipTo(parameter.patrolPoints[patrolPosition]);
// Debug.Log("Patrol" + parameter.animator.GetBool("isRun"));
manager.transform.position = Vector2.MoveTowards(manager.transform.position,
parameter.patrolPoints[patrolPosition].position, parameter.moveSpeed * Time.deltaTime);
//向量类中有直接计算距离的函数真是太棒了
if(Vector2.Distance(manager.transform.position, parameter.patrolPoints[patrolPosition].position ) < 0.1f)
{
manager.TransitionState(StateType.Idle);
}
}
public void OnExit()
{
parameter.animator.SetBool("isRun",false);
patrolPosition ++;
if(patrolPosition >= parameter.patrolPoints.Length){
patrolPosition = 0;
}
}
}
//攻击
public class AttackState : IState {
private FSM manager;
private Parameter parameter;
private AnimatorStateInfo info;
public AttackState(FSM manager){
this.manager = manager;
this.parameter = manager.parameter;
}
public void OnEnter()
{
parameter.animator.Play("Attack");
}
public void OnExecute()
{
//得到动画器的播放状态
info = parameter.animator.GetCurrentAnimatorStateInfo(0);
if(info.normalizedTime > .95f){
manager.TransitionState(StateType.Chase);
}
}
public void OnExit()
{
}
}
代码仅提供思路,由于是默写的,不能确保没有错误