#state #state-machine #stack #state-management #control-flow #machine #game-state

solstack

通过简单的状态堆栈机,管理您的应用程序或游戏的控制流

5 个不稳定版本

0.3.2 2023 年 3 月 28 日
0.3.1 2023 年 2 月 2 日
0.3.0 2023 年 1 月 30 日
0.2.0 2023 年 1 月 29 日
0.1.0 2023 年 1 月 28 日

449游戏开发

每月 28 次下载

MIT 许可证

30KB
219

索堆栈

索堆栈 是一个库,通过 状态堆栈机 管理应用程序的控制流。

Stack struct 存储任意数量的 State。当该 Stack 被触发时,它执行最顶层 State 的主要方法。这意味着只有 Stack 顶部的 State 被执行,而下面的则实际上 暂停。例外的是 Stateon_shadow_tick 方法,在 v0.3.0 中引入,它总是独立于 StateStack 上的位置执行。

项目处于早期开发阶段,可能会有所变化!在更新之前请查看变更日志。有些文本可能暗示某些事情已经完成,但实际上并没有,比如《索堆栈手册》。尽管如此,可能会有一些错误,但测试应该保证错误数量很少。

概述

State 拥有您需要的所有方法。以下是一个概述

  • on_start — 当此 State 首次推入 Stack 时执行。
  • on_stop — 当此 StateStack 中弹出时执行。
  • on_pause — 当另一个 State 被推入 Stack 并在顶部覆盖此 State 时执行。
  • on_resume —— 当此之上的所有 StateStack 中弹出时执行。
  • on_tick —— 每次调用持有此 StateStacktick 方法时执行。
  • on_shadow_tick —— 与 on_tick 相同,但总是独立于此 StateStack 中的位置运行。

要在 State 之间跳转,您需要在 on_tick 或类似方法中返回一个 Trans 选项枚举,请求 Stack 在下一个时钟周期执行此类转换。以下是一个概述

  • Trans::None —— 请求 Stack 不做任何事情。
  • Trans::Quit —— 请求 Stack 弹出它持有的所有 State
  • Trans::Push(Box::new(State)) —— 请求 Stack 在顶部 Push 提供的 State
  • Trans::Pop —— 请求 Stack 弹出它持有的最顶层的 State,并删除它。
  • Trans::Replace(Box::new(State)) —— 请求 Stack 弹出一次,然后 Push 提供的 State
  • Trans::Isolate(Box::new(State)) —— 请求 Stack 弹出所有内容,然后 Push 提供的 State

可以直接请求 Stack 或通过返回一个 Trans 来请求转换,从 Stateon_tickon_shadow_tick 方法中返回。

特性

  • 易于实现的 State trait

  • 易于使用的 State Stack Machine struct

  • 易于使用的 Transitions between States 集合。

  • v0.3.0 中新增

    • on_shadow_tickState 中提供;类似于 on_tick,但 始终独立于 StateStack 中的位置 执行。

用例

假设您正在编写一个游戏。您需要一种控制程序流程的方法。从主菜单到游戏本身;从游戏到暂停菜单;或者从暂停菜单到退出程序。

Solstack 将帮助您构建这些 State 和它们之间的 Transitions.

让我们来模拟一些东西。以下是我们的 State(以 S 开头)

  • SMainMenu:玩家在初始化游戏时着陆的地方。
  • SGame:实际游戏逻辑所在的地方。
  • SPauseMenu:玩家可以保存、继续游戏或退出的地方。

由于 SMainMenu 是用户将首先遇到的东西,因此当程序开始时,我们将手动将那个 State 推送到我们的 Stack 上。然后我们在主循环中检查 Stack,直到它里面不再有任何 State

您可以通过在本地实例上使用其方法来手动在 Stack 上执行转换。

可以使用 Stackis_running 方法来实现循环。

在程序开始时,Stack 将看起来像这样

  1. SMainMenu

只有一个状态在 Stack 上,它位于顶部;因此它将调用其方法。 SMainMenu 的逻辑很简单:当玩家按下 START 时,它请求 Stack Push 一个 SGame。如果发生这种情况,Stack 将看起来像这样

  1. SGame
  2. SMainMenu

由于只有最顶部的状态由 Stacktick 执行,所以 SMainMenu 只在那里坐着。现在玩家正在享受他们的游戏;但他们希望暂停!好吧,在 SGame 中,我们只需要请求堆栈在玩家按下 ESCPush 一个 SPauseMenu。很简单!让我们再次看看 Stack

  1. SPauseMenu
  2. SGame
  3. SMainMenu

现在 SPauseMenu 在顶部。 SGame 将被 暂停;它还在那里,但正在执行。在 SPauseMenu 中应该有逻辑说,如果玩家再次按下 ESC,则 Stack 应该 PopPop 的意思是删除或完全删除 Stack 顶部的最顶层 State。在这种情况下,SPauseMenu 本身。然后,Stack 将再次看起来像这样

  1. SGame
  2. SMainMenu

最终,SGame 再次位于顶部!因此,它将重新开始 精确 到它离开的地方!如果玩家选择 退出游戏,您只需请求 Stack Quit,这将 Pop 它拥有的每个 State,使主循环结束。

这个概念可以扩展到更大的模式;希望您在使用 solstack 结构化应用程序时找到乐趣!

开始吧

这只是一个 非常简单 的代码示例。请查看此页面的开头链接,特别是项目存储库中的示例。测试也是了解库内部工作方式的好方法。

use solstack::prelude::*;
use solstack::macros::*; // easy abstractions over boilerplate-y code.

// data available to `State`s for writing and reading
#[derive(Default)]
struct GameData {
    value: i32
}

// a `State` that does what it says
struct AddOneAndPrintState;
impl State<GameData> for AddOneAndPrintState {
    // run when this `State` is first pushed onto a `Stack`
    fn on_start(&mut self, data: &mut GameData) {
        data.value = 41;
        println!("on_start `make data be 41` > GameData({})", data.value);
    }

    // run every time the `Stack` is ticked.
    fn on_tick(&mut self, data: &mut GameData) -> Trans<GameData> {
        data.value += 1;
        println!("on_tick `add one to data` > GameData({})", data.value);
        Trans::None
    }
}

fn main() {
    // initializing
    let mut data = GameData::default();
    let mut stack = Stack::<GameData>::new();
    
    assert_eq!(data.value, 0);
    
    // manually pushing and ticking the `Stack`
    
    stack_push!(stack, data, AddOneAndPrintState);
    assert_eq!(data.value, 41);
    
    stack_tick!(stack, data);
    assert_eq!(data.value, 42);
    
    stack_tick!(stack, data);
    assert_eq!(data.value, 43);
    
}

谢谢

文档将始终是最新的。

感谢您使用 solstack

由 Sol [email protected]

无运行时依赖项