2 个不稳定版本

0.2.0 2024年5月17日
0.1.0 2024年1月19日

#159 in 游戏开发


bevy_ghx_proc_gen 中使用

MIT/Apache

145KB
2K SLoC

Ghx Proc(edural) Gen(eration)

ghx_proc_gen on crates.io ghx_proc_gen on doc.rs

一个用于2D & 3D过程生成(带有模型合成/Wave function Collapse),也适用于Bevy引擎的Rust库。

canyon

使用模型合成/Wave function Collapse,算法输入提供了邻接约束,并且内部,求解器(本例中为AC-4)将尝试生成满足这些约束的解决方案。

尽管它可以应用于纹理合成(主要与位图相关),但ghx_proc_gen更侧重于基于网格的使用场景,如地形或结构生成,目标如下

  • 快速。尽可能将算法的核心实现为快速;如果API操作可能损害性能,这将清楚地可见。
  • 输出格式无关。在核心算法中,模型只是数字,你在生成器结果中如何解释它们取决于你(3d对象、2d精灵、文本、颜色等)。
  • 人体工程学设计
  • 良好的文档(#![warn(missing_docs)]

快速入门

cargo add ghx_proc_gen

生成的基本构建块称为Models,邻接约束用Socket定义。每个模型在其每一侧都有一个或多个插口。

定义插口之间的连接,并允许在相对两侧有连接插口的模型成为邻居。

让我们构建一个棋盘图案

  1. 首先为算法创建Rules
  // A SocketCollection is what we use to create sockets and define their connections
  let mut sockets = SocketCollection::new();
  // For this example, we will only need two sockets
  let (white, black) = (sockets.create(), sockets.create());

  // With the following, a `white` socket can connect to a `black` socket and vice-versa
  sockets.add_connection(white, vec![black]);

  let mut models = ModelCollection::<Cartesian2D>::new();
  // We define 2 very simple models: a white tile model with the `white` socket on each side
  // and a black tile model with the `black` socket on each side
  models.create(SocketsCartesian2D::Mono(white));
  // We keep the black model for later
  let black_model = models.create(SocketsCartesian2D::Mono(black)).clone();

  // We give the models and socket collection to a RulesBuilder and get our Rules
  let rules = RulesBuilder::new_cartesian_2d(models, sockets).build().unwrap();
  1. 创建一个GridDefinition
  // Like a chessboard, let's do an 8x8 2d grid
  let grid = GridDefinition::new_cartesian_2d(8, 8, false, false);
  1. 创建一个Generator
  // There many more parameters you can tweak on a Generator before building it, explore the API.
  let mut generator = GeneratorBuilder::new()
      .with_rules(rules)
      .with_grid(grid)
      // Let's ensure that we make a chessboard, with a black square bottom-left
      .with_initial_nodes(vec![(GridPosition::new_xy(0, 0), black_model)]).unwrap()
      .build()
      .unwrap();
  1. 获取结果
  // Here we directly generate the whole grid, and ask for the result to be returned.
  // The generation could also be done iteratively via `generator.select_and_propagate()`, or the results could be obtained through an `Observer`
  let chess_pattern = generator.generate_collected().unwrap();

通过在终端中简单地打印结果,我们获得

  let icons = vec!["◻️ ", ""];
  for y in 0..chess_pattern.grid().size_y() {
      for x in 0..chess_pattern.grid().size_x() {
        print!("{}", icons[chess_pattern.get_2d(x, y).model_index]);
      }
      println!();
  }

chess_board_pattern

有关更多信息,请参阅主crate文档或所有示例

API的更多详细信息

模型变化


为了简化规则定义步骤,可以自动为您创建一些模型变体。这将负责适当地旋转所有模型的 sockets

让我们以这个绳桥模型为例

bridge

  let bridge_model = SocketsCartesian3D::Simple {
    x_pos: bridge_side,
    x_neg: bridge_side,
    z_pos: bridge,
    z_neg: bridge,
    y_pos: bridge_top,
    y_neg: bridge_bottom,
  }
  .to_template()
  .with_additional_rotation(ModelRotation::Rot90)

使用上述声明,我们声明了我们的基础模型(默认允许 Rot0),并允许额外的 Rot90 度旋转。内部,当构建 Rules 时,将创建两个模型变体。

当检索生成的结果时,您将获得 ModelInstances,它引用原始模型的 index 以及对其应用的 ModelRotation

您还可以手动创建模型的旋转变体:bridge_model.rotated(ModelRotation::Rot180),并为它使用不同的资源,更改其重量等。[文档]

坐标系 & 轴


ghx_proc_gen 使用一个 右手 坐标系。但用于创建模型变体的旋转轴可以不同

  • 当使用 Cartesian3D 时,它默认为 Y+,并且可以在 RulesBuilder 上自定义。
  • 当使用 Cartesian2D 时,旋转轴固定为 Z+ [文档]

有关 Bevy 的信息,请参阅非官方 Bevy 速查表

套接字连接


如快速入门所示,套接字连接使用 SocketCollection 声明 [文档]

请注意,如果它们用于具有生成旋转变体的模型,则位于您的旋转轴上的套接字连接应不同处理。

socket_compatibility

在上述图中旋转模型 2 将导致其顶部的套接字(此处为 B)不同。对于此示例,我们可以使用

  // a socket `B` can only be connected to another `B` if their **relative** rotation is 0°
  sockets.add_constrained_rotated_connection(B, vec![ModelRotation::Rot0], vec![B]);

让我们假设模型 12 在其顶部和底部分别有不同的套接字声明,并且这些套接字仅在相对旋转为 0° 或 180° 时兼容

  // a socket `model_2_top` can only be connected to another `model_1_bottom`
  // if their **relative** rotation is 0° or 180°
  sockets.add_constrained_rotated_connection(
    model_2_top,
    vec![ModelRotation::Rot0, ModelRotation::Rot180],
    vec![model_1_bottom]
  );

例如,请参阅峡谷示例中的 bridge_start_bottom 套接字,它只能从岩石面向外部。

生成器与人类交互


可以通过用户设置特定的初始值来定制生成:通过调用 with_initial_nodes/with_initial_grid,或者通过调用 set_and_propagate 直接与正在进行中的生成交互。[Bevy 插件视频示例]

这被 ProcGenDebugPlugin 使用。

观察者


您可以直接通过连接到生成器的 Observer 来检索生成器的结果,而不是直接收集生成器的结果 [文档]

这被 ProcGenDebugPlugin 使用。

网格循环


可以配置网格在任意轴上循环,这在其 GridDefinition 上设置 [文档]

grid_scroll-75

Cargo 功能

ghx_proc_gen/cargo.toml 中找到列表和描述

  • models-names [默认]:在创建模型时,可以使用 with_name 函数为它们注册名称。禁用此功能时,该函数不执行任何操作。但启用后,您的模型名称将在运行时(如果启用,则可在调试跟踪中可见)可访问。

  • debug-traces:默认禁用,此功能将使用 tracing 包添加跟踪到包的核心算法。由于其中一些日志位于热点路径上,因此仅在调试时启用此功能。

    日志级别可以通过用户包(tracing::level、Bevy 的 LogPlugin 等)进行配置。

  • bevy:默认禁用,启用它仅使包的常见结构体自动派生 Component

  • reflect:默认禁用,启用它仅使包的常见结构体自动派生 Reflect

针对 Bevy 用户

请参阅使用并公开 ghx_proc_gen 以及针对 Bevy 定制的附加插件和工具的 bevy_ghx_proc_gen 包。

cargo add bevy_ghx_proc_gen

示例

网格坐标系 资产 引擎 插件 相机
棋盘 笛卡尔2D Unicode N/A N/A
Unicode 地形 笛卡尔2D Unicode N/A N/A
Bevy-棋盘 笛卡尔2D 过程网格 Bevy ProcGenSimplePlugin 3D
柱子 笛卡尔3D .glb Bevy ProcGenDebugPlugin 3D
瓦片- 笛卡尔3D .png Bevy ProcGenDebugPlugin 2D
峡谷 笛卡尔3D .glb Bevy ProcGenDebugPlugin 3D

unicode-terrainpillarstile-layerscanyon 的示例视频已被减速,以便查看生成进度。

[命令行] 棋盘示例
cargo run --example chessboard

简单的独立示例,与快速入门部分中的示例相同。

[命令行] Unicode 地形示例
cargo run --example unicode-terrain

简单的独立示例,生成一个俯视的 2D 地形,并在终端中用 Unicode 字符显示。

https://github.com/Henauxg/ghx_proc_gen/assets/19689618/6a1108af-e078-4b27-bae1-65c793ef99c1

[Bevy] Bevy 棋盘示例
cargo run --example bevy-chessboard

最简单的 Bevy 示例,与快速入门部分中的示例相同。

[Bevy] 柱子示例
cargo run --example pillars

此示例在空房间中生成多个不同大小的柱子。它的 rules 非常简单,只有 4 个模型:一个空块、一个柱子底座、一个柱子核心和一个柱子顶部。

https://github.com/Henauxg/ghx_proc_gen/assets/19689618/7beaa23c-df88-47ca-b1e6-8dcfc579ede2

[Bevy] 瓦片层示例
cargo run --example tile-layers

此示例使用 Bevy 和 2D 相机,但通过组合多个 z 层生成俯视瓦片图,因此使用的网格和规则仍然是 3D 的。

https://github.com/Henauxg/ghx_proc_gen/assets/19689618/3efe7b78-3c13-4100-999d-af07c94f5a4d

[Bevy] 峡谷示例
cargo run --example canyon

此示例生成一个类似峡谷的地形,并有一些动画风力涡轮。

https://github.com/Henauxg/ghx_proc_gen/assets/19689618/25cbc758-3f1f-4e61-b6ed-bcf571e229af

其他

编写规则的建议

  • 从简单开始,然后逐步添加复杂性(新模型、插座和连接)。添加一个模型可能会对生成结果产生巨大影响,并可能需要调整权重。
  • 不要犹豫,根据需要定义尽可能多的插座。插座仅存在于 Rules 完全创建之前,并在之后被优化掉。
  • 更改节点选择启发式算法可能会极大地改变生成的结果。
  • 在矩形网格上,对角线约束更难以处理,需要中间模型。
  • 通常有多种实现特定结果的方法,而WFC/模型综合在与其他工具和效果结合使用时表现出色。特别是,您可能会发现对生成结果进行一些后处理很有用(添加支撑、合并模型等)。

局限性

为什么叫“ghx”?

  • 它作为一个命名空间,避免选择诸如 proc_genbevy_proc_gen 的负载名称。

致谢

感谢

许可证

代码

ghx-proc-gen是免费和开源的。此存储库中的所有代码都根据您的选择双许可以下任一许可证

任选。

除非您明确表示,否则任何根据Apache-2.0许可证定义有意提交以包含在您的工作中的贡献,均将根据上述许可证双许可,而无需任何其他条款或条件。

资产

依赖关系

~3–32MB
~483K SLoC