Skip to content

Instantly share code, notes, and snippets.

@tigregalis
Created June 19, 2023 14:05
Show Gist options
  • Save tigregalis/b1cde096d917c945a840e33a53619752 to your computer and use it in GitHub Desktop.
Save tigregalis/b1cde096d917c945a840e33a53619752 to your computer and use it in GitHub Desktop.
Hacking together restarting a Bevy app using States
//! ```cargo
//! [dependencies]
//! bevy = { version = "0.11", git = "https://github.com/bevyengine/bevy" }
//! ```
use bevy::prelude::*;
fn main() {
App::new()
// adds a Window entity
.add_plugins(DefaultPlugins)
// Boilerplate for adding our state
.add_state::<AppState>()
// spawn the immortals, adds a Camera
.add_systems(PreStartup, spawn_immortals)
// changes the state to Running immediately
.add_systems(Startup, restart)
// if user pressed R, trigger a restart by setting the state to Restarting
.add_systems(PreUpdate, trigger_restart)
// changes the state to Running immediately
.add_systems(OnEnter(AppState::Restarting), restart)
// this is where we would normally put all of our setup code in PreStartup or Startup
.add_systems(OnEnter(AppState::Running), (setup1, setup2))
// this is where we put any teardown code (despawning entities, except immortals and the primary window)
.add_systems(OnExit(AppState::Running), teardown)
.run();
}
/// Boilerplate for setting up a basic restarting architecture:
/// The two states (Re)starting and Running
#[derive(States, Default, Debug, Clone, Hash, Eq, PartialEq)]
enum AppState {
/// Nothing happens in this state other than moving immediately to the Running state
#[default]
Restarting,
// When we enter this state, we run any user-defined setup code (what would normally live in Startup / Prestartup for example)
// When we exit this state we tear down anything that was spawned
Running,
}
/// Boilerplate for setting up a basic restarting architecture:
/// Moves the state into AppState::Running so that the OnEnter(AppState::Running) system is called
fn restart(mut next_state: ResMut<NextState<AppState>>) {
*next_state = NextState(Some(AppState::Running));
}
/// Boilerplate for setting up a basic restarting architecture:
/// Moves the state into AppState::Running so that the OnEnter(AppState::Running) system is called
fn trigger_restart(input: Res<Input<KeyCode>>, mut next_state: ResMut<NextState<AppState>>) {
if input.just_pressed(KeyCode::R) {
println!("user triggered restart");
*next_state = NextState(Some(AppState::Restarting));
}
}
/// Marker component for things we spawn once and never despawn
#[derive(Component)]
struct Immortal;
/// We spawn these once in PreStartup and never need to despawn this
fn spawn_immortals(mut commands: Commands) {
println!("immortal");
commands.spawn((Camera2dBundle::default(), Immortal));
}
/// A user-defined setup system
fn setup1(mut commands: Commands, time: Res<Time>) {
println!("(re-)starting from setup1");
// simulating randomness
let secs = time.elapsed_seconds();
let cos = secs.cos();
let sin = secs.sin();
println!("spawning");
commands.spawn(SpriteBundle {
sprite: Sprite {
color: Color::rgb(1.0, cos, sin),
custom_size: Some(Vec2::new(100.0 + 50.0 * cos, 50.0 + 30.0 * sin)),
..default()
},
transform: Transform::from_translation(Vec3::new(cos, sin, 0.0)),
..default()
});
}
/// Another user-defined setup system
/// Too lazy to come up with another example
fn setup2() {
println!("(re-)starting from setup2");
}
/// User-defined teardown code can live here
/// If you kill all the Windows it will quit the app, so we use Without<PrimaryWindow> here
/// We also don't despawn the "immortals"
fn teardown(
mut commands: Commands,
query: Query<Entity, (Without<bevy::window::PrimaryWindow>, Without<Immortal>)>,
) {
for entity in query.iter() {
commands.entity(entity).despawn();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment