Unity有限状态机

11 篇文章 0 订阅
2 篇文章 0 订阅

一、引言

在游戏开发中,经常会遇到游戏角色或实体具有多种状态,并且在不同状态之间需要切换的情况。例如,一个角色可能处于行走、奔跑、跳跃等不同的状态,并且根据玩家的输入或游戏逻辑,在这些状态之间进行切换。为了管理这些状态及其之间的转换,我们可以使用有限状态机(Finite State Machine,简称FSM)技术。Unity作为一款强大的游戏开发引擎,也提供了丰富的工具和接口来支持有限状态机的实现。本文将详细介绍Unity中的有限状态机技术,并探讨其实现方法和最佳实践。

二、有限状态机的基本概念与结构

有限状态机是一种数学模型,用于描述系统在不同状态下的行为以及状态之间的转换。它由一组有限的状态和一组转换规则组成。每个状态表示系统在某一时刻的特定行为,而转换规则定义了从一个状态切换到另一个状态的条件和动作。有限状态机可以用于模拟和控制系统的行为,例如游戏角色的状态管理、用户界面交互等。

在Unity中,有限状态机通常由以下几个部分组成:

  1. 状态集(State Set):包含所有可能的状态,每个状态都对应一个类或枚举类型。
  2. 转换规则(Transition Rules):定义了从一个状态切换到另一个状态的条件和动作。这些规则通常由条件判断语句和相应的行为实现组成。
  3. 当前状态(Current State):表示系统在某一时刻所处的状态。这个变量通常由一个枚举类型或类的实例来表示。
  4. 状态转换函数(State Transition Functions):根据转换规则,判断是否满足从当前状态切换到下一个状态的条件下,执行相应的动作并更新当前状态。

三、Unity中实现有限状态机的方法

在Unity中实现有限状态机有多种方法,下面介绍两种常用的实现方式:

  1. 状态模式(State Pattern):状态模式是一种行为设计模式,用于管理对象的状态并根据状态的变化改变对象的行为。在Unity中,我们可以使用C#编写状态模式来实现有限状态机。每个状态可以定义为一个类,该类包含该状态下的行为和转换到其他状态的逻辑。游戏实体包含一个当前状态的引用,并根据转换规则在不同的状态之间进行切换。
  2. 动画控制器(Animator Controller):Unity的动画系统提供了强大的工具来管理游戏角色的动画和状态。我们可以使用动画控制器来创建有限状态机,将不同的状态定义为不同的动画状态,并使用动画过渡来控制状态之间的切换。动画控制器还支持条件过渡和参数驱动过渡,可以根据游戏逻辑和玩家输入来动态控制状态的转换。

下面以状态模式为例,介绍在Unity中实现有限状态机的步骤:

  1. 定义状态集:首先,我们需要定义所有可能的状态,每个状态可以是一个类或枚举类型。例如,我们可以定义一个名为State的枚举类型,包含IdleWalkingRunningJumping等状态。
  2. 实现状态转换函数:接下来,我们需要实现状态转换函数来判断是否满足从当前状态切换到下一个状态的条件下,执行相应的动作并更新当前状态。例如,我们可以实现一个名为TransitionToWalking的函数来判断是否满足从Idle状态切换到Walking状态的条件下,执行相应的动作并更新当前状态。
  3. 管理状态转换:在实际的游戏逻辑中,我们需要根据游戏输入或逻辑判断来触发状态转换。例如,当玩家按下前进键时,我们需要判断当前状态是否为Idle,如果是,则调用TransitionToWalking函数来进行状态转换。

四、示例代码

下面是一个简单的示例代码,展示了如何在Unity中使用有限状态机设计敌人AI。

using System.Collections;  
using System.Collections.Generic;  
using UnityEngine;  
  
public class StateMachine : MonoBehaviour  
{  
    public enum State  //声明状态枚举
    {  
        Idle,  
        Walking,  
        Running,  
        Jumping  
    }  
  
    private State currentState;  //当前状态变量
  
