#状态机 #状态转换 #AI #bevy #gamedev

seldom_state

Bevy的组件化状态机插件。适用于AI、玩家状态以及其他处于各种状态的实体。

17个版本 (11个破坏性版本)

0.11.0 2024年7月9日
0.10.0 2024年2月21日
0.9.0 2024年1月13日
0.8.0 2023年11月12日
0.3.0 2022年11月13日

#57 in 游戏开发

Download history 8/week @ 2024-04-29 23/week @ 2024-05-06 36/week @ 2024-05-13 43/week @ 2024-05-20 56/week @ 2024-05-27 37/week @ 2024-06-03 62/week @ 2024-06-10 27/week @ 2024-06-17 21/week @ 2024-06-24 101/week @ 2024-07-01 168/week @ 2024-07-08 63/week @ 2024-07-15 53/week @ 2024-07-22 48/week @ 2024-07-29 13/week @ 2024-08-05 25/week @ 2024-08-12

每月147次下载
用于 2 crates

MIT/Apache

55KB
1K SLoC

seldom_state

Crates.io MIT/Apache 2.0 Crates.io

seldom_state 是Bevy的组件化状态机插件。适用于AI、玩家状态以及其他处于各种状态的实体。它允许在实体之间更高效地复用状态逻辑,相比直接在系统中管理互斥组件。

状态是附加到实体的组件,定义其实际行为,如 JumpingStunned。触发器是一个系统,它检查世界中的实体信息,如 near_positionhealth_below_threshold。转换将两个状态连接起来:一个要转换的,一个要转换到的;一旦发生给定的触发器。状态机是附加到实体的组件,用于跟踪实体的转换,并根据这些转换自动更改实体的状态。

状态机的创建方式如下

commands.spawn((
    // ... (other inserts)
    MyInitialState::new(),
    StateMachine::default()
        .trans::<MyInitialState, _>(my_trigger_1, my_state_2)
        .trans::<AnyState, _>(my_trigger_3, my_state_4)
        .trans_builder(my_trigger_5, |my_state_6: &MyState6, trigger_data| {
            make_state_7(my_state_6, trigger_data)
        })
        .on_enter::<MyState7>(move |entity| entity.insert(my_bundle.clone()))
        .on_exit::<MyState7>(|entity| entity.remove::<MyBundle>())
        // etc.
));

有关更完整的示例,请参阅 examples 目录。示例 chase.rs 写作指南风格,因此非常适合学习。如果您需要帮助,请随时在 Bevy Discord服务器 上联系我(@Seldom)!如果您有任何改进意见,请随时提交问题或pr!

功能

  • 具有用户定义状态和触发器的状态机组件
  • 30个内置触发器
    • always:始终触发
    • NotTriggerAndTriggerOrTrigger:使用布尔逻辑组合触发器
    • done:当将 Done 组件添加到实体时触发
    • leafwing_input特性启用的24个更多触发器:action_dataaxis_pairaxis_pair_length_boundsaxis_pair_max_lengthaxis_pair_min_lengthaxis_pair_rotation_boundsaxis_pair_unboundedclamped_axis_pairclamped_axis_pair_length_boundsclamped_axis_pair_max_lengthclamped_axis_pair_min_lengthclamped_axis_pair_rotation_boundsclamped_axis_pair_unboundedclamped_valueclamped_value_maxclamped_value_minclamped_value_unboundedjust_pressedjust_releasedpressedvaluevalue_maxvalue_min以及value_unbounded
    • on_event:读取指定类型的事件时触发
    • Bevy的内置运行条件也用作触发器
  • AnyState状态,可用于类型参数来表示任何状态
  • 允许从输出状态和触发器到输入状态的转换的转换构建器(StateMachine::trans_builder)
  • 在进入或退出状态时自动执行行为(StateMachine::on_enterStateMachine::on_exitStateMachine::command_on_enterStateMachine::command_on_exit)

big-brain的比较

有限状态机是游戏AI中古老且常用的模式,因此其优点和局限性都是众所周知的。它适用于以下实体:

  1. 没有大量相互连接的状态,因为转换的数量可以呈平方增长。然后很容易忘记添加转换,导致难以解决的错误。
  2. 行为僵硬,如Spelunky中的敌人,根据如被玩家跳上、等待5秒等清晰的触发器行动,对玩家来说是可预测的,而与Dwarf Fortress中的矮人不同,矮人在采取行动之前会权衡选择,显得生动。

