【Unity人工智能】实现有限状态机FSM

有限状态机FSM导图

在这里插入图片描述

  1. 第一步
    开发FSMState核心类

首先添加两个枚举类,用来定义状态和转换条件

public enum StateID
{
    nullStateID,
    walk,
    chase
}

public enum Transition
{
    nullTransition,
    lookEnemy,
    lostEnemy
}
public abstract class FSMState
{
    protected StateID _StateID;
    public StateID stateID { get { return _StateID; } }

    //存储转换状态的集合
    protected Dictionary<Transition, StateID> map = new Dictionary<Transition, StateID>();

    //添加转换条件
    public void AddTransition(Transition trans,StateID id)
    {
        if(trans == Transition.nullTransition)
        {
            Debug.LogError("添加的转换条件为空");return;
        }
        if(id == StateID.nullStateID)
        {
            Debug.LogError("添加到状态为空");return;
        }
        if (map.ContainsKey(trans))
        {
            Debug.LogError("添加的转换条件已存在");return;
        }
        map.Add(trans, id);
    }

    //删除转换条件
    public void DeleteTransition(Transition trans)
    {
        if(trans == Transition.nullTransition)
        {
            Debug.LogError("删除的条件为空");return;
        }
        if (!map.ContainsKey(trans))
        {
            Debug.LogError("删除的条件不存在");return;
        }

        map.Remove(trans);
    }

    //根据转换条件得到响应状态
    public StateID GetOutputState(Transition trans)
    {
        if (map.ContainsKey(trans))
        {
            return map[trans];
        }
        return StateID.nullStateID;
    }

    //在特定状态需要做的事
    public abstract void Act(GameObject npc);
    //判断转换条件
    public abstract void Reason(GameObject npc);

}

2.第二步
开发FSMSystem核心类

public class FSMSystem
{
	//定义字典,用来存放状态ID所对应的状态
    private Dictionary<StateID, FSMState> states = new Dictionary<StateID, FSMState>();
	
	//定义当前状态
    private StateID currentStateID;
    private FSMState currentFSMState;

	//添加状态
    public void AddState(FSMState s)
    {   
        if(s == null)
        {
            Debug.LogError("添加的状态为空");return;
        }

        if(currentFSMState == null)
        {
            currentFSMState = s;
            currentStateID = s.stateID;
        }
        if (states.ContainsKey(s.stateID))
        {
            Debug.LogError("已存在该状态");return;
        }
        states.Add(s.stateID, s);

    }

	//删除状态
    public void DeleteState(StateID id)
    {
        if(id == StateID.nullStateID)
        {
            Debug.LogError("状态为空");return;
        }
        if (!states.ContainsKey(id))
        {
            Debug.LogError("删除状态不存在");return;
        }
        states.Remove(id);
    }

	//根据转换条件实现状态的转换
    public void PerformTransition(Transition trans)
    {
        if(trans == Transition.nullTransition)
        {
            Debug.LogError("无法执行空的转换条件");return;
        }
        StateID id = currentFSMState.GetOutputState(trans);
        if(id == StateID.nullStateID)
        {
            Debug.LogError("当前转台无法根据转换条件进行转换");return;
        }
        if (!states.ContainsKey(id))
        {
            Debug.LogError("当前状态不存在");return;
        }
        FSMState state = states[id];
        currentStateID = id;
        currentFSMState = state;
    }
}

3.第三步
FSMState类添加构造方法

public FSMSystem system;

    public FSMState(FSMSystem system)
    {
        this.system = system;
    }

4.第四步
开发子状态Walk状态Chase状态

public class WalkState : FSMState
{
    private List<Transform> path = new List<Transform>();
    private int index = 0;
    //Unity中需创建一个Enemy,需要包含Transform组件
    private Transform enemy;

    public WalkState(FSMSystem system) : base(system)
    {
        _StateID = StateID.walk;
        //Path为博主在Unity中定义的一个空的GameObject,其中子物体包含了三个点,只要有Transform组件即可
        Transform pathTransform = GameObject.Find("Path").transform;
        Transform[] chindren = pathTransform.GetComponentsInChildren<Transform>();
        foreach (Transform item in chindren)
        {
            if(item != pathTransform)
            {
                path.Add(item);
            }
        }
        enemy = GameObject.Find("Enemy").transform;
    }

    public override void Act(GameObject npc)
    {
        npc.transform.LookAt(path[index].position);
        npc.transform.Translate(Vector3.forward*Time.deltaTime*10);
        if (Vector3.Distance(npc.transform.position, path[index].position) < 0.5)
        {
            index++;
            index %= path.Count;
        }
    }

    public override void Reason(GameObject npc)
    {
        if (Vector3.Distance(npc.transform.position, enemy.position) < 3)
        {
            system.PerformTransition(Transition.lookEnemy);
        }
    }
}
public class ChaseState : FSMState
{
    private Transform enemyTransform;
    public ChaseState(FSMSystem system) : base(system)
    {
        //定义当前类的状态
        _StateID = StateID.chase;
        enemyTransform= GameObject.Find("Enemy").transform;

    }

    public override void Act(GameObject npc)
    {
        npc.transform.LookAt(enemyTransform.position);
        npc.transform.Translate(Vector3.forward * Time.deltaTime * 20);
    }

    public override void Reason(GameObject npc)
    {
        if (Vector3.Distance(enemyTransform.position, npc.transform.position) > 6)
        {
            system.PerformTransition(Transition.lostenemy);
        }
    }
}

5.第五步
FSMSystem添加Update方法,用来每一帧执行Act和Reason方法

public void update(GameObject npc)
    {
        currentFSMState.Act(npc);
        currentFSMState.Reason(npc);
    }

6.第六步
开发Player类,并挂载到Unity中Player游戏物体上

public class Player : MonoBehaviour
{
    private FSMSystem fsm;
    // Start is called before the first frame update
    void Start()
    {
        fsm = new FSMSystem();

        FSMState walkState = new WalkState(fsm);
        walkState.AddTransition(Transition.lookEnemy, StateID.chase);

        FSMState chaseState = new ChaseState(fsm);
        chaseState.AddTransition(Transition.lostEnemy, StateID.walk);


        fsm.AddState(walkState);
        fsm.AddState(chaseState);

    }

    // Update is called once per frame
    void Update()
    {
        fsm.update(this.gameObject);
    }
}

完成以上步骤即可初步实现Player自动行走,当距离范围内有敌人时便会追击敌人,当敌人离远了,又会回到原来的地点行走的功能。

需要添加其他状态只需要写好状态类继承自FSMState,重写Act和Reason方法。然后在Player里将状态添加到状态机里即可。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值