#bevy #spawn #prefab

moonshine-spawn

Bevy中实体生成工具集合

6个版本

0.2.2 2024年7月5日
0.2.1 2024年5月4日
0.2.0 2024年3月19日
0.1.2 2024年2月20日
0.1.1 2023年11月20日

#672游戏开发

Download history 192/week @ 2024-05-04 2/week @ 2024-05-11 23/week @ 2024-05-18 4/week @ 2024-05-25 4/week @ 2024-06-01 6/week @ 2024-06-08 6/week @ 2024-06-15 2/week @ 2024-06-22 130/week @ 2024-06-29 26/week @ 2024-07-06 10/week @ 2024-07-13 12/week @ 2024-07-20 10/week @ 2024-07-27

174 每月下载次数
2 个库中使用(通过 moonshine-core

MIT 协议

28KB
415

🥚 Moonshine Spawn

crates.io downloads docs.rs license stars

Bevy中实体生成工具集合。

概述

在Bevy中,通常使用ChildBuilder来生成复杂的实体层次结构。

use bevy::prelude::*;

fn spawn_chicken(commands: &mut Commands) -> Entity {
    // Spawn logic is spread between this function and the bundle
    commands.spawn(ChickenBundle { /* Components */ }).with_children(|chicken| {
        // Children
    })
    .id()
}

#[derive(Bundle)]
struct ChickenBundle {
    // ...
}

虽然这种模式适用于大多数情况,但它往往会在实体生成逻辑的捆绑和构建实体层次结构的函数之间分散逻辑。这可能会使代码更不模块化,更难以阅读和维护。

此外,没有内置的功能来引用预定义的实体层次结构(即“预制件”)。

这个crate旨在通过提供使生成更便捷的工具来解决这些问题。

use bevy::prelude::*;
use moonshine_spawn::prelude::*;

let mut app = App::new();
// Make sure `SpawnPlugin` is added to your `App`:
app.add_plugins((DefaultPlugins, SpawnPlugin));

// Register spawnables during initialization:
let chicken_key: SpawnKey = app.add_spawnable("chicken", Chicken);

// Spawn a spawnable with a key:
let chicken = app.world_mut().spawn_with_key(chicken_key); // .spawn_with_key("chicken") also works!

#[derive(Component)]
struct Chicken;

impl Spawn for Chicken {
    type Output = (Chicken, SpawnChildren);

    // Spawn logic is now unified:
    fn spawn(&self, world: &World, entity: Entity) -> Self::Output {
        // Components
        (Chicken,
        spawn_children(|chicken| {
            // Children
        }))
    }
}

使用方法

可生成类型

如果一种类型实现了SpawnSpawnOnce,则它是一个“可生成”类型。

trait Spawn {
    type Output;

    fn spawn(&self, world: &World, entity: Entity) -> Self::Output;
}

trait SpawnOnce {
    type Output;

    fn spawn_once(self, world: &World, entity: Entity) -> Self::Output;
}

SpawnOnce默认为所有Bevy捆绑实现。

Spawn为所有实现SpawnOnce + Clone的类型实现。这意味着任何Bundle + Clone都实现了Spawn

生成的输出始终是一个捆绑,然后在生成过程的最后将其插入给定的entity

您可以使用这些特性来定义功能性的可生成类型

use bevy::prelude::*;
use moonshine_spawn::prelude::*;

#[derive(Resource)]
struct DefaultChickenName(Name);

struct Egg;

impl SpawnOnce for Egg {
    type Output = ChickenBundle;

    fn spawn_once(self, world: &World, entity: Entity) -> Self::Output {
        let DefaultChickenName(name) = world.resource::<DefaultChickenName>();
        ChickenBundle::new(name.clone())
    }
}

#[derive(Bundle)]
struct ChickenBundle {
    chicken: Chicken,
    name: Name,
}

impl ChickenBundle {
    fn new(name: Name) -> Self {
        Self {
            chicken: Chicken,
            name,
        }
    }

}

#[derive(Component)]
struct Chicken;

fn open_egg(egg: Egg, commands: &mut Commands) -> Entity {
    commands.spawn_with(egg).id()
}

捆绑 + 子实体

要生成具有子实体的捆绑,请使用WithChildren特性

use bevy::prelude::*;
use moonshine_spawn::prelude::*;

#[derive(Component)]
struct Chicken;

fn chicken() -> impl SpawnOnce {
    Chicken.with_children(|chicken| {
        // ...
    })
}

或者使用SpawnChildren组件和spawn_children函数

use bevy::prelude::*;
use moonshine_spawn::prelude::*;

#[derive(Bundle)]
struct ChickenBundle {
    chicken: Chicken,
    children: SpawnChildren,
}

#[derive(Component)]
struct Chicken;

fn chicken() -> impl SpawnOnce {
    ChickenBundle {
        chicken: Chicken,
        children: spawn_children(|chicken| {
            // ...
        })
    }
}

生成键

SpawnKey是注册的生成器的引用。

每个键必须在World的作用域内是唯一的,并使用AddSpawnable扩展特性进行注册。

在应用程序初始化期间使用此功能注册可生成实体

app.add_spawnable("chicken", chicken());

您可以在运行时使用生成键生成可生成实体,无论是通过 命令 还是 &mut 世界

commands.spawn_with_key("chicken");

生成时,您还可以使用生成键生成捆绑包的子实体

fn chicken() -> impl SpawnOnce {
    ChickenBundle {
        chicken: Chicken,
        children: spawn_children(|chicken| {
            chicken.spawn_with_key("chicken_head");
        })
    }
}

强制生成子实体

此软件包通过运行一个系统来实现,该系统在 SpawnChildren 组件First 调度期间调用。

有时,由于系统依赖关系,在 First 调度运行之前,可能需要手动生成子实体。

在这种情况下,您可以使用 强制生成子实体 手动调用这些组件

use bevy::prelude::*;
use moonshine_spawn::{prelude::*, force_spawn_children};

let mut app = App::new();

// This system spawns a chicken during setup:
fn spawn_chicken(mut commands: Commands) {
    commands.spawn_with(chicken());
}

// This system depends on children of `Chicken`:
fn update_chicken_head(query: Query<(Entity, &Chicken, &Children)>, mut head: Query<&mut ChickenHead>) {
    for (entity, chicken, children) in query.iter() {
        if let Ok(mut head) = head.get_mut(children[0]) {
            // ...
        }
    }
}

#[derive(Component)]
struct Chicken;

fn chicken() -> impl SpawnOnce {
    Chicken.with_children(|chicken| {
        chicken.spawn(ChickenHead);
    })
}

#[derive(Component)]
struct ChickenHead;

let mut app = App::new();

app.add_plugins((DefaultPlugins, SpawnPlugin))
    .add_systems(Startup, spawn_chicken)
    // Without `force_spawn_children`, chicken head would only be updated on the second update cycle after spawn.
    // With `force_spawn_children`, chicken head would be updated in the same update cycle.
    .add_systems(Startup, force_spawn_children())
    .add_systems(Update, update_chicken_head);

支持

对于任何错误、问题或建议,请 发布一个问题

您还可以在官方 Bevy Discord 服务器上通过 @Zeenobit 联系我。

依赖项

~11MB
~195K SLoC