Ralith / hecs

A handy ECS
Apache License 2.0
924 stars 81 forks source link

Dynamic scene spawning #319

Closed konceptosociala closed 1 year ago

konceptosociala commented 1 year ago

I'm trying to implement spawning a dynamic de-/serializable scene to a World like this:

#[derive(Default, Serialize, Deserialize)]
#[serde(rename = "Entity")]
pub struct SerializableEntity {
    pub components: Vec<Arc<dyn SerializableComponent + 'static>>
}

#[derive(Default, Serialize, Deserialize)]
pub struct Scene {
    pub entities: Vec<SerializableEntity>,
}

pub trait SpawnSceneExt {
    fn spawn_scene(&mut self, scene: Scene);
}

impl SpawnSceneExt for CommandBuffer {
    fn spawn_scene(&mut self, scene: Scene) {
        for entity in scene.entities {
            let mut entity_builder = EntityBuilder::new();

            for component in entity.components {
                entity_builder.add(*component);
            }

            self.spawn(entity_builder.build());
        }
    }
}

where say SerializableComponent necessarily implements both Component and Serialize/Deserialize (using typetag crate)

But it consequentially results into an error:

error[E0277]: the size for values of type `dyn SerializableComponent` cannot be known at compilation time
  --> src/assets/scene.rs:55:36
   |
55 |                 entity_builder.add(*component);
   |                                --- ^^^^^^^^^^ doesn't have a size known at compile-time
   |                                |
   |                                required by a bound introduced by this call
   |

Is there a way to do it, or is this just impossible?

MathiasPius commented 1 year ago

The issue is that EntityBuilder needs to know the size of the component, which means you need to pass it the concrete type. SerializableObject is a trait object which does not know the concrete type, but only information about how to execute the methods of the trait on the underlying object.

What you can do is move the entity_builder.add(...) call into the SerializableComponent trait. That way each implementation of the trait can handle insertion of its concrete type itself, where the knowledge is present.

I've implemented a plain non-hecs example in the Rust which shows how to achieve it: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=1a8557ebcc67432f1c402ecaba8d8387