#Prefabs

26 messages · Page 1 of 1 (latest)

lofty ruin
#

I have been using a Prefab system like this for a while now, and wanted to ask for some comments. I am not sure if I am handling the SystemState correctly or if i am doing stuff manually that actually bevy can do itself already.

/// Trait that marks a struct as a Prefab
pub trait Prefab {
    type Param: SystemParam;
    fn spawn(self, commands: EntityCommands, param: SystemParamItem<Self::Param>);
}

/// Command that can be inserted to Commands
struct InsertPrefab<T: Prefab> {
    prefab: T,
    entity: Entity,
}

impl<T: Prefab + Send + Sync + 'static> Command for InsertPrefab<T> {
    fn write(self, world: &mut World) {
        let mut state: SystemState<(Commands, T::Param)> = SystemState::new(world);
        let (mut commands, param) = state.get_mut(world);
        let e_commands = commands.entity(self.entity);
        self.prefab.spawn(e_commands, param);
        state.apply(world);
    }
}
#

In addition i defined some extension traits for commands

#
//===== EXTENSIONS TO COMMANDS / ENTITY_COMMANDS =======
pub trait InsertPrefabCommand {
    fn insert_prefab<T: Prefab + Send + Sync + 'static>(&mut self, prefab: T) -> &mut Self;
}

impl<'w, 's, 'a> InsertPrefabCommand for EntityCommands<'w, 's, 'a> {
    fn insert_prefab<T: Prefab + Send + Sync + 'static>(&mut self, prefab: T) -> &mut Self {
        let entity = self.id();
        self.commands().add(InsertPrefab { prefab, entity });
        self
    }
}

pub trait SpawnPrefabCommand<'w, 's> {
    fn spawn_prefab<'a, T: Prefab + Send + Sync + 'static>(
        &'a mut self,
        prefab: T,
    ) -> EntityCommands<'w, 's, 'a>;
}
impl<'w, 's> SpawnPrefabCommand<'w, 's> for Commands<'w, 's> {
    fn spawn_prefab<'a, T: Prefab + Send + Sync + 'static>(
        &'a mut self,
        prefab: T,
    ) -> EntityCommands<'w, 's, 'a> {
        let entity = self.spawn_empty().id();
        self.add(InsertPrefab { prefab, entity });
        return self.entity(entity);
    }
}

which allow me to make calls like this, which seem pretty ergonomic

commands.spawn_prefab(InstructionDisplayPrefab {
            action: instruction.action,
            transform: Transform::from_xyz(...)
});

however defining prefabs is a bit of a hassle, and i'd especially love it if there was some trickery to make them a bit more ergonomic

pub struct InstructionDisplayPrefab {
    pub action: Action,
    pub transform: Transform,
}
impl Prefab for InstructionDisplayPrefab {
    type Param = (SRes<UiConfig>, SRes<Windows>);

    fn spawn(
        self,
        mut entity: EntityCommands,
        (config, windows): SystemParamItem<Self::Param>,
    ) {
        entity.insert(... bla).with_children(....);
}
fiery basalt
#

This seems like only a slightly more powerful version of Bundle with a FromWorld implementation

#

(the main difference being that you can have optional components which bundles currently don't support)

sly blaze
#

The other advantage is you can spawn children.

#

@spare flume Concerning your recent question in #general you might be interested in prefabs

#

Hmm, have you considered removing the type Param and instead make spawn generic over Param? (not sure if it works or is workable)

spare flume
#

Hard for me to understand, particularly the system param part. How is the prefab actually getting the type Param: SystemParam; params required to construct it?

#

Specific to my case, I think the associated type not being object safe makes it really difficult to utilize as well

lofty ruin
lofty ruin
lofty ruin
# spare flume Hard for me to understand, particularly the system param part. How is the prefab...
impl<T: Prefab + Send + Sync + 'static> Command for InsertPrefab<T> {
    fn write(self, world: &mut World) {
        let mut state: SystemState<(Commands, T::Param)> = SystemState::new(world);
        let (mut commands, param) = state.get_mut(world);
        let e_commands = commands.entity(self.entity);
        self.prefab.spawn(e_commands, param);
        state.apply(world);
    }
}

The InsertPrefab<T> command fetches T::Param via the SystemState::get_mut and passes it to self.prefab.spawn()

sly blaze
lofty ruin
sly blaze
#

@lofty ruin may I steal your Prefab trait for my crate? I think I've a good use-case for it.

lofty ruin
#

Sure

sly blaze
#

Oh also now taking a closer inspection to the code. Can't you drop the impl Command for InsertPrefab and just use Prefab::spawn in insert_prefab in the extension trait for EntityCommands

lofty ruin
lofty ruin
#

i cant because Prefab::spawn takes systemparams - so i need mutable access to the world

sly blaze
#

OOooh yeah right

lofty ruin
#

nice

#

I had some ideas for a macro yesterday, something that enables a call in a way like this...

#[derive(Prefab)]
#[prefab(spawn_players)]
struct SpawnPlayerPrefab {

}

fn spawn_players(commands: EntityCommands, prefab: SpawnPlayerPrefab, bla: Res<Something>, otherbla: Query<Bla>) {

}

in my head sucha macro should in theory be possible, tho there are some details that will probably be a bit more hairy...
do u think that would be a good idea?

sly blaze
#

I'm not sure it's possible to make it work. Specifically, getting the parameters of the system in Prefab. Maybe using SystemParamFunction

#

I'd recommend trying to write by hand what the expanded macro would look like first. If you get it to compile, then you can judge how much effort it would be to write a macro for it