    private void Start()  
    {  
        // 初始化当前状态为 Idle  
        currentState = State.Idle;  
    }  
  
    private void Update()  
    {  
        // 根据当前状态执行相应的行为  
        switch (currentState)  
        {  
            case State.Idle:  
                Idle();  
                break;  
            case State.Walking:  
                Walking();  
                break;  
            case State.Running:  
                Running();  
                break;  
            case State.Jumping:  
                Jumping();  
                break;  
            default:  
                Debug.LogError("Invalid state");  
                break;  
        }  
    }  
  
    // 状态转换函数  
    public void TransitionToWalking()  
    {  
        currentState = State.Walking;  
    }  
  
    public void TransitionToRunning()  
    {  
        currentState = State.Running;  
    }  
  
    public void TransitionToJumping()  
    {  
        currentState = State.Jumping;  
    }  
  
    // 状态行为实现  
    private void Idle()  
    {  
        Debug.Log("Idle state");  
        // 添加空操作或其他行为实现  
    }  
  
    private void Walking()  
    {  
        Debug.Log("Walking state");  
        // 添加步行行为实现或其他操作  
    }  
  
    private void Running()  
    {  
        Debug.Log("Running state");  
        // 添加跑步行为实现或其他操作  
    }  
  
    private void Jumping()  
    {  
        Debug.Log("Jumping state");  
        // 添加跳跃行为实现或其他操作  
    }  
}

这是一个简单的有限状态机实现方法,实现起来很简单,逻辑也很简单。但耦合性较高,不够灵活。
可以改进一下,我们可以将以上部分拆解开来,分别是 具体的对象控制+状态转换+状态下的逻辑执行。它们分别负责对象的控制逻辑,对象的状态转换逻辑,对象的状态方法执行。
改进之后代码
FSM管理器(核心部分,状态转换)

public abstract class IState //创建抽象类
{
    public abstract void  OnEnter(); //声明抽象方法,抽象类不可实例化,子类需要实现所有抽象方法
    public abstract void OnUpdate();
    public abstract void OnExit();
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public enum StateType//状态枚举
{
    Idle,
    Attack,
    Die,
}
public class FSM
{
    public BaseState curState;
    public Dictionary<StateType,IState> states //状态和对应执行方法的字典,这里还涉及到多态概念(基类引用会调用子类方法)
    public FSM()//创建构造函数,如果没有显式的构造函数,编译器会为它提供一个默认的无参构造函数,并且不执行任何特殊的操作。
    {
        this.states = new Dictionary<StateType, IState>();
    }
    public void AddState(StateType stateType,IState state)//添加状态方法
    {
        if(states.ContainsKey(stateType))
        {
            Debug.Log(stateType+"已存在,不可再次添加");
            return;
        }
        states.Add(stateType,state)
    }
    public void SwitchState(StateType stateType)//切换状态方法
    {
        if(!states.ContainsKey(stateType))
        {
            Debug.Log(stateType+"不存在,不可切换");
            return;
        }
        if(curState!=null)
        {
            curState.OnExit();
        }
        curState = states[stateType];
        curState.OnEnter();
    }

