Loading entities from a file

I’m evaluating Amethyst for an up coming project and I have a n00b question: How would you create entities specified in a config file?

Every example of working with entities (not just for Amethyst but other Rust engines I’ve looked at) looks like this:

world.spawn_entity((tuple, of, components))

This is fine for toy projects, but I’d like to dynamically load entities from some file format so I don’t need to re-compile the game every time I’m tweaking the game.

In principle I could do something like

// Pseudocode!
let entity_description: Vec<Component> = deserialize_entity_file("001.entity")
let mut entity = world.spawn_entity(())
for component in entity_description {
    entity.add_component(component_from_file)
}

but many ECS frameworks (including Legion) require the 'static trait on Components which makes this difficult since my components (created at runtime) don’t (can’t ?) have the 'static lifetime. I don’t fully grok the 'static lifetime though so feel free to correct me.

TLDR Has anybody seen dynamic entity-loading in the wild or have a description of how to make it work?

What you are probably looking for are prefabs. They contain one or more entities along with all their components. They can be human-readable text files and are loaded at runtime.

// somewhere in main.rs
let loader = data.resources.get_mut::<DefaultLoader>().unwrap();
let prefab_handle: Handle<Prefab> = loader.load("prefab/test.prefab");
self.prefab_handle = Some(prefab_handle.clone());
data.world.push((prefab_handle,));
// test.prefab
Prefab(
    id: "14dec17f-ae14-40a3-8e44-e487fc423287",
    objects: [
        Entity((
             id: "62b3dbd1-56a8-469e-a262-41a66321da8b",
             components: [
                 (
                     type: "f5780013-bae4-49f0-ac0e-a108ff52fec0",
                     data: (
                         position: [100.0, 100.0]
                     ),
                 ),
             ]
        )),
    ]
);
2 Likes

Interesting! How would I do this without prefabs? If I was using Legion without Amethyst how would I create something like prefabs?

The process is called “serialization”, or in the case of reading from a file “deserialization”. Legion offers its own mechanism, which may be used by Amethyst.

// create a registry which uses strings as the external type ID
let mut registry = Registry::<String>::default();
registry.register::<Position>("position".to_string());
registry.register::<f32>("f32".to_string());
registry.register::<bool>("bool".to_string());

// serialize entities with the `Position` component
let json = serde_json::to_value(&world.as_serializable(component::<Position>(), &registry)).unwrap();
println!("{:#}", json);

// registries are also serde deserializers
use serde::de::DeserializeSeed;
let world: World = registry.as_deserialize().deserialize(json).unwrap();

In order to provide serialization, Legion makes use of one of the top libraries for that job on Crates: Serde. Serde supports different formats, including various text-based formats, like RON, JSON, YAML, TOML, etc., multiple binary formats, URI de-/encoding and custom formats by implementing traits.

2 Likes

Thank you so much! This explains why I wasn’t able to find what I was looking for on the Legion docs. I was looking for “load” from a file, but I should have been looking for Serialization.