Unity简单实现FSM有限状态机

前言

这段时间写游戏遇到实现敌人AI的需求,打算使用FSM。当然无论是FSM、行为树、亦或者简单粗暴的ifelse都能制作AI,但是考虑到FSM比ifelse、switch架构更清晰方便管理,比行为树更好把控(毕竟自己纯手撸代码),最后决定使用FSM。

代码多处使用到里氏替换原则,不了解的同学可以先了解一下更便于理解。

核心代码

FSM管理类

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Linq;

//AI状态
public enum FSMStateType
{
    Idle, Patrol, Seek, Attack,LookAt, Hit, Death
}

public class FSM : MonoBehaviour
{
	//这里使用list主要考虑到有可能需要状态并行
    private List<IState> currentState = new List<IState>();
    //存储所有状态
    protected Dictionary<FSMStateType, IState> states = new Dictionary<FSMStateType, IState>();
	
	//初始化状态机,传入所需状态并切换到初始状态
    protected void InitFSM(Dictionary<FSMStateType, IState> states, List<FSMStateType> startState)
    {
        this.states = states;
        TransitionState(startState);
    }

	//执行所有状态的OnUpdate方法
    void Update()
    {
        if(currentState.Count != 0)
        {
            for (int i = 0; i < currentState.Count; i++)
            {
                currentState[i].OnUpdate();
            }
        }
    }
	
	//状态切换
    public void TransitionState(List<FSMStateType> type)
    {
    	//执行当前状态的OnExit
        if (currentState != null)
            currentState.ForEach(_ => _.OnExit());
		
		//切换成传入的新状态
        currentState.Clear();
        List<IState> stateList = new List<IState>();
        type.ForEach(_ => stateList.Add(states[_]));
        currentState = stateList;
		
		//执行新状态的OnEnter
        currentState.ForEach(_ => _.OnEnter());
    }
}

IState类,所实现的具体状态类需集成该接口

public interface IState
{
    void OnEnter();

    void OnUpdate();

    void OnExit();
}

实际应用

具体的AI类,需继承FSM类。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Enemy1AI : FSM
{
	//实现的状态类
    private SeekState seekState;
    private EnemyAttackState attackState;
    private LookAtTargetState lookAtState;
    private EnemyDeathState deathState;

    private void Start()
    {
        seekState = GetComponent<SeekState>();
        attackState = GetComponent<EnemyAttackState>();
        lookAtState = GetComponent<LookAtTargetState>();
        deathState = GetComponent<EnemyDeathState>();
		
		//初始化FSM
        InitFSM(new Dictionary<FSMStateType, IState>() { 
            { FSMStateType.Seek, seekState },
            { FSMStateType.Attack, attackState },
            { FSMStateType.LookAt,lookAtState},
            { FSMStateType.Death,deathState}
        }, new List<FSMStateType>() { FSMStateType.Seek });
    }
}

具体实现状态,这里是实现了一个寻路状态。注意继承IState接口。

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;

public class SeekState : MonoBehaviour,IState
{
    private NavMeshAgent agent;

    public string tagStr; //根据标签寻找
    public Transform target; //目标
    public Vector3 targetPos; //目标点
    public float arriveDis; //距离多少为到达
    public float speed; //移速
    public float acceleration; //加速度
    public float angularSpeed; //角速度
    public List<string> nextState; //完成后是否切换到下一个状态

    bool isComplete;

    void Start()
    {
        agent = GetComponent<NavMeshAgent>();
    }

    public void OnEnter()
    {
        isComplete = false;

        if (tagStr != string.Empty)
        {
            target = GameObject.FindWithTag(tagStr).transform;
        }
    }

    public void OnUpdate()
    {
        //Debug.Log(Vector3.Distance(target.position, transform.position));
        if (isComplete)
            return;

        if (target != null)
        {
            DoSeek(target.position);
        }
        else
        {
            DoSeek(targetPos);
        }
    }

    public void OnExit()
    {
    }

    void DoSeek(Vector3 pos)
    {
        agent.stoppingDistance = arriveDis;
        agent.angularSpeed = angularSpeed;
        agent.speed = speed;
        agent.acceleration = acceleration;
        agent.SetDestination(pos);

        if (Vector3.Distance(pos, transform.position) <= arriveDis)
        {
            //Debug.Log(Vector3.Distance(pos, transform.position));
            //Debug.Log("完成");

            isComplete = true;

            if (nextState.Count != 0)
            {
                List<FSMStateType> temp = new List<FSMStateType>();
                nextState.ForEach(_ => temp.Add((FSMStateType)Enum.Parse(typeof(FSMStateType), _)));

				//由于里氏替换原则我们不需要获取Enemy1AI直接获取FSM即可
                GetComponent<FSM>().TransitionState(temp);
            }
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

我寄人间雪满头丶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值