From 25c1b94e26bb5fe5e9d3cbcc27cbfff1ea2dfe6b Mon Sep 17 00:00:00 2001 From: Nathan Stocks Date: Mon, 3 Jan 2022 17:34:29 -0700 Subject: [PATCH 01/11] fix 'common setup' links and fill out 'car shoot' scenario --- examples/scenarios/car_shoot.rs | 71 +++++++++------------- scenarios/cannon_practice.md | 2 +- scenarios/car_shoot.md | 103 ++++++++++++++++++++++++++++---- scenarios/drivers_ed.md | 2 +- scenarios/labrinth.md | 2 +- scenarios/space_invaders.md | 2 +- 6 files changed, 124 insertions(+), 58 deletions(-) diff --git a/examples/scenarios/car_shoot.rs b/examples/scenarios/car_shoot.rs index 2adf792..5dac4f6 100644 --- a/examples/scenarios/car_shoot.rs +++ b/examples/scenarios/car_shoot.rs @@ -7,13 +7,19 @@ rusty_engine::init!(GameState); #[derive(Default)] struct GameState { marbles_left: Vec, - cars_left: Vec, + cars_left: i32, spawn_timer: Timer, } fn main() { let mut game = Game::new(); + // Set the title of the window to be Car Shooter + game.window_settings(WindowDescriptor { + title: "Car Shooter".into(), + ..Default::default() + }); + // Create the player let player = game.add_sprite("player", RacingBarrierRed); player.rotation = UP; @@ -21,12 +27,6 @@ fn main() { player.translation.y = -325.0; player.layer = 10.0; - // Set the Window Settings - game.window_settings(WindowDescriptor { - title: "Car Shooter".into(), - ..Default::default() - }); - // Start the music game.audio_manager.play_music(MusicPreset::Classy8Bit, 0.1); @@ -35,14 +35,10 @@ fn main() { // Marbles left. We'll use these strings as labels for sprites. If they are present in the // vector, then they are available to be shot out of the marble gun. If they are not present, // then they are currently in play. - for i in 0..3 { - game_state.marbles_left.push(format!("marble{}", i)); - } + game_state.marbles_left = vec!["marble1".into(), "marble2".into(), "marble3".into()]; // Cars left in level - each integer represents a car that will be spawned - for i in 0..25 { - game_state.cars_left.push(i); - } + game_state.cars_left = 25; let cars_left = game.add_text("cars left", "Cars left: 25"); cars_left.translation = Vec2::new(540.0, -320.0); @@ -55,24 +51,16 @@ const CAR_SPEED: f32 = 300.0; fn game_logic(engine_state: &mut EngineState, game_state: &mut GameState) -> bool { // Handle marble gun movement - for event in engine_state.mouse_location_events.drain(..) { - let player = engine_state.sprites.get_mut("player").unwrap(); - player.translation.x = event.position.x; + let player = engine_state.sprites.get_mut("player").unwrap(); + if let Some(location) = engine_state.mouse_state.location() { + player.translation.x = location.x; } + let player_x = player.translation.x; // Shoot marbles! - for event in engine_state.mouse_button_events.clone() { - if !matches!(event.state, ElementState::Pressed) { - continue; - } + if engine_state.mouse_state.just_pressed(MouseButton::Left) { // Create the marble if let Some(label) = game_state.marbles_left.pop() { - let player_x = engine_state - .sprites - .get_mut("player") - .unwrap() - .translation - .x; let marble = engine_state.add_sprite(label, RollingBallBlue); marble.translation.y = -275.0; marble.translation.x = player_x; @@ -91,9 +79,18 @@ fn game_logic(engine_state: &mut EngineState, game_state: &mut GameState) -> boo marble.translation.y += MARBLE_SPEED * engine_state.delta_f32; } + // Move cars across the screen + for car in engine_state + .sprites + .values_mut() + .filter(|car| car.label.starts_with("car")) + { + car.translation.x += CAR_SPEED * engine_state.delta_f32; + } + // Clean up sprites that have gone off the screen let mut labels_to_delete = vec![]; - for sprite in engine_state.sprites.values_mut() { + for sprite in engine_state.sprites.values() { if sprite.translation.y > 400.0 || sprite.translation.x > 750.0 { labels_to_delete.push(sprite.label.clone()); } @@ -105,15 +102,6 @@ fn game_logic(engine_state: &mut EngineState, game_state: &mut GameState) -> boo } } - // Move cars across the screen - for car in engine_state - .sprites - .values_mut() - .filter(|car| car.label.starts_with("car")) - { - car.translation.x += CAR_SPEED * engine_state.delta_f32; - } - // Spawn cars if game_state .spawn_timer @@ -123,10 +111,11 @@ fn game_logic(engine_state: &mut EngineState, game_state: &mut GameState) -> boo // Reset the timer to a new value game_state.spawn_timer = Timer::from_seconds(thread_rng().gen_range(0.1..1.25), false); // Get the next car - if let Some(i) = game_state.cars_left.pop() { - let cars_left = engine_state.texts.get_mut("cars left").unwrap(); - cars_left.value = format!("Cars left: {}", i); - let label = format!("car{}", i); + if game_state.cars_left > 0 { + game_state.cars_left -= 1; + let label = format!("car{}", game_state.cars_left); + let cars_left_text = engine_state.texts.get_mut("cars left").unwrap(); + cars_left_text.value = format!("Cars left: {}", game_state.cars_left); let car_choices = vec![ RacingCarBlack, RacingCarBlue, @@ -154,8 +143,6 @@ fn game_logic(engine_state: &mut EngineState, game_state: &mut GameState) -> boo continue; } if !event.pair.one_starts_with("marble") { - // it's two cars spawning on top of each other, take one out - engine_state.sprites.remove(&event.pair.0); continue; } engine_state.sprites.remove(&event.pair.0); diff --git a/scenarios/cannon_practice.md b/scenarios/cannon_practice.md index 8655fd3..202988b 100644 --- a/scenarios/cannon_practice.md +++ b/scenarios/cannon_practice.md @@ -6,7 +6,7 @@ In this scenario you will create a cannon that sits on the bottom left side of t ## Common Setup -1. Follow the instructions in the [Common Setup](https://github.com/CleanCut/rusty_engine/tree/main/scenarios#common-setup) section of the scenarios readme to set up the skeleton of the project. +1. Follow the instructions in the [Common Setup](https://github.com/CleanCut/rusty_engine/tree/main/scenarios#common-setup-do-this-first) section of the scenarios readme to set up the skeleton of the project. ## Game Initialization diff --git a/scenarios/car_shoot.md b/scenarios/car_shoot.md index bd24ac1..5bb1043 100644 --- a/scenarios/car_shoot.md +++ b/scenarios/car_shoot.md @@ -2,36 +2,115 @@ Cars are floating past. Shoot them down! -You are at a carnival booth. Cars float across the back of the booth, occasionally obscured by obstacles. The player uses their marble gun to shoot down as many of the cars as possible before the time runs out. +You are at a carnival booth. Cars float across the back of the booth. The player uses their marble gun to shoot down as many of the cars as possible before the time runs out. This scenario can be extended to 2 players. ## Common Setup -1. Follow the instructions in the [Common Setup](https://github.com/CleanCut/rusty_engine/tree/main/scenarios#common-setup) section of the scenarios readme to set up the skeleton of the project. +1. Follow the instructions in the [Common Setup](https://github.com/CleanCut/rusty_engine/tree/main/scenarios#common-setup-do-this-first) section of the scenarios readme to set up the skeleton of the project. -## Game Initialization +## Engine Initialization -In your `// setup goes here` section of `main()`... +1. Define a `GameState` struct with the following fields: + - `marbles_left` - a vector of strings. These will be labels for our marble sprites. + - `cars_left` - an integer tracking how many cars are left to spawn + - `spawn_timer` - a timer indicating when it's time to spawn another car +1. Add `GameState` to your `init!` call -1. +## Game Setup -## Gameplay Logic -In your `game_logic(...)` function... +In your `// game setup goes here` section of `main`... + +1. (Optional) Set the [window title](https://cleancut.github.io/rusty_engine/450-game.html#window-settings) to be `Car Shooter` +1. (Optional) [Play some music.](https://cleancut.github.io/rusty_engine/205-music.html#play) We recommend the music preset `MusicPreset::Classy8Bit` at a volume of `0.1`. +1. [Create a player sprite.](https://cleancut.github.io/rusty_engine/55-sprite-creation.html) The player will be represented by a rectangle that represents the barrel of the marble gun. We'll use `SpritePreset::RacingBarrierRed` +1. We'll pretend the player is standing off the bottom of the screen, and only the barrel of their gun is visible. Let's place the sprite accordingly--set the sprite's: + - `rotation` to `UP` so it is pointing towards the top of the screen + - `scale` to `0.5` so it's about the right size + - `translation.y` to `-325.0` so it goes off the bottom of the screen a little + - `layer` to `10.0` so it will be on top of the marble which will be at a lower layer +1. Create an instance of your `GameState` struct, and set: + - `marbles_left` to the following vector of strings: `vec!["marble1".into(), "marble2".into(), "marble3".into()]` - we'll pop these off to use as labels for marble sprites, and then push them back on the vector when the marble sprites get destroyed. + - `cars_left` to a reasonable number such as `25` + - `spawn_timer` should be a `Timer` set to `0.0` seconds so that it goes off immediately. +1. Pass your game state variable to [`game.run()`](https://cleancut.github.io/rusty_engine/450-game.html#running-the-game) +1. [Create a `Text`](https://cleancut.github.io/rusty_engine/155-text-creation.html) with the label `"cars left"` that displays how many cars are left to spawn, with: + - the `value` of `format!("Cars left: {}", game_state.cars_left);` + - the `translation` of `Vec2::new(540.0, -320.0);` to put it in the bottom right corner of the screen. + +## Game Logic + +In your [`game_logic(...)` function](https://cleancut.github.io/rusty_engine/25-game-logic-function.html)... + +1. Have the "gun barrel" follow the mouse on the X axis by set the `translation.x` of the player sprite to the `x` value of the mouse location. + - Get a [mutable reference to the player sprite](https://cleancut.github.io/rusty_engine/60-sprite-transform.html#adjusting-an-existing-sprite) + - Get the mouse location via the [mouse state's `location` method](https://cleancut.github.io/rusty_engine/115-mouse-state.html#location) + - Make a variable `player_x` and set it to the player's current `translation.x` so we can use it later on: `let player_x = player.translation.x;` +1. If the [left mouse button was just pressed](https://cleancut.github.io/rusty_engine/115-mouse-state.html#mouse-buttons), then: + 1. [If there is](https://doc.rust-lang.org/book/ch06-03-if-let.html) a label string [left in the `game_state.marbles_left` vector](https://doc.rust-lang.org/std/vec/struct.Vec.html#method.pop), then: + - Using the label value, create a new marble sprite using `SpritePreset::RollingBallBlue` + - Make sure the label has been removed from the `game-state.marbles_left` vector. This way, we can only have as many marbles on thes screen as there are labels to remove from the vector. (We'll add the label back to the vector when we've finished with the marble). + - Set the marble sprite's: + - `translation.x` to the player's x location that we put in our `player_x` variable. + - `translation.y` to `-275.0`, which will put the marble under the end of the gun. + - `layer` to `5.0`, which will put it underneath the gun sprite + - `collision` to `true` so that we can detect collisions between the marble and the cars. + - [Play a sound effect](https://cleancut.github.io/rusty_engine/210-sfx.html#play) to indicate the firing of the marble. We suggest the sound effect preset `SfxPreset::Impact2` at a volume of `0.7`. +1. Move the marbles upwards (in the positive Y direction) + 1. Define a `MARBLE_SPEED` [constant](https://doc.rust-lang.org/std/keyword.const.html) (probably out in the module level) for how fast your marble should move and set it to the `f32` value of `600.0`. + 1. Loop through all the marble sprites (the sprites whose labels [start with](https://doc.rust-lang.org/std/string/struct.String.html#method.starts_with) `"marble"`), for each of them: + - increment the marble sprite's `translation.y` by `MARBLE_SPEED * engine_state.delta_f32` +1. Move cars right across the screen (in the positive X direction). No, we don't have any cars yet, but once we spawn them this code will move them! The logic for this section is _very_ similar to the previous section that moved marbles. + 1. Define a `CAR_SPEED` constant and set it to `250.0` + 1. Loop through all the car sprites (the sprites whose labels start with `"car"`), for each of them: + - increment the car sprite's `translation.x` by `CAR_SPEED * engine_state.delta_f32` +1. Clean up sprites that have moved off the top or the right side of the screen. + 1. We can't modify a hash map of sprites while we're looping through its values, so let's create an empty vector of strings and fill it with labels of sprites that we want to delete. Once we're done examining the hash map, we can loop through the vector of labels and remove those hash map entries. + 1. Create a new vector `labels_to_delete` + 1. For every sprite [value in the hash map](https://doc.rust-lang.org/std/collections/struct.HashMap.html#method.values): + - check to see if either the `translation.y > 400.0` or the `translation.x > 750.0`. If either of those conditions are true, push a clone of the label onto the `labels_to_delete` vector. + 1. For every label in `labels_to_delete`: + - [Remove the sprite entry.](https://cleancut.github.io/rusty_engine/60-sprite-transform.html#deleting-a-sprite) The hash map's `remove` method takes an immutable reference to the key type, so if you are looping through the label strings by value, you may need to add a `&` in front of your label variable: `engine_state.sprites.remove(&label)` +1. Spawn a car if the `game_state.spawn_timer` just finished! So [tick the spawn timer and check to see if it just finished](https://cleancut.github.io/rusty_engine/250-timer.html#counting-down--finishing) -- if it did, then: + 1. Set `game_state.spawn_timer` to a new `Timer` with a random value between `0.1` and `1.25` + - Add the `rand` crate as a dependency in your `Cargo.toml` + - Add `use rand::prelude::*;` to the top of your `main.rs` file + - Use `thread_rng().gen_range(0.1..1.25)` to obtain a random `f32` value between `0.1` and `1.25` + - [Create a non-repeating `Timer`](https://cleancut.github.io/rusty_engine/250-timer.html#creation) and assign it as the value to `game_state.spawn_timer` + 1. If there are any cars left (check the value of `game_state.cars_left`), then: + 1. Decrement `game_state.cars_left` by one + 1. [Retrieve a mutable reference to the](https://cleancut.github.io/rusty_engine/165-text-transform.html#adjusting-an-existing-text) `Text` we labeled `"cars left"` + - Set the `value` to `format!("Cars left: {}", game_state.cars_left)` + 1. Create a label for the current car that starts with `car`: `format!("car{}", game_state.cars_left)` (remember, a label starting with `car` is what the movement code is looking for). + 1. Create a vector of `SpritePreset`s of cars to randomly select from: `let car_choices = vec![SpritePreset::RacingCarBlack, SpritePreset::RacingCarBlue, SpritePreset::RacingCarGreen, SpritePreset::RacingCarRed, SpritePreset::RacingCarYellow];` + 1. Make a random sprite preset choice: `car_choices.iter().choose(&mut thread_rng()).unwrap().clone()` + 1. Actually create the sprite with the label and sprite preset selected above. Set the sprite's: + - `translation.x` to `-740.0` + - `translation.y` to a random value from `-100.0` to `325.0` -- `thread_rng().gen_range(-100.0..325.0)` + - `collision` to `true` so that the car will collide with marbles +1. Now it's time to handle the collisions! For each [`CollisionEvent`](https://docs.rs/rusty_engine/latest/rusty_engine/physics/struct.CollisionEvent.html) in `engine_state.collision_events`: + - We only care about the start of collisions, not the ending of them, so if `event.state.is_end()`, then `continue` the loop. + - Similarly, if one of the event pair's labels _doesn't_ start with `"marble"`, then it's either two marbles or two cars colliding with each other, which we don't care about. So if `!event.pair.one_starts_with("marble")`, then `continue` the loop. + - At this point we know that one of the pair is a marble and the other is a car, and they both need to be removed. So using the labels in the `event.pair` tuple, [delete both sprites](https://cleancut.github.io/rusty_engine/60-sprite-transform.html#deleting-a-sprite). + - Now that a marble has been "destroyed", we are allowed to shoot it from the gun again, so grab whichever label of the `event.pair` tuple that starts with `"marble"` and [push](https://doc.rust-lang.org/std/vec/struct.Vec.html#method.push) a clone of it back onto the `game_state.marbles_left` vector. + - [Play a sound effect](https://cleancut.github.io/rusty_engine/210-sfx.html#play) for successfully hitting a car with a marble. Use `SfxPreset::Confirmation1` with a volume of `0.5` + + +You made it to the end of the main scenario! You should have a playable game prototype by this point. -1. # Challenges * Keep track of points, display the points in a corner of the screen -* Different types of marbles -* Limited ammount of marbles -* Powerups! Powerups float across like cars +* Make it so that after the game ends, you can press a key and start a new game +* Keep track of the high score across games and display it when the game ends +* Don't allow cars to spawn on top of other cars +* Powerups! Powerups float across like cars and activate when hit. * Spread-fire * Rapid-fire * Explosion - clear the screen * Make the movement of the cars more interesting - have them drive in curvy motions * Smart black cars - black cars sometimes slow down or speed up so a shot will miss them * Armored cars - Green cars take two marbles to take down - diff --git a/scenarios/drivers_ed.md b/scenarios/drivers_ed.md index ed3dec4..aee9d37 100644 --- a/scenarios/drivers_ed.md +++ b/scenarios/drivers_ed.md @@ -8,7 +8,7 @@ This scenario can be extended to 2 players. ## Common Setup -1. Follow the instructions in the [Common Setup](https://github.com/CleanCut/rusty_engine/tree/main/scenarios#common-setup) section of the scenarios readme to set up the skeleton of the project. +1. Follow the instructions in the [Common Setup](https://github.com/CleanCut/rusty_engine/tree/main/scenarios#common-setup-do-this-first) section of the scenarios readme to set up the skeleton of the project. ## Game Initialization diff --git a/scenarios/labrinth.md b/scenarios/labrinth.md index a11fc71..e73ba83 100644 --- a/scenarios/labrinth.md +++ b/scenarios/labrinth.md @@ -6,7 +6,7 @@ This game consists of a [Labrinth](https://en.wikipedia.org/wiki/Labyrinth) or m ## Common Setup -1. Follow the instructions in the [Common Setup](https://github.com/CleanCut/rusty_engine/tree/main/scenarios#common-setup) section of the scenarios readme to set up the skeleton of the project. +1. Follow the instructions in the [Common Setup](https://github.com/CleanCut/rusty_engine/tree/main/scenarios#common-setup-do-this-first) section of the scenarios readme to set up the skeleton of the project. ## Game Initialization diff --git a/scenarios/space_invaders.md b/scenarios/space_invaders.md index 5239733..debe6a8 100644 --- a/scenarios/space_invaders.md +++ b/scenarios/space_invaders.md @@ -6,7 +6,7 @@ Similar to the classic [Space Invaders](https://en.wikipedia.org/wiki/Space_Inva ## Common Setup -1. Follow the instructions in the [Common Setup](https://github.com/CleanCut/rusty_engine/tree/main/scenarios#common-setup) section of the scenarios readme to set up the skeleton of the project. +1. Follow the instructions in the [Common Setup](https://github.com/CleanCut/rusty_engine/tree/main/scenarios#common-setup-do-this-first) section of the scenarios readme to set up the skeleton of the project. ## Game Initialization From d6c079654d423e4ac2fef582ec430847d9e0cc71 Mon Sep 17 00:00:00 2001 From: Nathan Stocks Date: Mon, 3 Jan 2022 17:43:01 -0700 Subject: [PATCH 02/11] use correct title for car shoot --- examples/scenarios/car_shoot.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/scenarios/car_shoot.rs b/examples/scenarios/car_shoot.rs index 5dac4f6..992168f 100644 --- a/examples/scenarios/car_shoot.rs +++ b/examples/scenarios/car_shoot.rs @@ -16,7 +16,7 @@ fn main() { // Set the title of the window to be Car Shooter game.window_settings(WindowDescriptor { - title: "Car Shooter".into(), + title: "Car Shoot".into(), ..Default::default() }); From 858410947472ba4fdb9e3edbc1279729daed0111 Mon Sep 17 00:00:00 2001 From: Nathan Stocks Date: Mon, 3 Jan 2022 17:56:31 -0700 Subject: [PATCH 03/11] add screenshots --- scenarios/car_shoot.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/scenarios/car_shoot.md b/scenarios/car_shoot.md index 5bb1043..a8e0138 100644 --- a/scenarios/car_shoot.md +++ b/scenarios/car_shoot.md @@ -48,6 +48,9 @@ In your [`game_logic(...)` function](https://cleancut.github.io/rusty_engine/25- - Get a [mutable reference to the player sprite](https://cleancut.github.io/rusty_engine/60-sprite-transform.html#adjusting-an-existing-sprite) - Get the mouse location via the [mouse state's `location` method](https://cleancut.github.io/rusty_engine/115-mouse-state.html#location) - Make a variable `player_x` and set it to the player's current `translation.x` so we can use it later on: `let player_x = player.translation.x;` + +screenshot1 + 1. If the [left mouse button was just pressed](https://cleancut.github.io/rusty_engine/115-mouse-state.html#mouse-buttons), then: 1. [If there is](https://doc.rust-lang.org/book/ch06-03-if-let.html) a label string [left in the `game_state.marbles_left` vector](https://doc.rust-lang.org/std/vec/struct.Vec.html#method.pop), then: - Using the label value, create a new marble sprite using `SpritePreset::RollingBallBlue` @@ -100,6 +103,7 @@ In your [`game_logic(...)` function](https://cleancut.github.io/rusty_engine/25- You made it to the end of the main scenario! You should have a playable game prototype by this point. +screenshot2 # Challenges From 7dbee6f98b72f7c4f6169fa3aa4431ca542055c2 Mon Sep 17 00:00:00 2001 From: Nathan Stocks Date: Mon, 3 Jan 2022 17:58:45 -0700 Subject: [PATCH 04/11] add demo movie --- scenarios/car_shoot.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/scenarios/car_shoot.md b/scenarios/car_shoot.md index a8e0138..ba775d8 100644 --- a/scenarios/car_shoot.md +++ b/scenarios/car_shoot.md @@ -6,6 +6,8 @@ You are at a carnival booth. Cars float across the back of the booth. The player This scenario can be extended to 2 players. +https://user-images.githubusercontent.com/5838512/147995928-4705d9fc-c3fa-41b7-901f-d120307e455f.mp4 + ## Common Setup 1. Follow the instructions in the [Common Setup](https://github.com/CleanCut/rusty_engine/tree/main/scenarios#common-setup-do-this-first) section of the scenarios readme to set up the skeleton of the project. @@ -118,3 +120,4 @@ You made it to the end of the main scenario! You should have a playable game pro * Make the movement of the cars more interesting - have them drive in curvy motions * Smart black cars - black cars sometimes slow down or speed up so a shot will miss them * Armored cars - Green cars take two marbles to take down +* Add support for a second player, with separate scores for each player. You'll need to figure out some way for the second player to control their marble gun... From 57f1b236dd4a2064b069f00ada839f81b2257c5d Mon Sep 17 00:00:00 2001 From: Nathan Stocks Date: Mon, 3 Jan 2022 21:35:10 -0700 Subject: [PATCH 05/11] add header explaining how to run the examples --- examples/collider_creator.rs | 6 ++++++ examples/collision.rs | 4 ++++ examples/game_state.rs | 4 ++++ examples/keyboard_events.rs | 4 ++++ examples/keyboard_state.rs | 4 ++++ examples/layer.rs | 4 ++++ examples/level_creator.rs | 4 ++++ examples/mouse_events.rs | 4 ++++ examples/mouse_state.rs | 4 ++++ examples/music.rs | 4 ++++ examples/music_sampler.rs | 4 ++++ examples/scenarios/car_shoot.rs | 4 ++++ examples/scenarios/extreme_drivers_ed.rs | 4 ++++ examples/scenarios/road_race.rs | 4 ++++ examples/sfx.rs | 4 ++++ examples/sfx_sampler.rs | 4 ++++ examples/sound.rs | 4 ++++ examples/sprite.rs | 5 ++++- examples/text.rs | 4 ++++ examples/transform.rs | 4 ++++ examples/window.rs | 4 ++++ 21 files changed, 86 insertions(+), 1 deletion(-) diff --git a/examples/collider_creator.rs b/examples/collider_creator.rs index 9da030c..c3bc099 100644 --- a/examples/collider_creator.rs +++ b/examples/collider_creator.rs @@ -1,3 +1,9 @@ +//! To run this code, clone the rusty_engine repository and run the command: +//! +//! cargo run --release --example collider_creator -- path/to/some_image.png +//! +//! ...where path/to/some_image.png is relative to assets/sprite/ + use std::path::PathBuf; use rusty_engine::prelude::*; diff --git a/examples/collision.rs b/examples/collision.rs index 9b2db07..cafc62a 100644 --- a/examples/collision.rs +++ b/examples/collision.rs @@ -1,3 +1,7 @@ +//! To run this code, clone the rusty_engine repository and run the command: +//! +//! cargo run --release --example collision + use rusty_engine::prelude::*; rusty_engine::init!(); diff --git a/examples/game_state.rs b/examples/game_state.rs index 6676e4c..b9d9985 100644 --- a/examples/game_state.rs +++ b/examples/game_state.rs @@ -1,3 +1,7 @@ +//! To run this code, clone the rusty_engine repository and run the command: +//! +//! cargo run --release --example game_state + use std::f32::consts::TAU; use rusty_engine::prelude::*; diff --git a/examples/keyboard_events.rs b/examples/keyboard_events.rs index ab30876..8b25150 100644 --- a/examples/keyboard_events.rs +++ b/examples/keyboard_events.rs @@ -1,3 +1,7 @@ +//! To run this code, clone the rusty_engine repository and run the command: +//! +//! cargo run --release --example keyboard_events + use rusty_engine::prelude::*; rusty_engine::init!(); diff --git a/examples/keyboard_state.rs b/examples/keyboard_state.rs index 3999f56..61fb8c8 100644 --- a/examples/keyboard_state.rs +++ b/examples/keyboard_state.rs @@ -1,3 +1,7 @@ +//! To run this code, clone the rusty_engine repository and run the command: +//! +//! cargo run --release --example keyboard_state + use std::f32::consts::PI; use rusty_engine::prelude::*; diff --git a/examples/layer.rs b/examples/layer.rs index 360114f..64dadf7 100644 --- a/examples/layer.rs +++ b/examples/layer.rs @@ -1,3 +1,7 @@ +//! To run this code, clone the rusty_engine repository and run the command: +//! +//! cargo run --release --example layer + use rusty_engine::prelude::*; rusty_engine::init!(); diff --git a/examples/level_creator.rs b/examples/level_creator.rs index 62cf78d..488302f 100644 --- a/examples/level_creator.rs +++ b/examples/level_creator.rs @@ -1,3 +1,7 @@ +//! To run this code, clone the rusty_engine repository and run the command: +//! +//! cargo run --release --example level_creator + use rusty_engine::prelude::*; struct GameState { diff --git a/examples/mouse_events.rs b/examples/mouse_events.rs index 5c0e523..15c1ebe 100644 --- a/examples/mouse_events.rs +++ b/examples/mouse_events.rs @@ -1,3 +1,7 @@ +//! To run this code, clone the rusty_engine repository and run the command: +//! +//! cargo run --release --example mouse_events + use rusty_engine::prelude::*; rusty_engine::init!(); diff --git a/examples/mouse_state.rs b/examples/mouse_state.rs index 97ee168..f731df1 100644 --- a/examples/mouse_state.rs +++ b/examples/mouse_state.rs @@ -1,3 +1,7 @@ +//! To run this code, clone the rusty_engine repository and run the command: +//! +//! cargo run --release --example mouse_state + use rusty_engine::prelude::*; rusty_engine::init!(); diff --git a/examples/music.rs b/examples/music.rs index f891d4f..1327df6 100644 --- a/examples/music.rs +++ b/examples/music.rs @@ -1,3 +1,7 @@ +//! To run this code, clone the rusty_engine repository and run the command: +//! +//! cargo run --release --example music + //! This is an example of playing a music preset. For playing your own music file, please see the //! `sound` example. diff --git a/examples/music_sampler.rs b/examples/music_sampler.rs index ecabc4c..cf87b25 100644 --- a/examples/music_sampler.rs +++ b/examples/music_sampler.rs @@ -1,3 +1,7 @@ +//! To run this code, clone the rusty_engine repository and run the command: +//! +//! cargo run --release --example music_sampler + use rusty_engine::prelude::*; struct GameState { diff --git a/examples/scenarios/car_shoot.rs b/examples/scenarios/car_shoot.rs index 992168f..0d6477a 100644 --- a/examples/scenarios/car_shoot.rs +++ b/examples/scenarios/car_shoot.rs @@ -1,3 +1,7 @@ +//! To run this code, clone the rusty_engine repository and run the command: +//! +//! cargo run --release --example car_shoot + use rand::prelude::*; use rusty_engine::prelude::*; use SpritePreset::*; // The SpritePreset enum was imported from rusty_engine::prelude diff --git a/examples/scenarios/extreme_drivers_ed.rs b/examples/scenarios/extreme_drivers_ed.rs index 5d2505c..4f5e473 100644 --- a/examples/scenarios/extreme_drivers_ed.rs +++ b/examples/scenarios/extreme_drivers_ed.rs @@ -1,3 +1,7 @@ +//! To run this code, clone the rusty_engine repository and run the command: +//! +//! cargo run --release --example extreme_drivers_ed + use rusty_engine::prelude::*; struct GameState { diff --git a/examples/scenarios/road_race.rs b/examples/scenarios/road_race.rs index c447743..99d54a2 100644 --- a/examples/scenarios/road_race.rs +++ b/examples/scenarios/road_race.rs @@ -1,3 +1,7 @@ +//! To run this code, clone the rusty_engine repository and run the command: +//! +//! cargo run --release --example road_race + use rand::prelude::*; use rusty_engine::prelude::*; use SpritePreset::*; // The SpritePreset enum was imported from rusty_engine::prelude diff --git a/examples/sfx.rs b/examples/sfx.rs index 91f33c4..9d05107 100644 --- a/examples/sfx.rs +++ b/examples/sfx.rs @@ -1,3 +1,7 @@ +//! To run this code, clone the rusty_engine repository and run the command: +//! +//! cargo run --release --example sfx + //! This is an example of playing a sound effect preset. For playing your own sound effect file, //! please see the `sound` example. diff --git a/examples/sfx_sampler.rs b/examples/sfx_sampler.rs index 6b7c68a..2f66345 100644 --- a/examples/sfx_sampler.rs +++ b/examples/sfx_sampler.rs @@ -1,3 +1,7 @@ +//! To run this code, clone the rusty_engine repository and run the command: +//! +//! cargo run --release --example sfx_sampler + use rusty_engine::prelude::*; #[derive(Default)] diff --git a/examples/sound.rs b/examples/sound.rs index 4877a1b..39a06ac 100644 --- a/examples/sound.rs +++ b/examples/sound.rs @@ -1,3 +1,7 @@ +//! To run this code, clone the rusty_engine repository and run the command: +//! +//! cargo run --release --example sound + //! This is an example of playing sound by path. For playing music or sound effect presets, please //! see the `music` or `sfx` examples. diff --git a/examples/sprite.rs b/examples/sprite.rs index 931dbea..16221a4 100644 --- a/examples/sprite.rs +++ b/examples/sprite.rs @@ -1,4 +1,7 @@ -// +//! To run this code, clone the rusty_engine repository and run the command: +//! +//! cargo run --release --example sprite + use rusty_engine::prelude::*; rusty_engine::init!(); diff --git a/examples/text.rs b/examples/text.rs index ef56edd..4b8fb8c 100644 --- a/examples/text.rs +++ b/examples/text.rs @@ -1,3 +1,7 @@ +//! To run this code, clone the rusty_engine repository and run the command: +//! +//! cargo run --release --example text + use rusty_engine::prelude::*; struct GameState { diff --git a/examples/transform.rs b/examples/transform.rs index 5c51d04..90a9dff 100644 --- a/examples/transform.rs +++ b/examples/transform.rs @@ -1,3 +1,7 @@ +//! To run this code, clone the rusty_engine repository and run the command: +//! +//! cargo run --release --example transform + use rusty_engine::prelude::*; rusty_engine::init!(); diff --git a/examples/window.rs b/examples/window.rs index 8090c69..36a1259 100644 --- a/examples/window.rs +++ b/examples/window.rs @@ -1,3 +1,7 @@ +//! To run this code, clone the rusty_engine repository and run the command: +//! +//! cargo run --release --example window + use rusty_engine::prelude::*; rusty_engine::init!(); From e6ec900af19d265a651629a6c5837c5672401b28 Mon Sep 17 00:00:00 2001 From: Nathan Stocks Date: Mon, 10 Jan 2022 22:18:09 -0700 Subject: [PATCH 06/11] wip --- scenarios/README.md | 2 +- scenarios/car_shoot.md | 2 ++ scenarios/drivers_ed.md | 23 ------------------- scenarios/extreme_drivers_ed.md | 39 +++++++++++++++++++++++++++++++++ 4 files changed, 42 insertions(+), 24 deletions(-) delete mode 100644 scenarios/drivers_ed.md create mode 100644 scenarios/extreme_drivers_ed.md diff --git a/scenarios/README.md b/scenarios/README.md index 31b3a89..8b5deda 100644 --- a/scenarios/README.md +++ b/scenarios/README.md @@ -54,7 +54,7 @@ Legend: - (Easy) [Road Race](https://github.com/CleanCut/rusty_engine/tree/main/scenarios/road_race.md) - (Medium) [Car Shoot](https://github.com/CleanCut/rusty_engine/tree/main/scenarios/car_shoot.md) -- (Medium) [Driver's Ed](https://github.com/CleanCut/rusty_engine/tree/main/scenarios/car_shoot.md) +- (Medium) [Driver's Ed](https://github.com/CleanCut/rusty_engine/tree/main/scenarios/extreme_drivers_ed.md) - (Hard) [Cannon Practice](https://github.com/CleanCut/rusty_engine/tree/main/scenarios/cannon_practice.md) - (Hard) [Space Invaders](https://github.com/CleanCut/rusty_engine/tree/main/scenarios/space_invaders.md) - (Hard) [Labrinth](https://github.com/CleanCut/rusty_engine/tree/main/scenarios/labrinth.md) diff --git a/scenarios/car_shoot.md b/scenarios/car_shoot.md index ba775d8..5c6d7c6 100644 --- a/scenarios/car_shoot.md +++ b/scenarios/car_shoot.md @@ -6,6 +6,8 @@ You are at a carnival booth. Cars float across the back of the booth. The player This scenario can be extended to 2 players. +- [Reference Code](https://github.com/CleanCut/rusty_engine/blob/main/examples/scenarios/car_shoot.rs) + https://user-images.githubusercontent.com/5838512/147995928-4705d9fc-c3fa-41b7-901f-d120307e455f.mp4 ## Common Setup diff --git a/scenarios/drivers_ed.md b/scenarios/drivers_ed.md deleted file mode 100644 index aee9d37..0000000 --- a/scenarios/drivers_ed.md +++ /dev/null @@ -1,23 +0,0 @@ -# Extreme Driver's Education - -Can you survive your driving exam? - -The screen represents a driving course full of obstacles. Carefully avoid the obstacles while driving your car around to collect all of the rewards. Only a master driver will pass this test. - -This scenario can be extended to 2 players. - -## Common Setup - -1. Follow the instructions in the [Common Setup](https://github.com/CleanCut/rusty_engine/tree/main/scenarios#common-setup-do-this-first) section of the scenarios readme to set up the skeleton of the project. - -## Game Initialization - -In your `// setup goes here` section of `main()`... - -1. - -## Gameplay Logic - -In your `game_logic(...)` function... - -1. diff --git a/scenarios/extreme_drivers_ed.md b/scenarios/extreme_drivers_ed.md new file mode 100644 index 0000000..f61e87f --- /dev/null +++ b/scenarios/extreme_drivers_ed.md @@ -0,0 +1,39 @@ +# Extreme Driver's Education + +Can you survive your driving exam? + +Navigate a driving course full of obstacles. Carefully avoid the obstacles while driving your car around to collect all of the white circles. Only a master driver will pass this test. + +- [Reference Code](https://github.com/CleanCut/rusty_engine/blob/main/examples/scenarios/extreme_drivers_ed.rs) + +## Common Setup + +1. Follow the instructions in the [Common Setup](https://github.com/CleanCut/rusty_engine/tree/main/scenarios#common-setup-do-this-first) section of the scenarios readme to set up the skeleton of the project. + +## Level Setup + +It can be _really_ tedious to set up dozens of obstacles via code and guessing coordinates. Instead, clone the `rusty_engine` repository and use the `level_creator` example to place several dozen obstacles and emit the level code for you to copy-and-paste into your own project. + +The sprite preset `SpritePreset::RollingHoleStart` are the goals for collecting (you _want_ to run into them). All other sprites will be obstacles. + +``` +git clone https://github.com/CleanCut/rusty_engine.git +cd rusty_engine +cargo run --release --example level_creator +``` + +## Engine Initialization + +- + +## Game Setup + +In your `// game setup goes here` section of `main`... + +1. + +## Game Logic + +In your `game_logic(...)` function... + +1. From 1086e5fdf80170f4b3945023dbfa20a3fa530484 Mon Sep 17 00:00:00 2001 From: Nathan Stocks Date: Sat, 15 Jan 2022 13:14:15 -0700 Subject: [PATCH 07/11] Wrote the whole cannon_practice scenario and some of the labrinth scenario --- scenarios/cannon_practice.md | 54 ++++++++++++++++++++++++++++++++++-- scenarios/labrinth.md | 21 ++++++++++++-- 2 files changed, 71 insertions(+), 4 deletions(-) diff --git a/scenarios/cannon_practice.md b/scenarios/cannon_practice.md index 202988b..8516072 100644 --- a/scenarios/cannon_practice.md +++ b/scenarios/cannon_practice.md @@ -8,14 +8,64 @@ In this scenario you will create a cannon that sits on the bottom left side of t 1. Follow the instructions in the [Common Setup](https://github.com/CleanCut/rusty_engine/tree/main/scenarios#common-setup-do-this-first) section of the scenarios readme to set up the skeleton of the project. +## Game State & Constants + +1. Define a game state struct with fields for: + - The firing magnitude of the cannon (an `f32`) + - The rotation of the cannon (an `f32`) + - The current velocity of the cannon ball (a `Vec2`) +1. Define a constant for acceleration due to gravity. The unit will be pixels per second per second. +1. Decide on a sprite to use as a cannon ball +1. Decide on a sprite to use as the cannon +1. Decide on a sprite to use for the goal that you are trying to hit +1. Decide on a sprite (or sprites) to use for obstacles that you should avoid hitting + ## Game Initialization In your `// setup goes here` section of `main()`... -1. +1. Create the initial game state struct with good starting values +1. Create and place the cannon, obstacles, and goal sprites. You may use the `level_creator` example to do this, if you wish. + - Place the cannon on the lower left side of the screen + - Use a field from the game state to set the rotation of the cannon + - Place a single obstacle in lower middle of the screen (so you have to fire over it) + - Place the goal on the lower right side of the screen +1. Create the text for displaying the firing magnitude of the cannon, place it in the top left corner of the screen. +1. If you want music, start it now. ## Gameplay Logic In your `game_logic(...)` function... -1. +1. Decide which keyboard/mouse input will control the rotation of the cannon, and implement rotating the cannon. + - Constrain the min/max angle of rotation to angles in the first quadrant (from straight up to straight right) with [the `.clamp` method](https://doc.rust-lang.org/std/primitive.f32.html#method.clamp) and the [`UP` and `RIGHT` constants](https://docs.rs/rusty_engine/latest/rusty_engine/#constants). +1. Decide which keyboard/mouse input will fire the cannon. Implement it so that pressing (whatever you chose) creates a cannon ball sprite, but only if one does not exist. Place it at the same coordinates as the cannon, but at a layer lower than the cannon so the cannon obscures it until it is out from underneath it. + - Play a sound when the cannon is fired. +1. Set the initial velocity `Vec2` for the cannon ball. This is fairly straightforward math: +```rust +let initial_cannonball_velocity = Vec2::new( + game_state.firing_magnitude * cannon.rotation.cos(), + game_state.firing_magnitude * cannon.rotation.sin(), +); +``` + +1. Move the cannon ball sprite by the amount stored in the game state's velocity field multiplied by `engine_state.delta_f32` each frame. At this point, you should be able to run the game, rotate the cannon, fire the cannon, and see the cannon move across the screen in a straight line. +1. Implement the gravity logic. Each frame, subtract (_gravity constant_ * `engine_state.delta_f32`) from the "Y" value of the cannon ball's velocity. +1. Decide which keyboard/mouse input will change the firing magnitude of the cannon, and implement it. + - Constrain the firing magnitude between `0.0` and some semi-reasonable value. + - Every time the firing magnitude changes, change the value of the `Text` that is displaying it in the top left corner of the screen. Don't change the value of the `Text` if the firing magnitude didn't change. +1. Detect collisions between the cannon ball and obstacles. The cannon ball should be destroyed if it hits an obstacle. + 1. For collisions to be detected between two sprites, the `.collision` field of _both_ sprites must be set to true. Set this field on the cannon ball, the obstacles, and the goal. + 1. Play a sound when the cannon ball hits an obstacle +1. Detect collisions between the cannon ball and the goal. The game is won if the cannon ball hits the goal. + 1. Play a sound when the cannon ball hits the goal. + + +## Challenge + +- Introduce constant wind that varies between shots +- Make the obstactle move, rotate, or scale dynamically to make it so you have to time your shot correctly as well +- Replace the `Text` displaying the magnitude of your starting velocity with a visual slider (literally slide a barrier from the edge of the screen to some pre-defined point) +- Make destructible obstactles that reduce the cannon ball's velocity by half in the X direction +- Allow the cannon to move a small distance in the +/- X direction +- Add scorekeeping and alter the layout of the obstacles each time the cannon hits the goal diff --git a/scenarios/labrinth.md b/scenarios/labrinth.md index e73ba83..f78b0c2 100644 --- a/scenarios/labrinth.md +++ b/scenarios/labrinth.md @@ -8,14 +8,31 @@ This game consists of a [Labrinth](https://en.wikipedia.org/wiki/Labyrinth) or m 1. Follow the instructions in the [Common Setup](https://github.com/CleanCut/rusty_engine/tree/main/scenarios#common-setup-do-this-first) section of the scenarios readme to set up the skeleton of the project. +## Game State + +1. Define a game state struct with fields for: + - Current velocity of the marble (a `Vec2`) + - Lives left (a u8) +1. Choose a sprite to represent the player's marble +1. Choose a sprite to represent the starting area or spot +1. Choose a sprite to represent holes in the labrinth +1. Choose a sprite to represent the ending area or spot +1. Choose a sprite to represent walls of the labrinth + ## Game Initialization In your `// setup goes here` section of `main()`... -1. +1. Use the `level_creator` example to create a labrinth (maze) with the sprite you selected for walls. + - Place "holes" as obstacles to avoid. + - Place one "starting area" sprite, where the marble will start on top of + - Place one "ending area" sprite, which will signal winning the game when touched + - Save out the game, copy and paste the sprite positioning code into your `main.rs` +1. Create the player's marble sprite and place it at the same coordinates as the "starting area" sprite, but at a high layer so it will be on top of any sprites it overlaps. +1. If you would like music, start playing it now. ## Gameplay Logic In your `game_logic(...)` function... -1. +1. We will move the marble by virtually tilting the whole labrinth. The relative movement of the mouse will do the tilting. The more tilted the labrinth is, the faster the marble will accelerate in that direction. From a194847919cc18577428cb294b1c75cf793fd8fb Mon Sep 17 00:00:00 2001 From: Nathan Stocks Date: Sat, 15 Jan 2022 14:56:02 -0700 Subject: [PATCH 08/11] huh, how did those spaces get there? --- examples/collision.rs | 2 +- examples/game_state.rs | 2 +- examples/keyboard_events.rs | 2 +- examples/keyboard_state.rs | 2 +- examples/layer.rs | 2 +- examples/level_creator.rs | 2 +- examples/mouse_events.rs | 2 +- examples/mouse_state.rs | 2 +- examples/music.rs | 2 +- examples/music_sampler.rs | 2 +- examples/scenarios/extreme_drivers_ed.rs | 2 +- examples/sfx.rs | 2 +- examples/sfx_sampler.rs | 2 +- examples/sound.rs | 2 +- examples/sprite.rs | 2 +- examples/text.rs | 2 +- examples/transform.rs | 2 +- examples/window.rs | 2 +- 18 files changed, 18 insertions(+), 18 deletions(-) diff --git a/examples/collision.rs b/examples/collision.rs index 439c307..09f1e6e 100644 --- a/examples/collision.rs +++ b/examples/collision.rs @@ -1,5 +1,5 @@ //! To run this code, clone the rusty_engine repository and run the command: -//! +//! //! cargo run --release --example collision use rusty_engine::prelude::*; diff --git a/examples/game_state.rs b/examples/game_state.rs index 0ce98c8..97b120c 100644 --- a/examples/game_state.rs +++ b/examples/game_state.rs @@ -1,5 +1,5 @@ //! To run this code, clone the rusty_engine repository and run the command: -//! +//! //! cargo run --release --example game_state use std::f32::consts::TAU; diff --git a/examples/keyboard_events.rs b/examples/keyboard_events.rs index 29c5d14..68dcd2d 100644 --- a/examples/keyboard_events.rs +++ b/examples/keyboard_events.rs @@ -1,5 +1,5 @@ //! To run this code, clone the rusty_engine repository and run the command: -//! +//! //! cargo run --release --example keyboard_events use rusty_engine::prelude::*; diff --git a/examples/keyboard_state.rs b/examples/keyboard_state.rs index 5bc3dac..2569dd2 100644 --- a/examples/keyboard_state.rs +++ b/examples/keyboard_state.rs @@ -1,5 +1,5 @@ //! To run this code, clone the rusty_engine repository and run the command: -//! +//! //! cargo run --release --example keyboard_state use std::f32::consts::PI; diff --git a/examples/layer.rs b/examples/layer.rs index 66728b1..5dbf879 100644 --- a/examples/layer.rs +++ b/examples/layer.rs @@ -1,5 +1,5 @@ //! To run this code, clone the rusty_engine repository and run the command: -//! +//! //! cargo run --release --example layer use rusty_engine::prelude::*; diff --git a/examples/level_creator.rs b/examples/level_creator.rs index 180fc83..3c1d75f 100644 --- a/examples/level_creator.rs +++ b/examples/level_creator.rs @@ -1,5 +1,5 @@ //! To run this code, clone the rusty_engine repository and run the command: -//! +//! //! cargo run --release --example level_creator use rusty_engine::prelude::*; diff --git a/examples/mouse_events.rs b/examples/mouse_events.rs index 6adfd3b..81baf77 100644 --- a/examples/mouse_events.rs +++ b/examples/mouse_events.rs @@ -1,5 +1,5 @@ //! To run this code, clone the rusty_engine repository and run the command: -//! +//! //! cargo run --release --example mouse_events use rusty_engine::prelude::*; diff --git a/examples/mouse_state.rs b/examples/mouse_state.rs index 671c052..5712d2c 100644 --- a/examples/mouse_state.rs +++ b/examples/mouse_state.rs @@ -1,5 +1,5 @@ //! To run this code, clone the rusty_engine repository and run the command: -//! +//! //! cargo run --release --example mouse_state use rusty_engine::prelude::*; diff --git a/examples/music.rs b/examples/music.rs index d3b68e8..ef19230 100644 --- a/examples/music.rs +++ b/examples/music.rs @@ -1,5 +1,5 @@ //! To run this code, clone the rusty_engine repository and run the command: -//! +//! //! cargo run --release --example music //! This is an example of playing a music preset. For playing your own music file, please see the diff --git a/examples/music_sampler.rs b/examples/music_sampler.rs index 4db41f7..7b6a668 100644 --- a/examples/music_sampler.rs +++ b/examples/music_sampler.rs @@ -1,5 +1,5 @@ //! To run this code, clone the rusty_engine repository and run the command: -//! +//! //! cargo run --release --example music_sampler use rusty_engine::prelude::*; diff --git a/examples/scenarios/extreme_drivers_ed.rs b/examples/scenarios/extreme_drivers_ed.rs index 8202fb2..f4c3ba6 100644 --- a/examples/scenarios/extreme_drivers_ed.rs +++ b/examples/scenarios/extreme_drivers_ed.rs @@ -1,5 +1,5 @@ //! To run this code, clone the rusty_engine repository and run the command: -//! +//! //! cargo run --release --example extreme_drivers_ed use rusty_engine::prelude::*; diff --git a/examples/sfx.rs b/examples/sfx.rs index b16f234..2a28650 100644 --- a/examples/sfx.rs +++ b/examples/sfx.rs @@ -1,5 +1,5 @@ //! To run this code, clone the rusty_engine repository and run the command: -//! +//! //! cargo run --release --example sfx //! This is an example of playing a sound effect preset. For playing your own sound effect file, diff --git a/examples/sfx_sampler.rs b/examples/sfx_sampler.rs index 54a018a..214ab12 100644 --- a/examples/sfx_sampler.rs +++ b/examples/sfx_sampler.rs @@ -1,5 +1,5 @@ //! To run this code, clone the rusty_engine repository and run the command: -//! +//! //! cargo run --release --example sfx_sampler use rusty_engine::prelude::*; diff --git a/examples/sound.rs b/examples/sound.rs index 798b702..1f99717 100644 --- a/examples/sound.rs +++ b/examples/sound.rs @@ -1,5 +1,5 @@ //! To run this code, clone the rusty_engine repository and run the command: -//! +//! //! cargo run --release --example sound //! This is an example of playing sound by path. For playing music or sound effect presets, please diff --git a/examples/sprite.rs b/examples/sprite.rs index 9343f70..4fb7762 100644 --- a/examples/sprite.rs +++ b/examples/sprite.rs @@ -1,5 +1,5 @@ //! To run this code, clone the rusty_engine repository and run the command: -//! +//! //! cargo run --release --example sprite use rusty_engine::prelude::*; diff --git a/examples/text.rs b/examples/text.rs index fe8fc58..8ed3909 100644 --- a/examples/text.rs +++ b/examples/text.rs @@ -1,5 +1,5 @@ //! To run this code, clone the rusty_engine repository and run the command: -//! +//! //! cargo run --release --example text use rusty_engine::prelude::*; diff --git a/examples/transform.rs b/examples/transform.rs index 38eb7f4..3d7933a 100644 --- a/examples/transform.rs +++ b/examples/transform.rs @@ -1,5 +1,5 @@ //! To run this code, clone the rusty_engine repository and run the command: -//! +//! //! cargo run --release --example transform use rusty_engine::prelude::*; diff --git a/examples/window.rs b/examples/window.rs index d6dcf20..b9fd19a 100644 --- a/examples/window.rs +++ b/examples/window.rs @@ -1,5 +1,5 @@ //! To run this code, clone the rusty_engine repository and run the command: -//! +//! //! cargo run --release --example window use rusty_engine::prelude::*; From e54ba959f9f3d40fb8c7d3364c9f01598cb5201f Mon Sep 17 00:00:00 2001 From: Nathan Stocks Date: Sat, 15 Jan 2022 15:19:42 -0700 Subject: [PATCH 09/11] wrote the labrinth scenario...unfortunately the engine doesn't support the whole scenario --- scenarios/cannon_practice.md | 2 +- scenarios/extreme_drivers_ed.md | 2 +- scenarios/labrinth.md | 37 ++++++++++++++++++++++++++++++--- scenarios/space_invaders.md | 2 +- 4 files changed, 37 insertions(+), 6 deletions(-) diff --git a/scenarios/cannon_practice.md b/scenarios/cannon_practice.md index 8516072..3a8607d 100644 --- a/scenarios/cannon_practice.md +++ b/scenarios/cannon_practice.md @@ -35,7 +35,7 @@ In your `// setup goes here` section of `main()`... ## Gameplay Logic -In your `game_logic(...)` function... +In your [`game_logic(...)` function](https://cleancut.github.io/rusty_engine/25-game-logic-function.html)... 1. Decide which keyboard/mouse input will control the rotation of the cannon, and implement rotating the cannon. - Constrain the min/max angle of rotation to angles in the first quadrant (from straight up to straight right) with [the `.clamp` method](https://doc.rust-lang.org/std/primitive.f32.html#method.clamp) and the [`UP` and `RIGHT` constants](https://docs.rs/rusty_engine/latest/rusty_engine/#constants). diff --git a/scenarios/extreme_drivers_ed.md b/scenarios/extreme_drivers_ed.md index f61e87f..a93e514 100644 --- a/scenarios/extreme_drivers_ed.md +++ b/scenarios/extreme_drivers_ed.md @@ -34,6 +34,6 @@ In your `// game setup goes here` section of `main`... ## Game Logic -In your `game_logic(...)` function... +In your [`game_logic(...)` function](https://cleancut.github.io/rusty_engine/25-game-logic-function.html)... 1. diff --git a/scenarios/labrinth.md b/scenarios/labrinth.md index f78b0c2..e328506 100644 --- a/scenarios/labrinth.md +++ b/scenarios/labrinth.md @@ -2,6 +2,8 @@ Guide the marble from the beginning to the end of the labrinth...but don't fall in any holes! +NOTE: This scenario is not fully supported by the capabilities of the engine. You will need to supplement the engine with your own physics logic and/or make changes to the engine itself to accomplish this complete scenario. This is included here because we _might_ add enough features to support this scenario in the future. You're certainly welcome to help! + This game consists of a [Labrinth](https://en.wikipedia.org/wiki/Labyrinth) or maze with a beginning and an end. The marble starts at the beginning of the labrynth (naturally) and must proceed to the end. Sounds easy...until you realize that there are holes all along the maze, and you don't have perfect control of the marble! If you fall in one of the holes, start over from the beginning. ## Common Setup @@ -11,8 +13,13 @@ This game consists of a [Labrinth](https://en.wikipedia.org/wiki/Labyrinth) or m ## Game State 1. Define a game state struct with fields for: + - Current tilt of the labrinth (a `Vec2`) - Current velocity of the marble (a `Vec2`) - - Lives left (a u8) + - Lives left (a `u8`) +1. Define constants for: + - Marble movement speed (an `f32`) + - Maximum tilt magnitude (an `f32`) + - Maximum marble speed (an `f32`) 1. Choose a sprite to represent the player's marble 1. Choose a sprite to represent the starting area or spot 1. Choose a sprite to represent holes in the labrinth @@ -33,6 +40,30 @@ In your `// setup goes here` section of `main()`... ## Gameplay Logic -In your `game_logic(...)` function... +In your [`game_logic(...)` function](https://cleancut.github.io/rusty_engine/25-game-logic-function.html)... + +1. We will move the marble by virtually tilting the whole labrinth (even though it won't look like we're tilting it). The relative movement of the mouse will do the tilting. The more tilted the labrinth is, the faster the marble will accelerate in that direction. + - Collect [mouse movement events](https://cleancut.github.io/rusty_engine/120-mouse-events.html#mouse-motion-events) (not location events!) and add them to the current tilt of the labrinth + - Clamp the maximum length of the tilt `Vec2` to the maximum tilt magnitude constant with [the `.clamp_length_max` method](https://docs.rs/glam/latest/glam/f32/struct.Vec2.html#method.clamp_length_max). +1. Accelerate the marble. + - Each frame, multiply the tilt `Vec2` in the game state by the movement speed constant AND `engine_state.delta_f32`, and then add that resulting `Vec2` to the marble velocity in the game state. Clamp the maximum length of the marble velocity using the maximum marble velocity constant with [the `.clamp_length_max` method](https://docs.rs/glam/latest/glam/f32/struct.Vec2.html#method.clamp_length_max). + - Each frame, increment the marble sprite's translation by the velocity in the game state. + - At this point, you should be able to get the marble to move around the screen (though it ignores all the other sprites). Play with all the constant values until you get something that feels reasonable. For the game to be playable, you'll need a decently large max tilt magnitude paired with a relatively small movement speed and small max movement speed to give you enough control over the marble. But you don't want _too_ much control...it should feel like rolling a marble on a flat surface by tilting it. + +## The sort of unfinished part + +This section isn't well-supported by the underlying engine. 😬 Sorry. + +1. Make it so that you can't go through your barriers. + - Missing engine feature: Collision contact normals. +1. Make it so that if the center of the marble overlaps a hole, you lose. + - Missing engine feature: Testing if an arbitrary `Vec2` is within a sprite's collider. + +## The rest + +1. When you touch the goal, you win! + + +## Challenges -1. We will move the marble by virtually tilting the whole labrinth. The relative movement of the mouse will do the tilting. The more tilted the labrinth is, the faster the marble will accelerate in that direction. +- When you fall down a hole, reset the game nicely and keep playing, keeping track of number of tries. diff --git a/scenarios/space_invaders.md b/scenarios/space_invaders.md index debe6a8..55fb6ee 100644 --- a/scenarios/space_invaders.md +++ b/scenarios/space_invaders.md @@ -16,6 +16,6 @@ In your `// setup goes here` section of `main()`... ## Gameplay Logic -In your `game_logic(...)` function... +In your [`game_logic(...)` function](https://cleancut.github.io/rusty_engine/25-game-logic-function.html)... 1. From 1994b712cce36b9c45209946e539863cf7c19203 Mon Sep 17 00:00:00 2001 From: Nathan Stocks Date: Sat, 15 Jan 2022 15:58:39 -0700 Subject: [PATCH 10/11] label the labrinth scenario as 'insane' difficulty --- scenarios/README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scenarios/README.md b/scenarios/README.md index b24e280..f2c3209 100644 --- a/scenarios/README.md +++ b/scenarios/README.md @@ -47,6 +47,7 @@ Legend: | Easy | You will be told each step, and each section includes the code that you should have ended up with, and there is a complete reference project. | | Medium | You will be told each step, but won't be shown all the code. There might be a reference project. | | Hard | You will be told what to accomplish, and maybe be given a couple pointers. There is probably no reference project. | +| Insane | You'll need to implement some game engine features yourself | ## Scenarios @@ -55,4 +56,4 @@ Legend: - (Medium) [Driver's Ed](https://github.com/CleanCut/rusty_engine/tree/main/scenarios/extreme_drivers_ed.md) - (Hard) [Cannon Practice](https://github.com/CleanCut/rusty_engine/tree/main/scenarios/cannon_practice.md) - (Hard) [Space Invaders](https://github.com/CleanCut/rusty_engine/tree/main/scenarios/space_invaders.md) -- (Hard) [Labrinth](https://github.com/CleanCut/rusty_engine/tree/main/scenarios/labrinth.md) +- (Insane) [Labrinth](https://github.com/CleanCut/rusty_engine/tree/main/scenarios/labrinth.md) - Rusty Engine doesn't yet provide all the features needed to implement this scenario. From c9f743afe38e6e73cdba7a08d89d0b3090b74a7e Mon Sep 17 00:00:00 2001 From: Nathan Stocks Date: Sat, 15 Jan 2022 16:01:19 -0700 Subject: [PATCH 11/11] add bit about updating scenarios to the changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8e898f3..6a9b2e7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ - Upgraded to Bevy 0.6 in the back end - `Text` rotation and scale now works! 🎉 - TODO: bevy_prototype_debug_lines hasn't had a release, and the `main` branch sorta works, but the lines now appear _under_ sprites instead of over them, which is not ideal. We _must_ have a release upstream and would _love_ a fix upstream. If there isn't an upstream release, we'll need to find another line-drawing solution before release. +- Updated (or finished) all of the game scenario descriptions. ## [3.0.0] - 2021-12-30