    public void OnUpdate()
    {
        curState.OnUpdate();
    }
}

状态方法执行

//Idel状态
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class AI_IdleState:IState //继承抽象类IState
{
    public AI_IdleState(FSM fsm) //创建构造函数,将AIEnemy中的fsm传递进来
    {
        this.fsm = fsm;
    }
    public override void OnEnter() //重写IState中的方法
    {
        //进入行走状态逻辑
    }
    public override void OnUpdate()
    {
        //执行行走逻辑
        if(发现玩家)
        {
            fsm.SwitchState(StateType.Attack);//状态切换
        }

    }
    public override void OnExit()
    {
        //离开行走状态逻辑
    }
}
//死亡状态
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class AI_DieState:IState
{
    public AI_IdleState(FSM fsm)
    {
        this.fsm = fsm;
    }
    public override void OnEnter()
    {
        //进入死亡状态逻辑
    }
    public override void OnUpdate()
    {
        //执行死亡状态逻辑
    }
    public override void OnExit()
    {
        //离开死亡状态逻辑
    }
}
//攻击状态
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class AI_AttackState:IState
{
    public AI_IdleState(FSM fsm)
    {
        this.fsm = fsm;
    }
    public override void OnEnter()
    {
        //进入攻击状态逻辑
    }
    public override void OnUpdate()
    {
        //执行攻击逻辑
    }
    public override void OnExit()
    {
        //离开攻击状态逻辑
    }
}

敌人具体的对象控制

//敌人具体的对象控制
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class AIEnemy : MonoBehaviour 
{
    private FSM fsm;

    private void Start() //初始化fsm数据
    {
        fsm = new FSM();
        fsm.AddState(StateType.Idle,new AI_IdleState());
        fsm.AddState(StateType.Attack,new AI_AttackState());
        fsm.AddState(StateType.DieState,new AI_DieState());
        fsm.SwitchState(State.Idle);
    }

    private void Update()
    {
        
    }

}

首先在对象控制类中创建FSM管理器,并初始化FSM,向FSM中添加状态,并通过FSM切换到Idel状态。然后执行AI_IdleState类中的各种方法。在Idle状态逻辑下,如果发现玩家,则通过FSM切换到攻击状态,然后执行AI_AttackState类中的各种方法。这种方式将角色与状态切换和状态逻辑分解开,代码灵活性和拓展性更好。

五、FSM优点和缺点
有限状态机在游戏开发中有很多用处,它可以用于管理游戏实体的行为和状态转换。以下是一些有限状态机的应用场景:

  1. 游戏角色控制:有限状态机可以用于实现游戏角色的行为控制,例如实现待机、行走、奔跑、跳跃等状态,并根据玩家输入或游戏逻辑来触发状态转换。
  2. 战斗系统:在角色扮演游戏或动作游戏中,有限状态机可以用于实现战斗系统。例如,敌人的行为可以根据当前的状态(如攻击、防御、逃跑等)进行管理,并根据玩家的行为或战斗进展来触发状态转换。
  3. 人工智能模拟:有限状态机可以用于模拟人工智能体的行为。例如,有限状态机可以用于实现机器人的移动、感知、决策等行为,根据环境信息和内部状态来触发状态转换。
  4. 游戏逻辑控制:有限状态机可以用于控制游戏的逻辑流程。例如,游戏关卡的流程可以根据有限状态机的状态转换来实现,根据关卡进度或游戏事件来触发状态转换。
  5. 动画控制:有限状态机可以用于控制动画的播放。例如,角色的动作可以根据当前的状态(如站立、奔跑、跳跃等)来选择相应的动画进行播放,并根据状态转换来控制动画的过渡。

有限状态机虽然具有很多优点,如简单易懂、灵活性高等,但也存在一些缺点,主要包括以下几点:

  1. 状态数量限制:有限状态机的状态数量是有限的,因此当需要管理的状态数量很多时,就需要使用更复杂的状态机或者其他技术进行管理。这会增加系统的复杂度和实现难度。
  2. 复杂性:当状态机的状态转换规则和条件变得复杂时,有限状态机的实现和维护也会变得更加困难。特别是当存在多个状态之间的转换和嵌套时,状态机的设计和实现会变得更加复杂。
  3. 缺乏适应性:有限状态机的行为是预先定义好的,因此当需要添加新的状态或修改现有状态时,需要重新设计和实现状态机。这使得有限状态机在面对需求变化时缺乏适应性。
  4. 难以处理并发事件:有限状态机通常只能处理单个事件或输入,当需要处理多个并发事件或输入时,就需要使用更复杂的技术或机制进行处理。这会增加系统的复杂度和实现难度。
  5. 代码量大:随着状态数量的增加和复杂性的提高,有限状态机的代码量也会逐渐增加。这会增加代码的维护成本和错误出现的概率。
  • 21
    点赞
  • 45
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值