Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
In the `GameState::Menu` game state, we need to show our wonderful new menu. In the `GameState::Playing` game state, we need to *hide* our wonderful UI. Kayak requires that we `bind` to resources we want to expose to the UI before we can use it to show/hide the UI. We need to add a new resource that is `bind(GameState::Menu)`. This is the binding that will update over time. Add a new system called `bind_gamestate` that accesses the `CurrentState` and the `Binding` for `GameState`. We can use `is_changed()` to detect if the `CurrentState` has changed, and set the binding to the current `GameState` value. This will continually update our binding when the `CurrentState` changes. ```rust impl Plugin for UiPlugin { fn build(&self, app: &mut bevy::prelude::App) { app.add_plugin(BevyKayakUIPlugin) .insert_resource(bind(GameState::Menu)) .add_startup_system(game_ui) .add_system(bind_gamestate); } } pub fn bind_gamestate( state: Res<CurrentState<GameState>>, binding: Res<Binding<GameState>>, ) { if state.is_changed() { binding.set(state.0); } } ``` You might think that we could insert this resource by querying the `CurrentState` in `game_ui` but unfortunately, the iyes_loopless `GameState` isn’t accessible in startup systems (our custom stage only inserts it after the startup systems have run), so our `game_ui` startup system can’t access it. If we tried to, with code that looks like this: ```rust pub fn game_ui( mut commands: Commands, mut font_mapping: ResMut<FontMapping>, asset_server: Res<AssetServer>, gamestate: Res<CurrentState<GameState>> ) {...} ``` Then we would get this error at runtime: `Resource requested by snake::ui::game_ui does not exist: iyes_loopless::state::CurrentState<snake::GameState>` ```rust ❯ cargo run Finished dev [unoptimized + debuginfo] target(s) in 0.08s Running `target/debug/snake` 2022-04-23T12:50:49.775340Z INFO bevy_render::renderer: AdapterInfo { name: "Apple M1 Max", vendor: 0, device: 0, device_type: DiscreteGpu, backend: Metal } thread 'Compute Task Pool (2)' panicked at 'Resource requested by snake::ui::game_ui does not exist: iyes_loopless::state::CurrentState<snake::GameState>', /Users/chris/.cargo/registry/src/github.com-1ecc6299db9ec823/bevy_ecs-0.7.0/src/system/system_param.rs:319:17 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace thread 'main' panicked at 'task has failed', /Users/chris/.cargo/registry/src/github.com-1ecc6299db9ec823/async-task-4.2.0/src/task.rs:425:45 thread 'main' panicked at 'Task thread panicked while executing.: Any { .. }', /Users/chris/.cargo/registry/src/github.com-1ecc6299db9ec823/bevy_tasks-0.7.0/src/task_pool.rs:77:21 ``` We do want to sync up the state that we’re actually starting the game with and the state that we initialize the binding with though. We can create a public const for this in `lib.rs`. ```rust pub const STARTING_GAME_STATE: GameState = GameState::Menu; ``` and then use it in our `add_loopless_state` in `main.rs`. ```rust .add_loopless_state(STARTING_GAME_STATE) ``` as well as our `bind` in `ui.rs`. ```rust .insert_resource(bind(STARTING_GAME_STATE)) ``` In our `GameMenu` widget, we can create a new boolean value called `show_menus`. This will control whether or not our menus will show. Using `context` we can query the world for the `Binding<GameState>` and clone the binding out for our own use. This is similar to how we sent the app exit event. The `Binding` type is a struct that holds an id and an `Arc` wrapped value, which means cloning is cheap. Then we need to bind the relevant value to this widget so that we can react to updates. This is done with `context.bind`. We can `.get` the value from the binding and check it against `Menu` to determine if the game is in the `Menu` state or not. ```rust let show_menus = { let gamestate = context .query_world::<Res<Binding<GameState>>, _, _>( |state| state.clone(), ); context.bind(&gamestate); gamestate.get() == GameState::Menu }; ``` We can wrap the `If` component around our entire menu and set the `condition` to our `show_menus` boolean. ```rust rsx! { <If condition={show_menus}> <Background styles={Some(container_styles)} > ... </Background> </If> } ``` A `STARTING_GAME_STATE` of `GameState::Menu` will now show us the menu. ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/59f057e7-477a-44d9-9ed3-904002d3c2a7/Untitled.png) While a `STARTING_GAME_STATE` of `GameState::Playing` will let us play the game, then transition to showing the menu when we hit a wall.
- Loading branch information