首页 > 解决方案 > 需要帮助了解状态机 (C#/Unity)

问题描述

我为此苦苦挣扎了几个星期,需要帮助来理解和为我的游戏构建状态机。

我需要的是这样的:

状态机图

基本上,FSM 类仅用于更改状态:

public class FSM: MonoBehaviour

public StateHandler currentState { get; private set; }

private void Start()
{
    currentState.OnEnterState();
}

public void ChangeState(StateHandler newState)
{
    currentState.OnExitState();
    currentState = newState;
    currentState.OnEnterState();
}

状态类是这样的:

public class StateHandler : FSM
public virtual void OnEnterState()
{

}

public virtual void OnExitState()
{

}

public virtual void StateLogicUpdate()
{

}

public virtual void StatePhysicsUpdate()
{

}

void Update()
{
    StateLogicUpdate();
}

void FixedUpdate()
{
    StatePhysicsUpdate();
}

State Attack 类或任何其他状态与 State 类相同,但具有覆盖方法和实际代码,没有更新方法。

我有几个问题我还不明白:

  1. 如何存储状态?要更改它们,我需要从某个地方获取它们并将它们全部放在我的游戏对象上将使它们同时工作。
  2. 这件事甚至会这样工作吗?)
  3. 在运行敌人行为状态时存储我需要访问的敌人数据的最佳方式是什么?只是敌人游戏对象上的另一个单一行为脚本?

如果您不介意,请像我很愚蠢一样回答。:) 非常感谢!

标签: c#unity3d

解决方案


我不知道统一,但我知道状态机。

首先,您的状态机图看起来很糟糕。“状态”可以被视为对象,但您的矩形之一表示“抽象类”。您永远不能拥有抽象类的对象。

无论如何。就像这个词所说的那样,“状态”存储对象或系统的状态。这是一种记忆形式。基于该状态和外部变量,事情可能会发生

代码中最简单的状态机形式使用枚举之类的东西

using System;

State currentState = State.Idle;

while (true)
{
    switch (currentState)
    {
        case State.Idle:
            Console.WriteLine("Doing Nothing");
            if (/* some external trigger is */true) currentState = State.Loading;
            break;
        case State.Loading:
            Console.WriteLine("Loading");
            if (/* loading issue is true, but now*/false)
            {
                Console.Error.WriteLine("Loading failed.");
                currentState = State.Idle;
            }
            if (/*loading finished is */true) currentState = State.Saving;
            break;
        case State.Saving:
            Console.WriteLine("Saving");
            // etc
            if (/*saving finished is */true) currentState = State.Idle;
            break;
    }
}

enum State
{
    Idle,
    Loading,
    Saving,
}

但是,您似乎想要一个更复杂的系统,在进入或退出特定状态时可能会发生一些事情。这需要更复杂的状态类型,可以定义为抽象类

注意:这可能不是最好的解决方案,只是为了说明

(它甚至可能容易出错)

internal abstract class State
{
    public virtual void OnEnter() { } //optional
    public abstract void LogicUpdate(); // required.
    public abstract void PhysicsUpdate(); // required.
    public virtual void OnExit() { } // optional
}

然后,您的每个状态都可以是这种类型的实现。例如:

internal sealed class IdleState : State
{
    public override void OnEnter()
    {
        Console.WriteLine("IdleEnter");
    }
    public override void LogicUpdate()
    {
        Console.WriteLine("IdleLogic");
    }
    public override void PhysicsUpdate()
    {
        Console.WriteLine("IdlePhysics");
    }
    // public override void OnExit() <-- not used in his example, as its optional
}

internal sealed class SaveState : State
{
    public override void LogicUpdate()
    {
        Console.WriteLine("SaveLogic");
    }
    public override void PhysicsUpdate()
    {
        Console.WriteLine("SavePhysics");
    }
    public override void OnExit()
    {
        Console.WriteLine("SaveExit");
    }
}

在这种情况下,你的 FSM 几乎是正确的

internal class FSM {

    private State _currentState = new IdleState();

    public void Handler() // called from main loop
    {
        _currentState.LogicUpdate();
        _currentState.PhysicsUpdate();
        if (_currentState is IdleState)
        {
            if (/*a certain condition is*/ true) ChangeState(new SaveState());
            // remember to call ChangeState.. dont set _currentState directly
            // (might be solved with better encapsulation...)
        }
        else if (_currentState is SaveState)
        {
            if (/*a certain condition is*/ true) ChangeState(new IdleState());
        }
    }

    public void ChangeState(State newState)
    {
        _currentState.OnExit();
        _currentState = newState;
        _currentState.OnEnter();
    }
}

然后你只需要添加一个主循环:

var fsm = new FSM();

while (true)
{
    fsm.Handler();
}

推荐阅读