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

0.8.0 2024年7月20日
0.6.0 2024年4月13日
0.5.1 2024年3月16日
0.3.4 2023年12月18日
0.3.3 2023年11月18日

#56游戏开发

Download history 2/week @ 2024-04-27 6/week @ 2024-05-18 2/week @ 2024-05-25 177/week @ 2024-06-01 23/week @ 2024-06-08 2/week @ 2024-06-15 4/week @ 2024-06-29 8/week @ 2024-07-06 102/week @ 2024-07-20 24/week @ 2024-07-27 3/week @ 2024-08-03

每月下载量129次

MIT/Apache

110KB
2.5K SLoC

bevy_voxel_world

Crates.io License Bevy tracking


什么是 bevy_voxel_world

此插件使得在Bevy中生成和修改体素地形变得简单。 bevy_voxel_world 处理多线程网格生成、块创建/销毁、纹理映射,并提供一个易于使用的API,可以从任何系统访问。

bvw_480

$ cargo run -r --example noise_terrain

世界可以通过两种主要方式控制:通过地形查找函数,以及通过 set_voxelget_voxel 函数直接控制。世界有两层体素信息,一层是通过地形查找函数生成的程序化信息,另一层是通过 set_voxel 控制的,并持久化存储在 HashMap 中。持久化层始终覆盖程序化层。这样,世界可以无限大,但我们只需要存储有意改变的体素信息。在当前实现中,程序化层被缓存在已创建的块中,因此如果创建距离很大,仍然可能会使用大量内存。

有关如何使用地形查找函数的示例,请参阅 此示例

基本设置

为您的世界创建一个配置结构体

#[derive(Resource, Clone, Default)]
struct MyWorld;

impl VoxelWorldConfig for MyWorld {
    // All options have defaults, so you only need to add the ones you want to modify.
    // For a full list, see src/configuration.rs
    fn spawning_distance(&self) -> u32 {
        25
    }
}

然后添加带有您的配置的插件

.add_plugins(VoxelWorldPlugin::with_config(MyWorld))

配置结构体做两件事

访问世界

要在系统中访问体素世界实例,您可以使用 VoxelWorld 系统参数。 VoxelWorld 接收一个类型参数,这是您要访问的世界配置结构体。

可以使用 set_voxelget_voxel 访问函数来操纵世界中的体素数据。

fn my_system(mut voxel_world: VoxelWorld<MyWorld>) {
    voxel_world.set_voxel(IVec3 { ... }, WorldVoxel::Solid(0));
}

这将更新持久 HashMap 中给定位置的体素值,并导致 bevy_voxel_world 将受影响的块排队进行重新网格化。

体素通过其在世界中的XYZ坐标进行索引,由一个IVec3指定。体素类型由WorldVoxel类型指定。一个体素可以是UnsetAirSolid

体素材料

Solid体素包含一个u8材料类型值。因此,最多支持256种材料类型。通过映射回调,材料类型可以轻松地映射到2D纹理数组中的索引。

在配置中可以提供自定义的数组纹理。它应该是一个大小为W x (W * n)的图像,其中n是索引数。因此,4个16x16像素纹理的数组大小将是16x64像素。索引数在第二个参数中指定。

然后,为了映射哪些索引属于哪种材料类型,你可以提供一个texture_index_mapper回调

impl VoxelWorldConfig for MyWorld {
    fn texture_index_mapper(&self) -> Arc<dyn Fn(u8) -> [u32; 3] + Send + Sync> {
        Arc::new(|vox_mat: u8| match vox_mat {
            SNOWY_BRICK => [0, 1, 2],
            FULL_BRICK => [2, 2, 2],
            GRASS | _ => [3, 3, 3],
        })
    }

    fn voxel_texture(&self) -> Option<(String, u32)> {
        Some(("example_voxel_texture.png".into(), 4)) // Array texture with 4 indexes
    }
}

texture_index_mapper回调会提供一个材料类型,并应该返回一个包含三个值的数组。这些值表示哪个纹理索引映射到体素的[top, sides, bottom]

请参见纹理示例以了解此示例的运行实例。

Screenshot 2023-11-06 at 21 50 05

自定义着色器支持

如果您需要进一步自定义材料,您可以在添加插件时使用.with_material(MyCustomVoxelMaterial),以注册您自己的Bevy材料。这允许您使用自己的自定义着色器与bevy_voxel_world一起使用。有关更多详细信息,请参阅此示例

光线投射

要从屏幕上的像素位置(例如鼠标位置)在世界中找到一个体素位置,您可以对体素世界进行光线投射。

fn do_something_with_mouse_voxel_pos(
    voxel_world: VoxelWorld<MyWorld>,
    camera_info: Query<(&Camera, &GlobalTransform), With<VoxelWorldCamera<MyWorld>>>,
    mut cursor_evr: EventReader<CursorMoved>,
) {
    for ev in cursor_evr.read() {
        // Get a ray from the cursor position into the world
        let (camera, cam_gtf) = camera_info.single();
        let Some(ray) = camera.viewport_to_world(cam_gtf, ev.position) else {
            return;
        };

        if let Some(result) = voxel_world.raycast(ray, &|(_pos, _vox)| true) {
            // result.position will be the world location of the voxel as a Vec3
            // To get the empty location next to the voxel in the direction of the surface where the ray intersected you can use result.normal:
            // let empty_pos = result.position + result.normal;
        }
    }
}

请参见此光线投射完整示例以获取更多详细信息。

注意事项

bevy_voxel_world最初是我正在开发的一款游戏的内部部分,但我认为它可以用作一个独立的插件,对我自己和可能对其他人都有用,所以我决定将其拆分出来,并作为crate公开。

在其当前状态下,还有一些硬编码的假设适用于我的用例,但可能不适合每个人。随着时间的推移,目标是使其泛化,并使bevy_voxel_world更具可配置性。还有许多潜在的性能优化,我尚未优先考虑。

目前仅支持“方块”,类似于Minecraft的体素,且不支持“半块”。网格处理由block-mesh-rs处理,仅使用“简单”算法(即没有贪婪网格)。

欢迎反馈、问题和拉取请求!


Bevy兼容性

bevy bevy_voxel_world
0.14 ^0.8.0
0.13 0.4.0 - 0.8.0
0.12 0.3.6
0.11 0.2.2

依赖项

~23MB
~428K SLoC