#bevy-ecs #serialization #ecs #bevy #save #serde

已删除 bevy-salo

bevy_ecs 的无反射依赖的基于 ECS 的序列化 crate

2 个版本

0.1.1 2023 年 11 月 27 日
0.1.0 2023 年 11 月 26 日

#51 in #bevy-ecs

MIT/Apache

87KB
2K SLoC

Rust 1.5K SLoC // 0.0% comments Rusty Object Notation 386 SLoC

bevy-salo

Latest version Documentation MIT/Apache 2.0

bevy_salo (SAveLOad) 是一个为 bevy_ecs 提供基于 ECS 的序列化的 crate。

独特功能

  • 不依赖于反射或 bevy_app。
  • 更大的用户控制。
  • 按名称匹配实体。
  • 自定义的 ser/de 方法,可以加载资源、生成实体等。

入门指南

要开始使用,请注册插件和所有需要序列化的类型。

// It is recommanded to alias here.
type All = bevy_salo::All<SerdeJson>;
app.add_plugins(
    SaveLoadPlugin::new::<All>()
        .register::<Unit>()
        .register::<Weapon>()
        .register::<Stat>()
        .register::<Hp>()
);

泛型类型(遗憾的是)需要单独注册。

SaveLoadPlugin::new::<All>()
    .register::<Unit<Human>>()
    .register::<Unit<Monster>>()

All 序列化所有实体,可以通过标记组件来缩小作用域

#[derive(Debug, Default, Component)]
pub struct SaLo;

impl bevy_salo::MarkerComponent for SaLo {
    // Set the serialization method here.
    type Method = SerdeJson;
}

app.add_plugins(
    SaveLoadPlugin::new::<SaLo>()
        .register::<Unit>()
);

用法

bevy_salo 创建序列化和反序列化的调度。如果您可以访问一个 &mut World,您可以使用这些扩展方法。您可以使用系统或实现自定义 Command

world.load_from_file::<All>("test.ron");
world.save_to_file::<All>("test.json");
world.deserialize_from::<All>(bytes);
let bytes = world.serialize_to::<All>();

反序列化不会删除现有项目。为了清理,请选择最适合您用例的这些函数之一,或者编写您自己的逻辑。

// remove all serialized components, does not despawn entities
world.remove_serialized_components::<All>();

// despawn entities with a marker.
world.despawn_with_marker::<Marker>();

特质

为了让您的结构体与 bevy_salo 一起工作,您需要实现三个特质之一: SaveLoadCoreSaveLoadMappedSaveLoad

SaveLoadCore

SaveLoadCore 可以很容易地应用于任何实现 serde::Serializeserde::Deserialize 的结构体。

struct Weapon {
    name: String,
    damage: f32,
    cost: i32,
}
impl SaveLoadCore for Weapon {}

但是,您几乎总是应该覆盖 SaveLoadCore 上的 type_name 函数,因为默认实现 Any::type_name() 在 Rust 版本和命名空间之间是不稳定的,这可能在重构时破坏保存格式。

impl SaveLoadCore for Weapon {
    // This has to be unique across all registered types.
    fn type_name() -> Cow<'static, str> {
        Cow::Borrowed("weapon")
    }
    // Provide a path name for the associated entity.
    fn path_name(&self) -> Option<Cow<'static, str>> {
        Some(self.name.clone().into())
    }
}

SaveLoadMapped

SaveLoadMappedSaveLoadCore 类似,但是您可以将不可序列化的结构映射到可序列化的。

SaveLoad

实现 SaveLoad 允许您在序列化和反序列化过程中执行任意操作。查看其文档以获取更多信息。

字符串池示例

interned_enum!(ElementsServer, Elements: u64 {
    Water, Earth, Fire, Air
});

impl SaveLoad for Elements {
    type Ser<'ser> = &'ser str;
    type De = String;
    type Context<'w, 's> = Res<'w, ElementsServer>;
    type ContextMut<'w, 's> = ResMut<'s, ElementsServer>;

    fn to_serializable<'t, 'w, 's>(&'t self, 
        _: Entity,
        _: impl Fn(Entity) -> EntityPath, 
        res: &'t Res<'w, ElementsServer>
    ) -> Self::Ser<'t> {
        res.as_str(*self)
    }

    fn from_deserialize<'w, 's>(
        de: Self::De, 
        _: &mut Commands,
        _: Entity,
        _: impl FnMut(&mut Commands, &EntityPath) -> Entity, 
        res: &mut ResMut<'s, ElementsServer>
    ) -> Self {
        res.get(&de)
    }
}

路径

bevy_salo 将每个实体记录为其实体 ID 或其路径。实体 ID 仅用于区分,而路径允许与现有实体进行匹配。

每个组件可以可选地使用上述 traits 中定义的 path_name 函数为其关联的实体提供名称。对于非序列化实体,可以使用 PathName 组件代替。

在此示例中,实体的路径名为 "John"

Entity {
    Character => Some("John"),
    Weapon => None,
    Armor => None,
}

如果名称冲突,则会引发 panic。

Entity {
    Character => Some("John"),
    Role => Some("Protagonist"),
}

一个实体的路径包含其所有命名的祖先。考虑这个实体

(root)::window::(unnamed)::characters::John::weapon

武器的路径是 characters::John::weapon,而其未命名的祖先之前的内容将被忽略。当你想要将 "John" 插入到现有的实体 "characters" 中时,这非常有用。

路径化实体必须具有唯一的路径,但允许重复的名称。

// legal, although both named `weapon`, paths are different
characters::John::weapon
characters::Jane::weapon

// illegal, 2 entities with path `characters`
characters::John::weapon
characters::Jane::(unnamed)::characters

警告

在序列化时,序列化子实体的非序列化父实体必须命名。

// legal, parent is root
(root)::[Named]

// legal, parent is named
Named::[Named]

// illegal, parent is not named, cannot deserialize correctly
(unnamed)::[Named]

PathName 不会被序列化,不应在非静态序列化实体中使用。

依赖

~21MB
~398K SLoC