seldom_state是有限状态机的实现,因此可能不适合所有类型的游戏AI。如果您需要一个与更复杂的状态和转换一起工作的解决方案,那么您可能需要实现行为树(我在不分支的情况下将现有实现转换为Bevy插件时运气不佳)。如果您需要一个基于模糊逻辑的解决方案,并且不需要定义哪些转换应该允许,那么我推荐big-brain。如果您需要模糊逻辑和离散转换,则可能需要实现模糊状态机。如果您需要离散转换,但不需要模糊逻辑,考虑使用seldom_state

seldom_state不仅仅是一个AI包。因此,您可能希望为敌人的AI使用big-brain,为玩家的状态管理使用seldom_state,控制敌人的动画,等等。

设计模式

seldom_state相当灵活,因此某些问题可能可以用多种方式解决。如果您感到困惑,这里有一些建议。

我有一个实体的动画取决于行为

针对这个问题,有几种解决方案。最直接的方法是将动画添加到具有 on_enter 的实体中。这对于严格遵循行为的动画系统有效,例如2D格斗游戏中的玩家控制器或基本敌人控制器。当然,这是刚性的,并且动画在状态之间必须记住的内容必须手动处理。在像Celeste这样的平台游戏中,单个状态有多个动画(假设状态有 GroundedAirborneDashingClimbing),你可能通过 on_enter 来管理一些动画,比如冲刺动画,而通过系统来管理其他动画,比如行走循环。或者,你可以通过系统来管理所有动画。这取决于个人喜好。

另一方面,如果你的动画不太受行为限制,考虑使用多个状态机,如下一节所述。

我的实体需要同时处于多个状态

考虑一个2D平台游戏,玩家有一个剑。玩家可以跑步和跳跃,并且可以挥舞剑。所以无论你是跑步、跳跃还是冲刺,你都会以相同的方式挥舞剑,与运动状态无关。在这种情况下,你可能想要有一个运动状态机和一个攻击状态机。由于实体只能有一个状态机,你可以创建另一个具有自己状态机的实体(作为一个子实体,我建议这样做),并在 command_on_entercommand_on_exit 中使用闭包来捕获原始的 Entity

然而,也许你的状态并不那么独立。也许在冲刺时攻击会使玩家处于 PowerAttack 状态,或者攻击冷却时间在移动时不计数。根据依赖关系的规模,你可能只想让状态机通过命令和观察彼此的状态进行通信,或者你可能想将状态机组合起来,将相关状态排列成 DashAttackIdleAttackCooldown 这样的状态。

此外,考虑通过状态机管理一组状态,而通过系统管理另一组状态。

我有一些其他问题,很难通过 StateMachine API 表达

请记住,StateMachine 是基于组件的,因此你可以通过 Bevy 的 ECS 解决一些问题。你可以在系统中使用 on_enter::<MyState> 而不是 Added<MyState>,甚至可以通过 removeinsert 命令手动更改状态。如果你手动更改状态,回调如 on_enter 将不会调用,并且你必须确保状态机每次只处于一个确切的状态。否则,它将引发恐慌。

使用方法

将以下内容添加到你的 Cargo.toml

# Replace * with your desired version

[dependencies]
seldom_state = "*"

有关进一步使用方法,请参阅 chase.rs 示例。

兼容性

Bevy leafwing-input-manager seldom_state
0.14 0.14 0.11
0.13 0.13 0.10
0.12 0.11 0.8 - 0.9
0.11 0.10 0.7
0.10 0.9 0.5 - 0.6
0.9 0.8 0.4
0.9 0.3
0.8 0.1 - 0.2

许可

seldom_state 可根据您的选择以 MIT 和 Apache 2.0 许可证双许可。

贡献

除非你明确说明,否则根据 Apache-2.0 许可证定义的,任何有意提交以包含在您的工作中的贡献,都将如上所述双许可,不附加任何额外条款或条件。

依赖项

~18–58MB
~1M SLoC