Created
June 19, 2023 14:05
-
-
Save tigregalis/b1cde096d917c945a840e33a53619752 to your computer and use it in GitHub Desktop.
Hacking together restarting a Bevy app using States
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//! ```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