Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

First class save / load support #1442

Open
alice-i-cecile opened this issue Feb 13, 2021 · 6 comments
Open

First class save / load support #1442

alice-i-cecile opened this issue Feb 13, 2021 · 6 comments
Labels
A-Assets Load files from disk to use for things like images, models, and sounds A-Scenes Serialized ECS data stored on the disk C-Feature A new feature, making something new possible

Comments

@alice-i-cecile
Copy link
Member

alice-i-cecile commented Feb 13, 2021

What problem does this solve or what need does it fill?

Saving and loading game state is a core functionality that will be reused across almost every game made in Bevy, but implementing this functionality requires advanced Bevy knowledge and there isn't consensus on how you might effectively accomplish it.

What solution would you like?

A pair of Commands to save and load a game state, probably in combination with some Resources to set global config of this functionality.

@TheRawMeatball whipped up the following prototype:

struct SaveCommand;
struct LastSaveData(Option<SomeSerializedDataType>)
trait SaveCommandExt {
  fn save(&mut self);
}

impl Command for SaveCommand {
  fn write(self: Box<Self>, world: &mut World, resources: &mut Resources){
    let data = /*serialize stuff*/;
    resources.get_mut::<LastSaveData>().unwrap().0 = Some(data);
  }  
}

impl SaveCommandExt for Commands {
  fn save(&mut self) {
    self.add_command(SaveCommand)
  } 
}

Usage:

fn regular_system(commands: &mut Commands) {
  commands.save();
}

What alternative(s) have you considered?

Clearly document an idiomatic way to do this.

Additional context

Here's an example of a new user trying to implement this on their own and stumbling.

This issue relates directly to #255.

@bjorn3
Copy link
Contributor

bjorn3 commented Feb 13, 2021

A big thing to be taken into consideration is that not everything should be saved or can even be saved. In addition how should changes to the game be handled that for example add or remove ui elements. Loading a game should use the new set of ui elements. Not the saved set. This issue has overlap with how to handle scene saving in the editor.

@alice-i-cecile alice-i-cecile added C-Feature A new feature, making something new possible A-Assets Load files from disk to use for things like images, models, and sounds labels Feb 16, 2021
@ypoonse
Copy link

ypoonse commented Aug 3, 2021

Chiming in on what @bjorn3 mentions here (and in the hope it can be useful for someone else). I'm implementing load/save, also taking inspiration from #166 .

I'm using the TypeRegistry to register which components I want to be saved, and before saving, I filter out the empty entities:

pub fn drop_empty(scene: DynamicScene) -> DynamicScene {
    let new_entities = scene.entities
        .into_iter()
        .filter(|entity| {
            entity.components.len() > 0
        });
    
    DynamicScene {
        entities: new_entities.collect()
    }
}

And here's the system saving:

fn save_scene(world: &mut World) {
    // let type_registry = world.get_resource::<TypeRegistryArc>().unwrap();

    let type_registry = TypeRegistry::default();
    type_registry.write().register::<Health>();
    type_registry.write().register::<Player>();
    type_registry.write().register::<Enemy>();


    let scene = DynamicScene::from_world(&world, &type_registry);
    let scene = helpers::drop_empty(scene);

    let mut file = File::create("assets/scenes/start_scene.scn.ron").unwrap();
    file.write_all(
        scene
            .serialize_ron(&type_registry)
            .unwrap()
            .as_bytes(),
    )
    .unwrap();
}

@alice-i-cecile alice-i-cecile moved this to Wishlist in Asset Pipeline Jul 17, 2022
@alice-i-cecile alice-i-cecile added the A-Scenes Serialized ECS data stored on the disk label Jul 17, 2022
@chamons
Copy link

chamons commented Jan 15, 2023

I ran into this, and instead of using bevy_reflect I write "hard coded" save logic to walk all entities/resources and serialize them.

As someone who had to ask around on discord for help on getting started, I figured an example might be useful for other people who came across this issue - https://gist.github.com/chamons/37e8c6f8753e63eaef08bef36686c2e2

In my use case, I use bevy_ecs but not bevy proper, so dynamic scenes were not an option.

@alice-i-cecile
Copy link
Member Author

A nice third party library experimenting in this space: https://github.com/hankjordan/bevy_save

@pinkponk
Copy link

Have to do the same in my game and also found this lib: https://github.com/Zeenobit/bevy_atomic_save

Haven't decided yet how I will go about myself. I guess the options today are:

  1. Doing it like @chamons and looping over and saving everything yourself
  2. use Dynamic scenes yourself (Havent fully understood this yet)
  3. use third party bevy_save or bevy_atomic_save

@bbarker
Copy link

bbarker commented Aug 4, 2024

Here's another alternative I wrote a while back, using a macro to save specified components:

https://github.com/bbarker/bevy_serde_macros

https://github.com/bbarker/bevy_serde_macros/blob/main/src/lib.rs#L206-L234

^ there's still a small amount of boilerplate, but only in one location (late last year - 2023).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-Assets Load files from disk to use for things like images, models, and sounds A-Scenes Serialized ECS data stored on the disk C-Feature A new feature, making something new possible
Projects
None yet
Development

No branches or pull requests

6 participants