MrGVSV / bevy_proto

Create config files for entities in Bevy

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Enquiry on better/proper way to despawn/remove entity with prototype

hafiidz opened this issue · comments

Hi, would like to check for a better practice in despawning and re-spawning an entity.
Scenario, I have Appstate:Game and Appstate:MainMenu transitioning between each other which require spawning and despawning and (re)-spawning again later. I had to create an additional entity existing check via if !is_spawned.value to ensure re-spawning happen when re-entering Game, which I think is not efficient. Would like to hear a better solution to force check if entity exist and if not respawning them. Option B, cheated a bit by only respawning descendants and leaving a non visible root entity when leaving Game state.

fn build(&self, app: &mut App) {
        app
            .add_system(load.in_schedule(OnEnter(AppState::GameInit)))
            .add_system(spawn.in_set(OnUpdate(AppState::Game)))
            .add_system(despawn.in_schedule(OnExit(AppState::Game)));
}

// == option A === //
fn spawn(mut commands: ProtoCommands) {
    commands.spawn("PlayerRoot");
}

pub fn despawn(mut commands: Commands, query: Query<Entity, With<PlayerRoot>>) {
    if let Ok(entity) = query.get_single() {
        commands.entity(entity).despawn_recursive();
    }
}


// ==== Option B ====//
fn spawn(
    mut commands: ProtoCommands,
    mut proto_entity: Local<Option<Entity>>,
    mut proto_asset_events: EventReader<ProtoAssetEvent>,
    mut is_spawned: ResMut<IsCardRootSpawned>,
) {
    let proto_entity = *proto_entity.get_or_insert_with(|| commands.spawn(PROTO_ID).id());

    // when the children has been de-spawned via despawn command, re-spawn
    // else re-spawn also when there is some changes to the assets (i.e. Root.prototype.ron)
    // this allows for spawn and despawn when transitioning from and to Main Menu
    if !is_spawned.value {
        commands.entity(proto_entity).insert(PROTO_ID);
        is_spawned.value = true;
    } else {
        for proto_asset_event in proto_asset_events.iter() {
            if proto_asset_event.is_modified(PROTO_ID) {
                commands.entity(proto_entity).insert(PROTO_ID);
            }
        }
    }
}

pub fn despawn(
    mut commands: Commands,
    query: Query<Entity, With<CardRoot>>,
    mut is_spawned: ResMut<IsCardRootSpawned>,
) {
    if let Ok(entity) = query.get_single() {
        commands.entity(entity).despawn_descendants();
        is_spawned.value = false;
    }
}

After further testing and re-reading the examples shared, I am currently using the following setup, seems to be working for me, full re-spawn and de-spawn both during state transition as well as when the *.prorotype.ron file changes.

Sharing here in case others are also experiencing similar issue.

impl Plugin for PlayerPlugin {
    fn build(&self, app: &mut App) {
        app
            // ==== Preload setups ==== //
            .register_type::<PlayerRoot>()
            .init_resource::<IsPlayerRootSpawned>()
            .add_system(load.in_schedule(OnEnter(AppState::GameInit)))
            // ==== Game logic  ==== //
            .add_system(spawn.in_set(OnUpdate(AppState::Game)))
            .add_system(despawn.in_schedule(OnExit(AppState::Game)))
            //  ==== etc  ==== //
            ;
    }
}

fn load(mut prototypes: PrototypesMut) {
    prototypes.load("schematics/player/PlayerRoot.prototype.ron");
}

fn spawn(
    mut proto_commands: ProtoCommands,
    mut commands: Commands,
    mut previous: Local<Option<Entity>>,
    mut proto_asset_events: EventReader<ProtoAssetEvent>,
    mut is_spawned: ResMut<IsPlayerRootSpawned>,
) {
    // when the children has been de-spawned via despawn command, re-spawn
    // else re-spawn also when there is some changes to the assets (i.e. Root.prototype.ron)
    // this allows for spawn and despawn when transitioning from and to Main Menu
    if previous.is_none() || !is_spawned.value {
        *previous = Some(proto_commands.spawn(PROTO_ID).id());
        is_spawned.value = true;
    } else {
        for proto_asset_event in proto_asset_events.iter() {
            if proto_asset_event.is_modified(PROTO_ID) {
                commands.entity(previous.unwrap()).despawn_recursive();
                is_spawned.value = false;
            }
        }
    }
}

fn despawn(
    mut commands: Commands,
    query: Query<Entity, With<PlayerRoot>>,
    mut is_spawned: ResMut<IsPlayerRootSpawned>,
) {
    if let Ok(entity) = query.get_single() {
        commands.entity(entity).despawn_recursive();
        is_spawned.value = false;
    }
}


``