From cad807b2192bed3a28ee65fe2fbc464c6597420e Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Mon, 3 May 2021 20:29:09 -0400 Subject: [PATCH 01/47] Cleaned up time step by moving to a separate config --- examples/game/breakout.rs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/examples/game/breakout.rs b/examples/game/breakout.rs index 5fc2ccf143efa..feacd09b3a213 100644 --- a/examples/game/breakout.rs +++ b/examples/game/breakout.rs @@ -5,8 +5,12 @@ use bevy::{ sprite::collide_aabb::{collide, Collision}, }; +/// Constants that can be used to fine-tune the behavior of our game +mod config { + const TIME_STEP: f64 = 1.0 / 60.0; +} + /// An implementation of the classic game "Breakout" -const TIME_STEP: f32 = 1.0 / 60.0; fn main() { App::build() .add_plugins(DefaultPlugins) @@ -15,7 +19,7 @@ fn main() { .add_startup_system(setup.system()) .add_system_set( SystemSet::new() - .with_run_criteria(FixedTimestep::step(TIME_STEP as f64)) + .with_run_criteria(FixedTimestep::step(config::TIME_STEP)) .with_system(paddle_movement_system.system()) .with_system(ball_collision_system.system()) .with_system(ball_movement_system.system()), @@ -196,7 +200,8 @@ fn paddle_movement_system( let translation = &mut transform.translation; // move the paddle horizontally - translation.x += direction * paddle.speed * TIME_STEP; + // FIXME: this should use delta_time + translation.x += direction * paddle.speed * config::TIME_STEP; // bound the paddle within the walls translation.x = translation.x.min(380.0).max(-380.0); } @@ -204,7 +209,8 @@ fn paddle_movement_system( fn ball_movement_system(mut ball_query: Query<(&Ball, &mut Transform)>) { if let Ok((ball, mut transform)) = ball_query.single_mut() { - transform.translation += ball.velocity * TIME_STEP; + // FIXME: this should use delta_time + transform.translation += ball.velocity * config::TIME_STEP; } } From 168f33f7cbe6edc44512309650cced655fb587e5 Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Mon, 3 May 2021 20:38:12 -0400 Subject: [PATCH 02/47] Organize resources and components into modules --- examples/game/breakout.rs | 33 ++++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/examples/game/breakout.rs b/examples/game/breakout.rs index feacd09b3a213..0b5c5974fe81a 100644 --- a/examples/game/breakout.rs +++ b/examples/game/breakout.rs @@ -5,12 +5,15 @@ use bevy::{ sprite::collide_aabb::{collide, Collision}, }; +use components::*; +use resources::*; + /// Constants that can be used to fine-tune the behavior of our game mod config { const TIME_STEP: f64 = 1.0 / 60.0; } -/// An implementation of the classic game "Breakout" +/// A simple implementation of the classic game "Breakout" fn main() { App::build() .add_plugins(DefaultPlugins) @@ -28,22 +31,26 @@ fn main() { .run(); } -struct Paddle { - speed: f32, +mod resources { + pub struct Scoreboard { + score: usize, + } } -struct Ball { - velocity: Vec3, -} +mod components { + pub struct Paddle; + pub struct Ball; -struct Scoreboard { - score: usize, -} + pub struct Velocity { + x: f32, + y: f32, + } -enum Collider { - Solid, - Scorable, - Paddle, + pub enum Collider { + Solid, + Scorable, + Paddle, + } } fn setup( From 930f6cf9ea12ad286cce2fb8a6260d9fbf18f8bf Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Mon, 3 May 2021 20:42:04 -0400 Subject: [PATCH 03/47] Move background color to configurable const --- examples/game/breakout.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/examples/game/breakout.rs b/examples/game/breakout.rs index 0b5c5974fe81a..11da3b1605d0e 100644 --- a/examples/game/breakout.rs +++ b/examples/game/breakout.rs @@ -1,7 +1,6 @@ use bevy::{ core::FixedTimestep, prelude::*, - render::pass::ClearColor, sprite::collide_aabb::{collide, Collision}, }; @@ -10,7 +9,11 @@ use resources::*; /// Constants that can be used to fine-tune the behavior of our game mod config { - const TIME_STEP: f64 = 1.0 / 60.0; + use bevy::render::color::Color; + use bevy::render::pass::ClearColor; + + pub const TIME_STEP: f64 = 1.0 / 60.0; + pub const BACKGROUND_COLOR: ClearColor = ClearColor(Color::rgb(0.9, 0.9, 0.9)); } /// A simple implementation of the classic game "Breakout" @@ -18,7 +21,7 @@ fn main() { App::build() .add_plugins(DefaultPlugins) .insert_resource(Scoreboard { score: 0 }) - .insert_resource(ClearColor(Color::rgb(0.9, 0.9, 0.9))) + .insert_resource(config::BACKGROUND_COLOR) .add_startup_system(setup.system()) .add_system_set( SystemSet::new() From 9fbe2065498da487dabf50066885c0d879704c8e Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Mon, 3 May 2021 20:45:58 -0400 Subject: [PATCH 04/47] Use init_resource for the Scoreboard --- examples/game/breakout.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/examples/game/breakout.rs b/examples/game/breakout.rs index 11da3b1605d0e..c27a9abdfe5b3 100644 --- a/examples/game/breakout.rs +++ b/examples/game/breakout.rs @@ -20,7 +20,8 @@ mod config { fn main() { App::build() .add_plugins(DefaultPlugins) - .insert_resource(Scoreboard { score: 0 }) + // This adds the Scoreboard resource with its default values: 0 + .init_resource::() .insert_resource(config::BACKGROUND_COLOR) .add_startup_system(setup.system()) .add_system_set( @@ -35,6 +36,7 @@ fn main() { } mod resources { + #[derive(Default)] pub struct Scoreboard { score: usize, } From 8de74a8956efbd0d605c7c9ad79e6a005a4c6616 Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Mon, 3 May 2021 20:56:50 -0400 Subject: [PATCH 05/47] Split setup into many small startup systems --- examples/game/breakout.rs | 99 +++++++++++++++++++++------------------ 1 file changed, 54 insertions(+), 45 deletions(-) diff --git a/examples/game/breakout.rs b/examples/game/breakout.rs index c27a9abdfe5b3..fe5d0b359f92e 100644 --- a/examples/game/breakout.rs +++ b/examples/game/breakout.rs @@ -23,7 +23,13 @@ fn main() { // This adds the Scoreboard resource with its default values: 0 .init_resource::() .insert_resource(config::BACKGROUND_COLOR) - .add_startup_system(setup.system()) + // These systems run only once, before all other systems + .add_startup_system(add_cameras.system()) + .add_startup_system(add_paddle.system()) + .add_startup_system(add_ball.system()) + .add_startup_system(add_walls.system()) + .add_startup_system(add_scoreboard.system()) + // These systems run repeatedly, whnever the FixedTimeStep has elapsed .add_system_set( SystemSet::new() .with_run_criteria(FixedTimestep::step(config::TIME_STEP)) @@ -58,17 +64,12 @@ mod components { } } -fn setup( - mut commands: Commands, - mut materials: ResMut>, - asset_server: Res, -) { - // Add the game's entities to our world - - // cameras +fn add_cameras(mut commands: Commands) { commands.spawn_bundle(OrthographicCameraBundle::new_2d()); commands.spawn_bundle(UiCameraBundle::default()); - // paddle +} + +fn add_paddle(mut commands: Commands, mut materials: ResMut>) { commands .spawn_bundle(SpriteBundle { material: materials.add(Color::rgb(0.5, 0.5, 1.0).into()), @@ -78,7 +79,9 @@ fn setup( }) .insert(Paddle { speed: 500.0 }) .insert(Collider::Paddle); - // ball +} + +fn add_ball(mut commands: Commands, mut materials: ResMut>) { commands .spawn_bundle(SpriteBundle { material: materials.add(Color::rgb(1.0, 0.5, 0.5).into()), @@ -89,41 +92,9 @@ fn setup( .insert(Ball { velocity: 400.0 * Vec3::new(0.5, -0.5, 0.0).normalize(), }); - // scoreboard - commands.spawn_bundle(TextBundle { - text: Text { - sections: vec![ - TextSection { - value: "Score: ".to_string(), - style: TextStyle { - font: asset_server.load("fonts/FiraSans-Bold.ttf"), - font_size: 40.0, - color: Color::rgb(0.5, 0.5, 1.0), - }, - }, - TextSection { - value: "".to_string(), - style: TextStyle { - font: asset_server.load("fonts/FiraMono-Medium.ttf"), - font_size: 40.0, - color: Color::rgb(1.0, 0.5, 0.5), - }, - }, - ], - ..Default::default() - }, - style: Style { - position_type: PositionType::Absolute, - position: Rect { - top: Val::Px(5.0), - left: Val::Px(5.0), - ..Default::default() - }, - ..Default::default() - }, - ..Default::default() - }); +} +fn add_walls(mut commands: Commands, mut materials: ResMut>) { // Add walls let wall_material = materials.add(Color::rgb(0.8, 0.8, 0.8).into()); let wall_thickness = 10.0; @@ -165,7 +136,9 @@ fn setup( ..Default::default() }) .insert(Collider::Solid); +} +fn add_bricks(mut commands: Commands, mut materials: ResMut>) { // Add bricks let brick_rows = 4; let brick_columns = 5; @@ -196,6 +169,42 @@ fn setup( } } +fn add_scoreboard(mut commands: Commands, asset_server: Res) { + commands.spawn_bundle(TextBundle { + text: Text { + sections: vec![ + TextSection { + value: "Score: ".to_string(), + style: TextStyle { + font: asset_server.load("fonts/FiraSans-Bold.ttf"), + font_size: 40.0, + color: Color::rgb(0.5, 0.5, 1.0), + }, + }, + TextSection { + value: "".to_string(), + style: TextStyle { + font: asset_server.load("fonts/FiraMono-Medium.ttf"), + font_size: 40.0, + color: Color::rgb(1.0, 0.5, 0.5), + }, + }, + ], + ..Default::default() + }, + style: Style { + position_type: PositionType::Absolute, + position: Rect { + top: Val::Px(5.0), + left: Val::Px(5.0), + ..Default::default() + }, + ..Default::default() + }, + ..Default::default() + }); +} + fn paddle_movement_system( keyboard_input: Res>, mut query: Query<(&Paddle, &mut Transform)>, From 858a7abad9518d4e1ad0291b515da14dcc1b485c Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Mon, 3 May 2021 20:59:12 -0400 Subject: [PATCH 06/47] Added marker components --- examples/game/breakout.rs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/examples/game/breakout.rs b/examples/game/breakout.rs index fe5d0b359f92e..54d9e9a4b02e1 100644 --- a/examples/game/breakout.rs +++ b/examples/game/breakout.rs @@ -20,8 +20,8 @@ mod config { fn main() { App::build() .add_plugins(DefaultPlugins) - // This adds the Scoreboard resource with its default values: 0 - .init_resource::() + // This adds the Score resource with its default values: 0 + .init_resource::() .insert_resource(config::BACKGROUND_COLOR) // These systems run only once, before all other systems .add_startup_system(add_cameras.system()) @@ -43,14 +43,18 @@ fn main() { mod resources { #[derive(Default)] - pub struct Scoreboard { + pub struct Score { score: usize, } } mod components { + // These are data-less marker components + // Which let us query for the correct entities pub struct Paddle; pub struct Ball; + pub struct Brick; + pub struct Scoreboard; pub struct Velocity { x: f32, @@ -77,7 +81,7 @@ fn add_paddle(mut commands: Commands, mut materials: ResMut Date: Mon, 3 May 2021 21:06:09 -0400 Subject: [PATCH 07/47] Clarity --- examples/game/breakout.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/game/breakout.rs b/examples/game/breakout.rs index 54d9e9a4b02e1..8cc3bd41a9142 100644 --- a/examples/game/breakout.rs +++ b/examples/game/breakout.rs @@ -29,7 +29,7 @@ fn main() { .add_startup_system(add_ball.system()) .add_startup_system(add_walls.system()) .add_startup_system(add_scoreboard.system()) - // These systems run repeatedly, whnever the FixedTimeStep has elapsed + // These systems run repeatedly, whnever the FixedTimeStep's duration has elapsed .add_system_set( SystemSet::new() .with_run_criteria(FixedTimestep::step(config::TIME_STEP)) From 1d82952ca1735cda5ac3ab3f37a6ade75559f5a0 Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Mon, 3 May 2021 21:50:15 -0400 Subject: [PATCH 08/47] Refactored Wall code to reduce mess --- examples/game/breakout.rs | 133 ++++++++++++++++++++++---------------- 1 file changed, 78 insertions(+), 55 deletions(-) diff --git a/examples/game/breakout.rs b/examples/game/breakout.rs index 8cc3bd41a9142..dde7fbcf766b2 100644 --- a/examples/game/breakout.rs +++ b/examples/game/breakout.rs @@ -2,6 +2,7 @@ use bevy::{ core::FixedTimestep, prelude::*, sprite::collide_aabb::{collide, Collision}, + render::pass::ClearColor; }; use components::*; @@ -9,20 +10,24 @@ use resources::*; /// Constants that can be used to fine-tune the behavior of our game mod config { + use bevy::math::Vec2; use bevy::render::color::Color; - use bevy::render::pass::ClearColor; pub const TIME_STEP: f64 = 1.0 / 60.0; - pub const BACKGROUND_COLOR: ClearColor = ClearColor(Color::rgb(0.9, 0.9, 0.9)); + pub const BACKGROUND_COLOR: Color = Color::rgb(0.9, 0.9, 0.9); + + pub const ARENA_BOUNDS: Vec2 = Vec2::new(900.0, 600.0); + pub const WALL_THICKNESS: f32 = 10.0; + pub const WALL_COLOR: Color = Color::rgb(0.8, 0.8, 0.8); } /// A simple implementation of the classic game "Breakout" fn main() { App::build() .add_plugins(DefaultPlugins) + .insert_resource(ClearColor(config::BACKGROUND_COLOR)) // This adds the Score resource with its default values: 0 .init_resource::() - .insert_resource(config::BACKGROUND_COLOR) // These systems run only once, before all other systems .add_startup_system(add_cameras.system()) .add_startup_system(add_paddle.system()) @@ -50,22 +55,20 @@ mod resources { mod components { // These are data-less marker components - // Which let us query for the correct entities + // which let us query for the correct entities + // and specialize behavior pub struct Paddle; pub struct Ball; pub struct Brick; pub struct Scoreboard; + pub struct Collides; + // The derived default values of numeric fields in Rust are zero + #[derive(Default)] pub struct Velocity { x: f32, y: f32, } - - pub enum Collider { - Solid, - Scorable, - Paddle, - } } fn add_cameras(mut commands: Commands) { @@ -82,7 +85,8 @@ fn add_paddle(mut commands: Commands, mut materials: ResMut>) { @@ -93,53 +97,71 @@ fn add_ball(mut commands: Commands, mut materials: ResMut> sprite: Sprite::new(Vec2::new(30.0, 30.0)), ..Default::default() }) - .insert(Ball { - velocity: 400.0 * Vec3::new(0.5, -0.5, 0.0).normalize(), - }); + .insert(Ball); +} + +/// Defines which side of the arena a wall is part of +enum Side { + Top, + Bottom, + Left, + Right, +} + +impl Side { + fn wall_coord(self, bounds: Vec2) -> Transform { + let (x, y) = match self { + Side::Top => (0.0, bounds.y/2.0), + Side::Bottom => (0.0, -bounds.y/2.0), + Side::Left => (-bounds.x/2.0, 0.0), + Side::Right => (bounds.x/2.0, 0.0) + }; + // We need to convert these coordinates into a 3D transform to add to our SpriteBundle + Transform::from_xyz(x, y, 0.0) + } + + fn wall_size(self, bounds: Vec2, thickness: f32) -> Vec2 { + match self { + Side::Top => Vec2::new(thickness, bounds.y + thickness), + Side::Bottom => Vec2::new(thickness, bounds.y + thickness), + Side::Left => Vec2::new(bounds.x + thickness, thickness), + Side::Right => Vec2::new(bounds.x + thickness, thickness), + } + } +} + +// By creating our own bundles, we can avoid duplicating code +#[derive(Bundle)] +struct WallBundle { + #[bundle] + sprite_bundle: SpriteBundle, + collides: Collides, +} + +impl WallBundle { + fn new(side: Side, material_handle: Handle) -> Self { + let bounds = config::ARENA_BOUNDS; + let thickness = config::WALL_THICKNESS; + + WallBundle { + sprite_bundle: SpriteBundle { + material: material_handle.clone(), + transform: side.wall_coord(bounds), + sprite: Sprite::new(side.wall_size(bounds, thickness)), + ..Default::default() + }, + collides: Collides, + } + } } fn add_walls(mut commands: Commands, mut materials: ResMut>) { - // Add walls - let wall_material = materials.add(Color::rgb(0.8, 0.8, 0.8).into()); - let wall_thickness = 10.0; - let bounds = Vec2::new(900.0, 600.0); + let material_handle = materials.add(config::WALL_COLOR.into()); - // left - commands - .spawn_bundle(SpriteBundle { - material: wall_material.clone(), - transform: Transform::from_xyz(-bounds.x / 2.0, 0.0, 0.0), - sprite: Sprite::new(Vec2::new(wall_thickness, bounds.y + wall_thickness)), - ..Default::default() - }) - .insert(Collider::Solid); - // right - commands - .spawn_bundle(SpriteBundle { - material: wall_material.clone(), - transform: Transform::from_xyz(bounds.x / 2.0, 0.0, 0.0), - sprite: Sprite::new(Vec2::new(wall_thickness, bounds.y + wall_thickness)), - ..Default::default() - }) - .insert(Collider::Solid); - // bottom - commands - .spawn_bundle(SpriteBundle { - material: wall_material.clone(), - transform: Transform::from_xyz(0.0, -bounds.y / 2.0, 0.0), - sprite: Sprite::new(Vec2::new(bounds.x + wall_thickness, wall_thickness)), - ..Default::default() - }) - .insert(Collider::Solid); - // top - commands - .spawn_bundle(SpriteBundle { - material: wall_material, - transform: Transform::from_xyz(0.0, bounds.y / 2.0, 0.0), - sprite: Sprite::new(Vec2::new(bounds.x + wall_thickness, wall_thickness)), - ..Default::default() - }) - .insert(Collider::Solid); + commands.spawn_bundle(WallBundle::new(Side::Top, material_handle)); + commands.spawn_bundle(WallBundle::new(Side::Bottom, material_handle)); + commands.spawn_bundle(WallBundle::new(Side::Left, material_handle)); + commands.spawn_bundle(WallBundle::new(Side::Right, material_handle)); } fn add_bricks(mut commands: Commands, mut materials: ResMut>) { @@ -168,7 +190,8 @@ fn add_bricks(mut commands: Commands, mut materials: ResMut Date: Mon, 3 May 2021 22:00:10 -0400 Subject: [PATCH 09/47] Cleaned up bricks logic --- examples/game/breakout.rs | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/examples/game/breakout.rs b/examples/game/breakout.rs index dde7fbcf766b2..d399696d65816 100644 --- a/examples/game/breakout.rs +++ b/examples/game/breakout.rs @@ -19,6 +19,8 @@ mod config { pub const ARENA_BOUNDS: Vec2 = Vec2::new(900.0, 600.0); pub const WALL_THICKNESS: f32 = 10.0; pub const WALL_COLOR: Color = Color::rgb(0.8, 0.8, 0.8); + + pub const BRICK_COLOR: Color = Color::rgb(0.5, 0.5, 1.0); } /// A simple implementation of the classic game "Breakout" @@ -165,24 +167,28 @@ fn add_walls(mut commands: Commands, mut materials: ResMut } fn add_bricks(mut commands: Commands, mut materials: ResMut>) { - // Add bricks - let brick_rows = 4; - let brick_columns = 5; - let brick_spacing = 20.0; - let brick_size = Vec2::new(150.0, 30.0); - let bricks_width = brick_columns as f32 * (brick_size.x + brick_spacing) - brick_spacing; - // center the bricks and move them up a bit - let bricks_offset = Vec3::new(-(bricks_width - brick_size.x) / 2.0, 100.0, 0.0); - let brick_material = materials.add(Color::rgb(0.5, 0.5, 1.0).into()); + let brick_material = materials.add(config::BRICK_COLOR.into()); + + // Brick layout constants + const brick_rows: i8 = 4; + const brick_columns: i8 = 5; + const brick_spacing: f32 = 20.0; + const brick_size: Vec2 = Vec2::new(150.0, 30.0); + + // Compute the total width that all of the bricks take + let total_width = brick_columns as f32 * (brick_size.x + brick_spacing) - brick_spacing; + // Center the bricks and move them up a bit + let bricks_offset = Vec3::new(-(total_width - brick_size.x) / 2.0, 100.0, 0.0); + + // Add the bricks for row in 0..brick_rows { - let y_position = row as f32 * (brick_size.y + brick_spacing); for column in 0..brick_columns { let brick_position = Vec3::new( column as f32 * (brick_size.x + brick_spacing), - y_position, + row as f32 * (brick_size.y + brick_spacing), 0.0, ) + bricks_offset; - // brick + // Adding one brick at a time commands .spawn_bundle(SpriteBundle { material: brick_material.clone(), From 11936694ad0fe1de7bcf86b6135b6e72a6d47786 Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Mon, 3 May 2021 22:05:03 -0400 Subject: [PATCH 10/47] More color constants --- examples/game/breakout.rs | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/examples/game/breakout.rs b/examples/game/breakout.rs index d399696d65816..d0c675b2cb505 100644 --- a/examples/game/breakout.rs +++ b/examples/game/breakout.rs @@ -16,11 +16,18 @@ mod config { pub const TIME_STEP: f64 = 1.0 / 60.0; pub const BACKGROUND_COLOR: Color = Color::rgb(0.9, 0.9, 0.9); + pub const PADDLE_COLOR: Color = Color::rgb(0.5, 0.5, 1.0); + + pub const BALL_COLOR: Color = Color::rgb(1.0, 0.5, 0.5); + pub const ARENA_BOUNDS: Vec2 = Vec2::new(900.0, 600.0); pub const WALL_THICKNESS: f32 = 10.0; pub const WALL_COLOR: Color = Color::rgb(0.8, 0.8, 0.8); pub const BRICK_COLOR: Color = Color::rgb(0.5, 0.5, 1.0); + + pub const SCOREBOARD_COLOR: Color = Color::rgb(0.5, 0.5, 1.0); + pub const SCORE_COLOR: Color = Color::rgb(1.0, 0.5, 0.5); } /// A simple implementation of the classic game "Breakout" @@ -81,7 +88,7 @@ fn add_cameras(mut commands: Commands) { fn add_paddle(mut commands: Commands, mut materials: ResMut>) { commands .spawn_bundle(SpriteBundle { - material: materials.add(Color::rgb(0.5, 0.5, 1.0).into()), + material: materials.add(config::PADDLE_COLOR.into()), transform: Transform::from_xyz(0.0, -215.0, 0.0), sprite: Sprite::new(Vec2::new(120.0, 30.0)), ..Default::default() @@ -94,7 +101,7 @@ fn add_paddle(mut commands: Commands, mut materials: ResMut>) { commands .spawn_bundle(SpriteBundle { - material: materials.add(Color::rgb(1.0, 0.5, 0.5).into()), + material: materials.add(config::BALL_COLOR.into()), transform: Transform::from_xyz(0.0, -50.0, 1.0), sprite: Sprite::new(Vec2::new(30.0, 30.0)), ..Default::default() @@ -211,7 +218,7 @@ fn add_scoreboard(mut commands: Commands, asset_server: Res) { style: TextStyle { font: asset_server.load("fonts/FiraSans-Bold.ttf"), font_size: 40.0, - color: Color::rgb(0.5, 0.5, 1.0), + color: config::SCOREBOARD_COLOR, }, }, TextSection { @@ -219,7 +226,7 @@ fn add_scoreboard(mut commands: Commands, asset_server: Res) { style: TextStyle { font: asset_server.load("fonts/FiraMono-Medium.ttf"), font_size: 40.0, - color: Color::rgb(1.0, 0.5, 0.5), + color: config::SCORE_COLOR, }, }, ], From eb8c8f05f46455304ccd14c9c01932f114f5d68f Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Mon, 3 May 2021 22:13:41 -0400 Subject: [PATCH 11/47] Extract Paddle and Ball magic numbers --- examples/game/breakout.rs | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/examples/game/breakout.rs b/examples/game/breakout.rs index d0c675b2cb505..6e8c6fe12810a 100644 --- a/examples/game/breakout.rs +++ b/examples/game/breakout.rs @@ -11,14 +11,21 @@ use resources::*; /// Constants that can be used to fine-tune the behavior of our game mod config { use bevy::math::Vec2; + use bevy::transform::components::Transform; use bevy::render::color::Color; pub const TIME_STEP: f64 = 1.0 / 60.0; pub const BACKGROUND_COLOR: Color = Color::rgb(0.9, 0.9, 0.9); pub const PADDLE_COLOR: Color = Color::rgb(0.5, 0.5, 1.0); + pub const PADDLE_STARTING_LOCATION: Transform = Transform::from_xyz(0.0, -215.0, 0.0); + pub const PADDLE_SIZE: Vec2 = Vec2::new(120.0, 30.0); pub const BALL_COLOR: Color = Color::rgb(1.0, 0.5, 0.5); + // We set the z-value to one to ensure it appears on top of our other objects in case of overlap + pub const BALL_STARTING_LOCATION: Transform = Transform::from_xyz(0.0, -50.0, 1.0); + // Our ball is actually a square. Shhh... + pub const BALL_SIZE: Vec2 = Vec2::new(30.0, 30.0); pub const ARENA_BOUNDS: Vec2 = Vec2::new(900.0, 600.0); pub const WALL_THICKNESS: f32 = 10.0; @@ -89,8 +96,8 @@ fn add_paddle(mut commands: Commands, mut materials: ResMut> commands .spawn_bundle(SpriteBundle { material: materials.add(config::BALL_COLOR.into()), - transform: Transform::from_xyz(0.0, -50.0, 1.0), - sprite: Sprite::new(Vec2::new(30.0, 30.0)), + transform: config::BALL_STARTING_LOCATION, + sprite: Sprite::new(config::BALL_SIZE), ..Default::default() }) .insert(Ball); From 57976a685871f620c307f731f165c3d36fe64497 Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Mon, 3 May 2021 22:16:52 -0400 Subject: [PATCH 12/47] Extract magic numbers from Scoreboard --- examples/game/breakout.rs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/examples/game/breakout.rs b/examples/game/breakout.rs index 6e8c6fe12810a..e7b228262d350 100644 --- a/examples/game/breakout.rs +++ b/examples/game/breakout.rs @@ -13,6 +13,7 @@ mod config { use bevy::math::Vec2; use bevy::transform::components::Transform; use bevy::render::color::Color; + use bevy::ui::Val; pub const TIME_STEP: f64 = 1.0 / 60.0; pub const BACKGROUND_COLOR: Color = Color::rgb(0.9, 0.9, 0.9); @@ -35,6 +36,8 @@ mod config { pub const SCOREBOARD_COLOR: Color = Color::rgb(0.5, 0.5, 1.0); pub const SCORE_COLOR: Color = Color::rgb(1.0, 0.5, 0.5); + pub const SCORE_FONT_SIZE: f32 = 40.0; + pub const SCORE_PADDING: Val = Val::Px(5.0); } /// A simple implementation of the classic game "Breakout" @@ -224,7 +227,7 @@ fn add_scoreboard(mut commands: Commands, asset_server: Res) { value: "Score: ".to_string(), style: TextStyle { font: asset_server.load("fonts/FiraSans-Bold.ttf"), - font_size: 40.0, + font_size: config::SCORE_FONT_SIZE, color: config::SCOREBOARD_COLOR, }, }, @@ -232,7 +235,7 @@ fn add_scoreboard(mut commands: Commands, asset_server: Res) { value: "".to_string(), style: TextStyle { font: asset_server.load("fonts/FiraMono-Medium.ttf"), - font_size: 40.0, + font_size: config::SCORE_FONT_SIZE, color: config::SCORE_COLOR, }, }, @@ -242,8 +245,8 @@ fn add_scoreboard(mut commands: Commands, asset_server: Res) { style: Style { position_type: PositionType::Absolute, position: Rect { - top: Val::Px(5.0), - left: Val::Px(5.0), + top: config::SCORE_PADDING, + left: config::SCORE_PADDING, ..Default::default() }, ..Default::default() From 8b154b5c3c96f6778dfa82257d05be0e913ad0c1 Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Mon, 3 May 2021 22:27:33 -0400 Subject: [PATCH 13/47] Added kinematics system --- examples/game/breakout.rs | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/examples/game/breakout.rs b/examples/game/breakout.rs index e7b228262d350..61d0aef36c87a 100644 --- a/examples/game/breakout.rs +++ b/examples/game/breakout.rs @@ -15,7 +15,7 @@ mod config { use bevy::render::color::Color; use bevy::ui::Val; - pub const TIME_STEP: f64 = 1.0 / 60.0; + pub const TIME_STEP: f32 = 1.0 / 60.0; pub const BACKGROUND_COLOR: Color = Color::rgb(0.9, 0.9, 0.9); pub const PADDLE_COLOR: Color = Color::rgb(0.5, 0.5, 1.0); @@ -56,11 +56,13 @@ fn main() { // These systems run repeatedly, whnever the FixedTimeStep's duration has elapsed .add_system_set( SystemSet::new() - .with_run_criteria(FixedTimestep::step(config::TIME_STEP)) + .with_run_criteria(FixedTimestep::step(config::TIME_STEP as f64)) + .with_system(kinematics_system.system()) .with_system(paddle_movement_system.system()) .with_system(ball_collision_system.system()) .with_system(ball_movement_system.system()), ) + // Ordinary systems run every frame .add_system(scoreboard_system.system()) .run(); } @@ -85,8 +87,8 @@ mod components { // The derived default values of numeric fields in Rust are zero #[derive(Default)] pub struct Velocity { - x: f32, - y: f32, + pub x: f32, + pub y: f32, } } @@ -255,6 +257,14 @@ fn add_scoreboard(mut commands: Commands, asset_server: Res) { }); } +/// Moves everything with both a Transform and a Velovity accordingly +fn kinematics_system(mut query: Query<(&mut Transform, &Velocity)>){ + for (transform, velocity) in query.iter_mut(){ + transform.translation.x += velocity.x * config::TIME_STEP; + transform.translation.y += velocity.y * config::TIME_STEP; + } +} + fn paddle_movement_system( keyboard_input: Res>, mut query: Query<(&Paddle, &mut Transform)>, From 2c99de02b266bf6e51e144e6f2cdd1c1f8f6a78b Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Mon, 3 May 2021 22:41:02 -0400 Subject: [PATCH 14/47] Cleaned up ball and paddle movement --- examples/game/breakout.rs | 74 +++++++++++++++++++-------------------- 1 file changed, 37 insertions(+), 37 deletions(-) diff --git a/examples/game/breakout.rs b/examples/game/breakout.rs index 61d0aef36c87a..88c6cbf697939 100644 --- a/examples/game/breakout.rs +++ b/examples/game/breakout.rs @@ -1,8 +1,8 @@ use bevy::{ core::FixedTimestep, prelude::*, + render::pass::ClearColor, sprite::collide_aabb::{collide, Collision}, - render::pass::ClearColor; }; use components::*; @@ -11,8 +11,8 @@ use resources::*; /// Constants that can be used to fine-tune the behavior of our game mod config { use bevy::math::Vec2; - use bevy::transform::components::Transform; use bevy::render::color::Color; + use bevy::transform::components::Transform; use bevy::ui::Val; pub const TIME_STEP: f32 = 1.0 / 60.0; @@ -21,7 +21,8 @@ mod config { pub const PADDLE_COLOR: Color = Color::rgb(0.5, 0.5, 1.0); pub const PADDLE_STARTING_LOCATION: Transform = Transform::from_xyz(0.0, -215.0, 0.0); pub const PADDLE_SIZE: Vec2 = Vec2::new(120.0, 30.0); - + pub const PADDLE_SPEED: f32 = 500.0; + pub const BALL_COLOR: Color = Color::rgb(1.0, 0.5, 0.5); // We set the z-value to one to ensure it appears on top of our other objects in case of overlap pub const BALL_STARTING_LOCATION: Transform = Transform::from_xyz(0.0, -50.0, 1.0); @@ -58,11 +59,11 @@ fn main() { SystemSet::new() .with_run_criteria(FixedTimestep::step(config::TIME_STEP as f64)) .with_system(kinematics_system.system()) - .with_system(paddle_movement_system.system()) - .with_system(ball_collision_system.system()) - .with_system(ball_movement_system.system()), + .with_system(paddle_input_system.system()) + .with_system(ball_collision_system.system()), ) // Ordinary systems run every frame + .add_system(bound_paddle_system.system()) .add_system(scoreboard_system.system()) .run(); } @@ -75,10 +76,12 @@ mod resources { } mod components { + pub struct Paddle { + pub speed: f32, + } // These are data-less marker components // which let us query for the correct entities // and specialize behavior - pub struct Paddle; pub struct Ball; pub struct Brick; pub struct Scoreboard; @@ -105,7 +108,9 @@ fn add_paddle(mut commands: Commands, mut materials: ResMut Transform { let (x, y) = match self { - Side::Top => (0.0, bounds.y/2.0), - Side::Bottom => (0.0, -bounds.y/2.0), - Side::Left => (-bounds.x/2.0, 0.0), - Side::Right => (bounds.x/2.0, 0.0) + Side::Top => (0.0, bounds.y / 2.0), + Side::Bottom => (0.0, -bounds.y / 2.0), + Side::Left => (-bounds.x / 2.0, 0.0), + Side::Right => (bounds.x / 2.0, 0.0), }; // We need to convert these coordinates into a 3D transform to add to our SpriteBundle Transform::from_xyz(x, y, 0.0) @@ -187,7 +192,7 @@ fn add_walls(mut commands: Commands, mut materials: ResMut fn add_bricks(mut commands: Commands, mut materials: ResMut>) { let brick_material = materials.add(config::BRICK_COLOR.into()); - + // Brick layout constants const brick_rows: i8 = 4; const brick_columns: i8 = 5; @@ -198,7 +203,7 @@ fn add_bricks(mut commands: Commands, mut materials: ResMut) { } /// Moves everything with both a Transform and a Velovity accordingly -fn kinematics_system(mut query: Query<(&mut Transform, &Velocity)>){ - for (transform, velocity) in query.iter_mut(){ +fn kinematics_system(mut query: Query<(&mut Transform, &Velocity)>) { + for (transform, velocity) in query.iter_mut() { transform.translation.x += velocity.x * config::TIME_STEP; transform.translation.y += velocity.y * config::TIME_STEP; } } -fn paddle_movement_system( +/// Turns left and right arrow key inputs to set paddle velocity +fn paddle_input_system( keyboard_input: Res>, - mut query: Query<(&Paddle, &mut Transform)>, + mut query: Query<(&Paddle, &mut Velocity)>, ) { - if let Ok((paddle, mut transform)) = query.single_mut() { - let mut direction = 0.0; - if keyboard_input.pressed(KeyCode::Left) { - direction -= 1.0; - } + let (paddle, mut velocity) = query.single_mut().unwrap(); - if keyboard_input.pressed(KeyCode::Right) { - direction += 1.0; - } + let mut direction = 0.0; + if keyboard_input.pressed(KeyCode::Left) { + direction -= 1.0; + } - let translation = &mut transform.translation; - // move the paddle horizontally - // FIXME: this should use delta_time - translation.x += direction * paddle.speed * config::TIME_STEP; - // bound the paddle within the walls - translation.x = translation.x.min(380.0).max(-380.0); + if keyboard_input.pressed(KeyCode::Right) { + direction += 1.0; } + + velocity.x += direction * paddle.speed; } -fn ball_movement_system(mut ball_query: Query<(&Ball, &mut Transform)>) { - if let Ok((ball, mut transform)) = ball_query.single_mut() { - // FIXME: this should use delta_time - transform.translation += ball.velocity * config::TIME_STEP; - } +/// Ensures our paddle never goes out of bounds +fn bound_paddle_system(mut query: Query<&mut Transform, With>) { + let mut paddle_transform = query.single_mut().unwrap(); + paddle_transform.translation.x = paddle_transform.translation.x.min(380.0).max(-380.0); } fn scoreboard_system(scoreboard: Res, mut query: Query<&mut Text>) { From 88e0eac0cfa69023b2630838e0cea70c3b788ebb Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Mon, 3 May 2021 23:22:19 -0400 Subject: [PATCH 15/47] Clean up scoreboard_system --- examples/game/breakout.rs | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/examples/game/breakout.rs b/examples/game/breakout.rs index 88c6cbf697939..f4a0d62f2621c 100644 --- a/examples/game/breakout.rs +++ b/examples/game/breakout.rs @@ -70,9 +70,7 @@ fn main() { mod resources { #[derive(Default)] - pub struct Score { - score: usize, - } + pub struct Score(pub usize); } mod components { @@ -295,11 +293,6 @@ fn bound_paddle_system(mut query: Query<&mut Transform, With>) { paddle_transform.translation.x = paddle_transform.translation.x.min(380.0).max(-380.0); } -fn scoreboard_system(scoreboard: Res, mut query: Query<&mut Text>) { - let mut text = query.single_mut().unwrap(); - text.sections[0].value = format!("Score: {}", scoreboard.score); -} - fn ball_collision_system( mut commands: Commands, mut scoreboard: ResMut, @@ -357,3 +350,9 @@ fn ball_collision_system( } } } + +/// Updates the Scoreboard entity based on the Score resource +fn scoreboard_system(score: Res, mut query: Query<&mut Text, With>) { + let mut scoreboard_text = query.single_mut().unwrap(); + scoreboard_text.sections[0].value = format!("Score: {}", score.0); +} From 3e864a662380b4ef58f4086e0acedd08a60c51d7 Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Mon, 3 May 2021 23:22:38 -0400 Subject: [PATCH 16/47] Add starting velocity to ball --- examples/game/breakout.rs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/examples/game/breakout.rs b/examples/game/breakout.rs index f4a0d62f2621c..de74d7014d89d 100644 --- a/examples/game/breakout.rs +++ b/examples/game/breakout.rs @@ -10,6 +10,7 @@ use resources::*; /// Constants that can be used to fine-tune the behavior of our game mod config { + use super::Velocity; use bevy::math::Vec2; use bevy::render::color::Color; use bevy::transform::components::Transform; @@ -28,6 +29,12 @@ mod config { pub const BALL_STARTING_LOCATION: Transform = Transform::from_xyz(0.0, -50.0, 1.0); // Our ball is actually a square. Shhh... pub const BALL_SIZE: Vec2 = Vec2::new(30.0, 30.0); + const BALL_STARTING_DIRECTION: Vec2 = Vec2::new(0.5, -0.5).normalize(); + const BALL_STARTING_SPEED: f32 = 400.0; + pub const BALL_STARTING_VELOCITY: Velocity = Velocity { + x: BALL_STARTING_DIRECTION.x * BALL_STARTING_SPEED, + y: BALL_STARTING_DIRECTION.y * BALL_STARTING_SPEED, + }; pub const ARENA_BOUNDS: Vec2 = Vec2::new(900.0, 600.0); pub const WALL_THICKNESS: f32 = 10.0; @@ -121,7 +128,9 @@ fn add_ball(mut commands: Commands, mut materials: ResMut> sprite: Sprite::new(config::BALL_SIZE), ..Default::default() }) - .insert(Ball); + .insert(Ball) + // Adds a `Velocity` component with the value defined in the `config` module + .insert(config::BALL_STARTING_VELOCITY); } /// Defines which side of the arena a wall is part of From 6835d60a6668290a4fd3e0cb6338c1da959f4198 Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Mon, 3 May 2021 23:25:25 -0400 Subject: [PATCH 17/47] Rename add_* startup systems to spawn_* --- examples/game/breakout.rs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/examples/game/breakout.rs b/examples/game/breakout.rs index de74d7014d89d..08c67535700be 100644 --- a/examples/game/breakout.rs +++ b/examples/game/breakout.rs @@ -56,11 +56,11 @@ fn main() { // This adds the Score resource with its default values: 0 .init_resource::() // These systems run only once, before all other systems - .add_startup_system(add_cameras.system()) - .add_startup_system(add_paddle.system()) - .add_startup_system(add_ball.system()) - .add_startup_system(add_walls.system()) - .add_startup_system(add_scoreboard.system()) + .add_startup_system(spawn_cameras.system()) + .add_startup_system(spawn_paddle.system()) + .add_startup_system(spawn_ball.system()) + .add_startup_system(spawn_walls.system()) + .add_startup_system(spawn_scoreboard.system()) // These systems run repeatedly, whnever the FixedTimeStep's duration has elapsed .add_system_set( SystemSet::new() @@ -100,12 +100,12 @@ mod components { } } -fn add_cameras(mut commands: Commands) { +fn spawn_cameras(mut commands: Commands) { commands.spawn_bundle(OrthographicCameraBundle::new_2d()); commands.spawn_bundle(UiCameraBundle::default()); } -fn add_paddle(mut commands: Commands, mut materials: ResMut>) { +fn spawn_paddle(mut commands: Commands, mut materials: ResMut>) { commands .spawn_bundle(SpriteBundle { material: materials.add(config::PADDLE_COLOR.into()), @@ -120,7 +120,7 @@ fn add_paddle(mut commands: Commands, mut materials: ResMut>) { +fn spawn_ball(mut commands: Commands, mut materials: ResMut>) { commands .spawn_bundle(SpriteBundle { material: materials.add(config::BALL_COLOR.into()), @@ -188,7 +188,7 @@ impl WallBundle { } } -fn add_walls(mut commands: Commands, mut materials: ResMut>) { +fn spawn_walls(mut commands: Commands, mut materials: ResMut>) { let material_handle = materials.add(config::WALL_COLOR.into()); commands.spawn_bundle(WallBundle::new(Side::Top, material_handle)); @@ -233,7 +233,7 @@ fn add_bricks(mut commands: Commands, mut materials: ResMut) { +fn spawn_scoreboard(mut commands: Commands, asset_server: Res) { commands.spawn_bundle(TextBundle { text: Text { sections: vec![ From 120bbdfd56857460256c49e70d53a5df6ef7ae70 Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Mon, 3 May 2021 23:28:58 -0400 Subject: [PATCH 18/47] Reduced system naming redundancy --- examples/game/breakout.rs | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/examples/game/breakout.rs b/examples/game/breakout.rs index 08c67535700be..7adb5b655f6c4 100644 --- a/examples/game/breakout.rs +++ b/examples/game/breakout.rs @@ -65,13 +65,13 @@ fn main() { .add_system_set( SystemSet::new() .with_run_criteria(FixedTimestep::step(config::TIME_STEP as f64)) - .with_system(kinematics_system.system()) - .with_system(paddle_input_system.system()) - .with_system(ball_collision_system.system()), + .with_system(kinematics.system()) + .with_system(paddle_input.system()) + .with_system(ball_collision.system()), ) // Ordinary systems run every frame - .add_system(bound_paddle_system.system()) - .add_system(scoreboard_system.system()) + .add_system(bound_paddle.system()) + .add_system(update_scoreboard.system()) .run(); } @@ -270,7 +270,7 @@ fn spawn_scoreboard(mut commands: Commands, asset_server: Res) { } /// Moves everything with both a Transform and a Velovity accordingly -fn kinematics_system(mut query: Query<(&mut Transform, &Velocity)>) { +fn kinematics(mut query: Query<(&mut Transform, &Velocity)>) { for (transform, velocity) in query.iter_mut() { transform.translation.x += velocity.x * config::TIME_STEP; transform.translation.y += velocity.y * config::TIME_STEP; @@ -278,10 +278,7 @@ fn kinematics_system(mut query: Query<(&mut Transform, &Velocity)>) { } /// Turns left and right arrow key inputs to set paddle velocity -fn paddle_input_system( - keyboard_input: Res>, - mut query: Query<(&Paddle, &mut Velocity)>, -) { +fn paddle_input(keyboard_input: Res>, mut query: Query<(&Paddle, &mut Velocity)>) { let (paddle, mut velocity) = query.single_mut().unwrap(); let mut direction = 0.0; @@ -297,12 +294,12 @@ fn paddle_input_system( } /// Ensures our paddle never goes out of bounds -fn bound_paddle_system(mut query: Query<&mut Transform, With>) { +fn bound_paddle(mut query: Query<&mut Transform, With>) { let mut paddle_transform = query.single_mut().unwrap(); paddle_transform.translation.x = paddle_transform.translation.x.min(380.0).max(-380.0); } -fn ball_collision_system( +fn ball_collision( mut commands: Commands, mut scoreboard: ResMut, mut ball_query: Query<(&mut Ball, &Transform, &Sprite)>, @@ -361,7 +358,7 @@ fn ball_collision_system( } /// Updates the Scoreboard entity based on the Score resource -fn scoreboard_system(score: Res, mut query: Query<&mut Text, With>) { +fn update_scoreboard(score: Res, mut query: Query<&mut Text, With>) { let mut scoreboard_text = query.single_mut().unwrap(); scoreboard_text.sections[0].value = format!("Score: {}", score.0); } From 9112742b1fc0f52bfc9f9d7c2003fbeb1e811536 Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Mon, 3 May 2021 23:57:44 -0400 Subject: [PATCH 19/47] Cleaned up collision function --- examples/game/breakout.rs | 104 ++++++++++++++++++++------------------ 1 file changed, 54 insertions(+), 50 deletions(-) diff --git a/examples/game/breakout.rs b/examples/game/breakout.rs index 7adb5b655f6c4..8e14a1826394d 100644 --- a/examples/game/breakout.rs +++ b/examples/game/breakout.rs @@ -129,6 +129,7 @@ fn spawn_ball(mut commands: Commands, mut materials: ResMut>) { } fn ball_collision( + mut ball_query: Query<(&Transform, &mut Velocity, &Sprite), With>, + // Option<&C> returns Some(c: C) if the component exists on the entity, and None if it does not + collider_query: Query< + (Entity, &Transform, &Sprite, Option<&Brick>), + (With, Without), + >, mut commands: Commands, - mut scoreboard: ResMut, - mut ball_query: Query<(&mut Ball, &Transform, &Sprite)>, - collider_query: Query<(Entity, &Collider, &Transform, &Sprite)>, + mut score: ResMut, ) { - if let Ok((mut ball, ball_transform, sprite)) = ball_query.single_mut() { - let ball_size = sprite.size; - let velocity = &mut ball.velocity; - - // check collision with walls - for (collider_entity, collider, transform, sprite) in collider_query.iter() { - let collision = collide( - ball_transform.translation, - ball_size, - transform.translation, - sprite.size, - ); - if let Some(collision) = collision { - // scorable colliders should be despawned and increment the scoreboard on collision - if let Collider::Scorable = *collider { - scoreboard.score += 1; - commands.entity(collider_entity).despawn(); - } - - // reflect the ball when it collides - let mut reflect_x = false; - let mut reflect_y = false; - - // only reflect if the ball's velocity is going in the opposite direction of the - // collision - match collision { - Collision::Left => reflect_x = velocity.x > 0.0, - Collision::Right => reflect_x = velocity.x < 0.0, - Collision::Top => reflect_y = velocity.y < 0.0, - Collision::Bottom => reflect_y = velocity.y > 0.0, - } - - // reflect velocity on the x-axis if we hit something on the x-axis - if reflect_x { - velocity.x = -velocity.x; - } - - // reflect velocity on the y-axis if we hit something on the y-axis - if reflect_y { - velocity.y = -velocity.y; - } - - // break if this collide is on a solid, otherwise continue check whether a solid is - // also in collision - if let Collider::Solid = *collider { - break; - } + let (ball_transform, mut ball_velocity, ball_sprite) = ball_query.single_mut().unwrap(); + let ball_size = ball_sprite.size; + + for (collider_entity, collider_transform, collider_sprite, maybe_brick) in collider_query.iter() + { + // Check for collisions + let collider_size = collider_sprite.size; + let potential_collision = collide( + ball_transform.translation, + ball_size, + collider_transform.translation, + collider_size, + ); + + // Handle collisions + if let Some(collision) = potential_collision { + // Reflect the ball when it collides + let mut reflect_x = false; + let mut reflect_y = false; + + // Only reflect if the ball's velocity is going + // in the opposite direction of the collision + match collision { + Collision::Left => reflect_x = ball_velocity.x > 0.0, + Collision::Right => reflect_x = ball_velocity.x < 0.0, + Collision::Top => reflect_y = ball_velocity.y < 0.0, + Collision::Bottom => reflect_y = ball_velocity.y > 0.0, + } + + // Reflect velocity on the x-axis if we hit something on the x-axis + if reflect_x { + ball_velocity.x = -ball_velocity.x; + } + + // Reflect velocity on the y-axis if we hit something on the y-axis + if reflect_y { + ball_velocity.y = -ball_velocity.y; + } + + // Perform special brick collision behavior + if maybe_brick.is_some() { + // Despawn bricks that are hit + commands.entity(collider_entity).despawn(); + + // Increase the score by 1 for each brick hit + score.0 += 1; } } } From 4768e9b06d58ce917dd87295909aafc163fa55c3 Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Tue, 4 May 2021 00:01:59 -0400 Subject: [PATCH 20/47] Respect ownership rules... --- examples/game/breakout.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/examples/game/breakout.rs b/examples/game/breakout.rs index 8e14a1826394d..26f43e8a49a94 100644 --- a/examples/game/breakout.rs +++ b/examples/game/breakout.rs @@ -143,7 +143,7 @@ enum Side { } impl Side { - fn wall_coord(self, bounds: Vec2) -> Transform { + fn wall_coord(&self, bounds: Vec2) -> Transform { let (x, y) = match self { Side::Top => (0.0, bounds.y / 2.0), Side::Bottom => (0.0, -bounds.y / 2.0), @@ -154,7 +154,7 @@ impl Side { Transform::from_xyz(x, y, 0.0) } - fn wall_size(self, bounds: Vec2, thickness: f32) -> Vec2 { + fn wall_size(&self, bounds: Vec2, thickness: f32) -> Vec2 { match self { Side::Top => Vec2::new(thickness, bounds.y + thickness), Side::Bottom => Vec2::new(thickness, bounds.y + thickness), @@ -173,7 +173,7 @@ struct WallBundle { } impl WallBundle { - fn new(side: Side, material_handle: Handle) -> Self { + fn new(side: Side, material_handle: &Handle) -> Self { let bounds = config::ARENA_BOUNDS; let thickness = config::WALL_THICKNESS; @@ -192,10 +192,10 @@ impl WallBundle { fn spawn_walls(mut commands: Commands, mut materials: ResMut>) { let material_handle = materials.add(config::WALL_COLOR.into()); - commands.spawn_bundle(WallBundle::new(Side::Top, material_handle)); - commands.spawn_bundle(WallBundle::new(Side::Bottom, material_handle)); - commands.spawn_bundle(WallBundle::new(Side::Left, material_handle)); - commands.spawn_bundle(WallBundle::new(Side::Right, material_handle)); + commands.spawn_bundle(WallBundle::new(Side::Top, &material_handle)); + commands.spawn_bundle(WallBundle::new(Side::Bottom, &material_handle)); + commands.spawn_bundle(WallBundle::new(Side::Left, &material_handle)); + commands.spawn_bundle(WallBundle::new(Side::Right, &material_handle)); } fn add_bricks(mut commands: Commands, mut materials: ResMut>) { @@ -272,7 +272,7 @@ fn spawn_scoreboard(mut commands: Commands, asset_server: Res) { /// Moves everything with both a Transform and a Velovity accordingly fn kinematics(mut query: Query<(&mut Transform, &Velocity)>) { - for (transform, velocity) in query.iter_mut() { + for (mut transform, velocity) in query.iter_mut() { transform.translation.x += velocity.x * config::TIME_STEP; transform.translation.y += velocity.y * config::TIME_STEP; } From 803c0e77fd325b82593c584afa2a530ebb2f8b62 Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Tue, 4 May 2021 00:16:18 -0400 Subject: [PATCH 21/47] Moved many consts to local variables Working around https://github.com/bitshifter/glam-rs/issues/173 :( --- examples/game/breakout.rs | 69 +++++++++++++++++++++------------------ 1 file changed, 37 insertions(+), 32 deletions(-) diff --git a/examples/game/breakout.rs b/examples/game/breakout.rs index 26f43e8a49a94..7918a632d9b39 100644 --- a/examples/game/breakout.rs +++ b/examples/game/breakout.rs @@ -10,33 +10,19 @@ use resources::*; /// Constants that can be used to fine-tune the behavior of our game mod config { - use super::Velocity; - use bevy::math::Vec2; use bevy::render::color::Color; - use bevy::transform::components::Transform; use bevy::ui::Val; + // TODO: add various Vec2's and Transforms to this config module for clarity and consistency + // Blocked on https://github.com/bitshifter/glam-rs/issues/173 pub const TIME_STEP: f32 = 1.0 / 60.0; pub const BACKGROUND_COLOR: Color = Color::rgb(0.9, 0.9, 0.9); pub const PADDLE_COLOR: Color = Color::rgb(0.5, 0.5, 1.0); - pub const PADDLE_STARTING_LOCATION: Transform = Transform::from_xyz(0.0, -215.0, 0.0); - pub const PADDLE_SIZE: Vec2 = Vec2::new(120.0, 30.0); pub const PADDLE_SPEED: f32 = 500.0; pub const BALL_COLOR: Color = Color::rgb(1.0, 0.5, 0.5); - // We set the z-value to one to ensure it appears on top of our other objects in case of overlap - pub const BALL_STARTING_LOCATION: Transform = Transform::from_xyz(0.0, -50.0, 1.0); - // Our ball is actually a square. Shhh... - pub const BALL_SIZE: Vec2 = Vec2::new(30.0, 30.0); - const BALL_STARTING_DIRECTION: Vec2 = Vec2::new(0.5, -0.5).normalize(); - const BALL_STARTING_SPEED: f32 = 400.0; - pub const BALL_STARTING_VELOCITY: Velocity = Velocity { - x: BALL_STARTING_DIRECTION.x * BALL_STARTING_SPEED, - y: BALL_STARTING_DIRECTION.y * BALL_STARTING_SPEED, - }; - pub const ARENA_BOUNDS: Vec2 = Vec2::new(900.0, 600.0); pub const WALL_THICKNESS: f32 = 10.0; pub const WALL_COLOR: Color = Color::rgb(0.8, 0.8, 0.8); @@ -60,6 +46,7 @@ fn main() { .add_startup_system(spawn_paddle.system()) .add_startup_system(spawn_ball.system()) .add_startup_system(spawn_walls.system()) + .add_startup_system(spawn_bricks.system()) .add_startup_system(spawn_scoreboard.system()) // These systems run repeatedly, whnever the FixedTimeStep's duration has elapsed .add_system_set( @@ -106,11 +93,14 @@ fn spawn_cameras(mut commands: Commands) { } fn spawn_paddle(mut commands: Commands, mut materials: ResMut>) { + let paddle_starting_location: Transform = Transform::from_xyz(0.0, -215.0, 0.0); + let paddle_size: Vec2 = Vec2::new(120.0, 30.0); + commands .spawn_bundle(SpriteBundle { material: materials.add(config::PADDLE_COLOR.into()), - transform: config::PADDLE_STARTING_LOCATION, - sprite: Sprite::new(config::PADDLE_SIZE), + transform: paddle_starting_location, + sprite: Sprite::new(paddle_size), ..Default::default() }) .insert(Paddle { @@ -121,17 +111,29 @@ fn spawn_paddle(mut commands: Commands, mut materials: ResMut>) { + // We set the z-value to one to ensure it appears on top of our other objects in case of overlap + let ball_starting_location: Transform = Transform::from_xyz(0.0, -50.0, 1.0); + // Our ball is actually a square. Shhh... + let ball_size: Vec2 = Vec2::new(30.0, 30.0); + + let ball_starting_direction: Vec2 = Vec2::new(0.5, -0.5).normalize(); + let ball_starting_speed: f32 = 400.0; + let ball_starting_velocity: Velocity = Velocity { + x: ball_starting_direction.x * ball_starting_speed, + y: ball_starting_direction.y * ball_starting_speed, + }; + commands .spawn_bundle(SpriteBundle { material: materials.add(config::BALL_COLOR.into()), - transform: config::BALL_STARTING_LOCATION, - sprite: Sprite::new(config::BALL_SIZE), + transform: ball_starting_location, + sprite: Sprite::new(ball_size), ..Default::default() }) .insert(Ball) .insert(Collides) // Adds a `Velocity` component with the value defined in the `config` module - .insert(config::BALL_STARTING_VELOCITY); + .insert(ball_starting_velocity); } /// Defines which side of the arena a wall is part of @@ -174,7 +176,9 @@ struct WallBundle { impl WallBundle { fn new(side: Side, material_handle: &Handle) -> Self { - let bounds = config::ARENA_BOUNDS; + let arena_bounds: Vec2 = Vec2::new(900.0, 600.0); + + let bounds = arena_bounds; let thickness = config::WALL_THICKNESS; WallBundle { @@ -198,26 +202,27 @@ fn spawn_walls(mut commands: Commands, mut materials: ResMut>) { +fn spawn_bricks(mut commands: Commands, mut materials: ResMut>) { let brick_material = materials.add(config::BRICK_COLOR.into()); // Brick layout constants - const brick_rows: i8 = 4; - const brick_columns: i8 = 5; - const brick_spacing: f32 = 20.0; - const brick_size: Vec2 = Vec2::new(150.0, 30.0); + const BRICK_ROWS: i8 = 4; + const BRICK_COLUMNS: i8 = 5; + const BRICK_SPACING: f32 = 20.0; + // TODO: change to const when https://github.com/bitshifter/glam-rs/issues/173 is fixed + let brick_size: Vec2 = Vec2::new(150.0, 30.0); // Compute the total width that all of the bricks take - let total_width = brick_columns as f32 * (brick_size.x + brick_spacing) - brick_spacing; + let total_width = BRICK_COLUMNS as f32 * (brick_size.x + BRICK_SPACING) - BRICK_SPACING; // Center the bricks and move them up a bit let bricks_offset = Vec3::new(-(total_width - brick_size.x) / 2.0, 100.0, 0.0); // Add the bricks - for row in 0..brick_rows { - for column in 0..brick_columns { + for row in 0..BRICK_ROWS { + for column in 0..BRICK_COLUMNS { let brick_position = Vec3::new( - column as f32 * (brick_size.x + brick_spacing), - row as f32 * (brick_size.y + brick_spacing), + column as f32 * (brick_size.x + BRICK_SPACING), + row as f32 * (brick_size.y + BRICK_SPACING), 0.0, ) + bricks_offset; // Adding one brick at a time From 400f3a3aab70c0683e8e352413fff67100bd625b Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Tue, 4 May 2021 00:26:53 -0400 Subject: [PATCH 22/47] Add missing marker component --- examples/game/breakout.rs | 56 ++++++++++++++++++++------------------- 1 file changed, 29 insertions(+), 27 deletions(-) diff --git a/examples/game/breakout.rs b/examples/game/breakout.rs index 7918a632d9b39..2da32f3f4d84e 100644 --- a/examples/game/breakout.rs +++ b/examples/game/breakout.rs @@ -240,39 +240,41 @@ fn spawn_bricks(mut commands: Commands, mut materials: ResMut) { - commands.spawn_bundle(TextBundle { - text: Text { - sections: vec![ - TextSection { - value: "Score: ".to_string(), - style: TextStyle { - font: asset_server.load("fonts/FiraSans-Bold.ttf"), - font_size: config::SCORE_FONT_SIZE, - color: config::SCOREBOARD_COLOR, + commands + .spawn_bundle(TextBundle { + text: Text { + sections: vec![ + TextSection { + value: "Score: ".to_string(), + style: TextStyle { + font: asset_server.load("fonts/FiraSans-Bold.ttf"), + font_size: config::SCORE_FONT_SIZE, + color: config::SCOREBOARD_COLOR, + }, }, - }, - TextSection { - value: "".to_string(), - style: TextStyle { - font: asset_server.load("fonts/FiraMono-Medium.ttf"), - font_size: config::SCORE_FONT_SIZE, - color: config::SCORE_COLOR, + TextSection { + value: "".to_string(), + style: TextStyle { + font: asset_server.load("fonts/FiraMono-Medium.ttf"), + font_size: config::SCORE_FONT_SIZE, + color: config::SCORE_COLOR, + }, }, + ], + ..Default::default() + }, + style: Style { + position_type: PositionType::Absolute, + position: Rect { + top: config::SCORE_PADDING, + left: config::SCORE_PADDING, + ..Default::default() }, - ], - ..Default::default() - }, - style: Style { - position_type: PositionType::Absolute, - position: Rect { - top: config::SCORE_PADDING, - left: config::SCORE_PADDING, ..Default::default() }, ..Default::default() - }, - ..Default::default() - }); + }) + .insert(Scoreboard); } /// Moves everything with both a Transform and a Velovity accordingly From 3d009c2794d2d1df9090b3cfb15605c50d416a23 Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Tue, 4 May 2021 00:33:55 -0400 Subject: [PATCH 23/47] Improve comments --- examples/game/breakout.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/examples/game/breakout.rs b/examples/game/breakout.rs index 2da32f3f4d84e..7c3f49a1d6e12 100644 --- a/examples/game/breakout.rs +++ b/examples/game/breakout.rs @@ -169,6 +169,7 @@ impl Side { // By creating our own bundles, we can avoid duplicating code #[derive(Bundle)] struct WallBundle { + // Use #[bundle] like this to nest bundles correctly #[bundle] sprite_bundle: SpriteBundle, collides: Collides, @@ -307,6 +308,7 @@ fn bound_paddle(mut query: Query<&mut Transform, With>) { paddle_transform.translation.x = paddle_transform.translation.x.min(380.0).max(-380.0); } +/// Detects and handles ball collisions fn ball_collision( mut ball_query: Query<(&Transform, &mut Velocity, &Sprite), With>, // Option<&C> returns Some(c: C) if the component exists on the entity, and None if it does not @@ -368,7 +370,7 @@ fn ball_collision( } } -/// Updates the Scoreboard entity based on the Score resource +/// Updates the Scoreboard entity's Text based on the value of the Score resource fn update_scoreboard(score: Res, mut query: Query<&mut Text, With>) { let mut scoreboard_text = query.single_mut().unwrap(); scoreboard_text.sections[0].value = format!("Score: {}", score.0); From 6e37cf40d43e278f019035c05f7bbc7204f36e15 Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Tue, 4 May 2021 00:47:26 -0400 Subject: [PATCH 24/47] Removed magic number --- examples/game/breakout.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/game/breakout.rs b/examples/game/breakout.rs index 7c3f49a1d6e12..ef395a994087f 100644 --- a/examples/game/breakout.rs +++ b/examples/game/breakout.rs @@ -304,8 +304,9 @@ fn paddle_input(keyboard_input: Res>, mut query: Query<(&Paddle, /// Ensures our paddle never goes out of bounds fn bound_paddle(mut query: Query<&mut Transform, With>) { + const BOUND: f32 = 380.0; let mut paddle_transform = query.single_mut().unwrap(); - paddle_transform.translation.x = paddle_transform.translation.x.min(380.0).max(-380.0); + paddle_transform.translation.x = paddle_transform.translation.x.min(BOUND).max(-BOUND); } /// Detects and handles ball collisions From be14d7a8c43912e50af29afcace642a3847e31af Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Tue, 4 May 2021 00:58:37 -0400 Subject: [PATCH 25/47] Fixed paddle input --- examples/game/breakout.rs | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/examples/game/breakout.rs b/examples/game/breakout.rs index ef395a994087f..a9e9c2eb0680f 100644 --- a/examples/game/breakout.rs +++ b/examples/game/breakout.rs @@ -53,11 +53,11 @@ fn main() { SystemSet::new() .with_run_criteria(FixedTimestep::step(config::TIME_STEP as f64)) .with_system(kinematics.system()) - .with_system(paddle_input.system()) .with_system(ball_collision.system()), ) // Ordinary systems run every frame - .add_system(bound_paddle.system()) + .add_system(bound_paddle.system().label("bound_paddle")) + .add_system(paddle_input.system().after("bound_paddle")) .add_system(update_scoreboard.system()) .run(); } @@ -299,14 +299,21 @@ fn paddle_input(keyboard_input: Res>, mut query: Query<(&Paddle, direction += 1.0; } - velocity.x += direction * paddle.speed; + velocity.x = direction * paddle.speed; } /// Ensures our paddle never goes out of bounds -fn bound_paddle(mut query: Query<&mut Transform, With>) { +fn bound_paddle(mut query: Query<(&mut Transform, &mut Velocity), With>) { const BOUND: f32 = 380.0; - let mut paddle_transform = query.single_mut().unwrap(); - paddle_transform.translation.x = paddle_transform.translation.x.min(BOUND).max(-BOUND); + let (mut paddle_transform, mut paddle_velocity) = query.single_mut().unwrap(); + + if paddle_transform.translation.x >= BOUND { + paddle_transform.translation.x = BOUND; + paddle_velocity.x = 0.0; + } else if paddle_transform.translation.x <= -BOUND { + paddle_transform.translation.x = -BOUND; + paddle_velocity.x = 0.0; + } } /// Detects and handles ball collisions From 76c6fba7fba47c7b170ac8081adc9c8c9d468eec Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Tue, 4 May 2021 01:00:03 -0400 Subject: [PATCH 26/47] Collision should always run before kinematics Reduces over-penetration risk --- examples/game/breakout.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/game/breakout.rs b/examples/game/breakout.rs index a9e9c2eb0680f..256c3c0dbebfb 100644 --- a/examples/game/breakout.rs +++ b/examples/game/breakout.rs @@ -52,8 +52,8 @@ fn main() { .add_system_set( SystemSet::new() .with_run_criteria(FixedTimestep::step(config::TIME_STEP as f64)) - .with_system(kinematics.system()) - .with_system(ball_collision.system()), + .with_system(kinematics.system().label("kinematics")) + .with_system(ball_collision.system().before("kinematics")), ) // Ordinary systems run every frame .add_system(bound_paddle.system().label("bound_paddle")) From c970378c49519413d4b0cf4108c8061b56a67efa Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Tue, 4 May 2021 02:54:59 -0400 Subject: [PATCH 27/47] Fixed walls --- examples/game/breakout.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/game/breakout.rs b/examples/game/breakout.rs index 256c3c0dbebfb..36daa207b518c 100644 --- a/examples/game/breakout.rs +++ b/examples/game/breakout.rs @@ -158,10 +158,10 @@ impl Side { fn wall_size(&self, bounds: Vec2, thickness: f32) -> Vec2 { match self { - Side::Top => Vec2::new(thickness, bounds.y + thickness), - Side::Bottom => Vec2::new(thickness, bounds.y + thickness), - Side::Left => Vec2::new(bounds.x + thickness, thickness), - Side::Right => Vec2::new(bounds.x + thickness, thickness), + Side::Top => Vec2::new(bounds.x + thickness, thickness), + Side::Bottom => Vec2::new(bounds.x + thickness, thickness), + Side::Left => Vec2::new(thickness, bounds.y + thickness), + Side::Right => Vec2::new(thickness, bounds.y + thickness), } } } From 658a8c635c1a1d80cb72164e6abd81e79dd277fc Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Tue, 4 May 2021 02:59:50 -0400 Subject: [PATCH 28/47] Point to non-duplicate issue --- examples/game/breakout.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/game/breakout.rs b/examples/game/breakout.rs index 36daa207b518c..a8009693b4d39 100644 --- a/examples/game/breakout.rs +++ b/examples/game/breakout.rs @@ -13,7 +13,7 @@ mod config { use bevy::render::color::Color; use bevy::ui::Val; // TODO: add various Vec2's and Transforms to this config module for clarity and consistency - // Blocked on https://github.com/bitshifter/glam-rs/issues/173 + // Blocked on https://github.com/bitshifter/glam-rs/issues/76 pub const TIME_STEP: f32 = 1.0 / 60.0; pub const BACKGROUND_COLOR: Color = Color::rgb(0.9, 0.9, 0.9); @@ -210,7 +210,7 @@ fn spawn_bricks(mut commands: Commands, mut materials: ResMut Date: Tue, 4 May 2021 13:14:43 -0400 Subject: [PATCH 29/47] Fixed scoreboard text formatting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: François --- examples/game/breakout.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/game/breakout.rs b/examples/game/breakout.rs index a8009693b4d39..f5464275a1523 100644 --- a/examples/game/breakout.rs +++ b/examples/game/breakout.rs @@ -381,5 +381,5 @@ fn ball_collision( /// Updates the Scoreboard entity's Text based on the value of the Score resource fn update_scoreboard(score: Res, mut query: Query<&mut Text, With>) { let mut scoreboard_text = query.single_mut().unwrap(); - scoreboard_text.sections[0].value = format!("Score: {}", score.0); + scoreboard_text.sections[1].value = format!("{}", score.0); } From 49e70708d87cdac36825487ccda3763c63859565 Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Tue, 4 May 2021 13:17:26 -0400 Subject: [PATCH 30/47] Whitespace Co-authored-by: bjorn3 --- examples/game/breakout.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/game/breakout.rs b/examples/game/breakout.rs index f5464275a1523..f732b45a5a98a 100644 --- a/examples/game/breakout.rs +++ b/examples/game/breakout.rs @@ -71,6 +71,7 @@ mod components { pub struct Paddle { pub speed: f32, } + // These are data-less marker components // which let us query for the correct entities // and specialize behavior From c5fa6858ec3b0f1e40c4ed029c7199ef9a73a495 Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Tue, 4 May 2021 13:17:39 -0400 Subject: [PATCH 31/47] Typo fix Co-authored-by: bjorn3 --- examples/game/breakout.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/game/breakout.rs b/examples/game/breakout.rs index f732b45a5a98a..e5fb02d018c8e 100644 --- a/examples/game/breakout.rs +++ b/examples/game/breakout.rs @@ -279,7 +279,7 @@ fn spawn_scoreboard(mut commands: Commands, asset_server: Res) { .insert(Scoreboard); } -/// Moves everything with both a Transform and a Velovity accordingly +/// Moves everything with both a Transform and a Velocity accordingly fn kinematics(mut query: Query<(&mut Transform, &Velocity)>) { for (mut transform, velocity) in query.iter_mut() { transform.translation.x += velocity.x * config::TIME_STEP; From 225c143be94dba3770d7f24c88eed755b2325bdc Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Tue, 4 May 2021 13:21:55 -0400 Subject: [PATCH 32/47] Reordered systems to fix input lag --- examples/game/breakout.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/examples/game/breakout.rs b/examples/game/breakout.rs index e5fb02d018c8e..d515606330e36 100644 --- a/examples/game/breakout.rs +++ b/examples/game/breakout.rs @@ -56,8 +56,13 @@ fn main() { .with_system(ball_collision.system().before("kinematics")), ) // Ordinary systems run every frame - .add_system(bound_paddle.system().label("bound_paddle")) - .add_system(paddle_input.system().after("bound_paddle")) + .add_system(paddle_input.system().before("bound_paddle")) + .add_system( + bound_paddle + .system() + .label("bound_paddle") + .after("kinematics"), + ) .add_system(update_scoreboard.system()) .run(); } From 974e602863f20790e7fcc484083279d3f25ed453 Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Tue, 4 May 2021 13:26:48 -0400 Subject: [PATCH 33/47] Swapped to for_each loops --- examples/game/breakout.rs | 95 ++++++++++++++++++++------------------- 1 file changed, 48 insertions(+), 47 deletions(-) diff --git a/examples/game/breakout.rs b/examples/game/breakout.rs index d515606330e36..ad9a242188ed4 100644 --- a/examples/game/breakout.rs +++ b/examples/game/breakout.rs @@ -286,10 +286,10 @@ fn spawn_scoreboard(mut commands: Commands, asset_server: Res) { /// Moves everything with both a Transform and a Velocity accordingly fn kinematics(mut query: Query<(&mut Transform, &Velocity)>) { - for (mut transform, velocity) in query.iter_mut() { + query.for_each_mut(|(mut transform, velocity)| { transform.translation.x += velocity.x * config::TIME_STEP; transform.translation.y += velocity.y * config::TIME_STEP; - } + }); } /// Turns left and right arrow key inputs to set paddle velocity @@ -336,52 +336,53 @@ fn ball_collision( let (ball_transform, mut ball_velocity, ball_sprite) = ball_query.single_mut().unwrap(); let ball_size = ball_sprite.size; - for (collider_entity, collider_transform, collider_sprite, maybe_brick) in collider_query.iter() - { - // Check for collisions - let collider_size = collider_sprite.size; - let potential_collision = collide( - ball_transform.translation, - ball_size, - collider_transform.translation, - collider_size, - ); - - // Handle collisions - if let Some(collision) = potential_collision { - // Reflect the ball when it collides - let mut reflect_x = false; - let mut reflect_y = false; - - // Only reflect if the ball's velocity is going - // in the opposite direction of the collision - match collision { - Collision::Left => reflect_x = ball_velocity.x > 0.0, - Collision::Right => reflect_x = ball_velocity.x < 0.0, - Collision::Top => reflect_y = ball_velocity.y < 0.0, - Collision::Bottom => reflect_y = ball_velocity.y > 0.0, - } - - // Reflect velocity on the x-axis if we hit something on the x-axis - if reflect_x { - ball_velocity.x = -ball_velocity.x; - } - - // Reflect velocity on the y-axis if we hit something on the y-axis - if reflect_y { - ball_velocity.y = -ball_velocity.y; + collider_query.for_each( + |(collider_entity, collider_transform, collider_sprite, maybe_brick)| { + // Check for collisions + let collider_size = collider_sprite.size; + let potential_collision = collide( + ball_transform.translation, + ball_size, + collider_transform.translation, + collider_size, + ); + + // Handle collisions + if let Some(collision) = potential_collision { + // Reflect the ball when it collides + let mut reflect_x = false; + let mut reflect_y = false; + + // Only reflect if the ball's velocity is going + // in the opposite direction of the collision + match collision { + Collision::Left => reflect_x = ball_velocity.x > 0.0, + Collision::Right => reflect_x = ball_velocity.x < 0.0, + Collision::Top => reflect_y = ball_velocity.y < 0.0, + Collision::Bottom => reflect_y = ball_velocity.y > 0.0, + } + + // Reflect velocity on the x-axis if we hit something on the x-axis + if reflect_x { + ball_velocity.x = -ball_velocity.x; + } + + // Reflect velocity on the y-axis if we hit something on the y-axis + if reflect_y { + ball_velocity.y = -ball_velocity.y; + } + + // Perform special brick collision behavior + if maybe_brick.is_some() { + // Despawn bricks that are hit + commands.entity(collider_entity).despawn(); + + // Increase the score by 1 for each brick hit + score.0 += 1; + } } - - // Perform special brick collision behavior - if maybe_brick.is_some() { - // Despawn bricks that are hit - commands.entity(collider_entity).despawn(); - - // Increase the score by 1 for each brick hit - score.0 += 1; - } - } - } + }, + ); } /// Updates the Scoreboard entity's Text based on the value of the Score resource From 339074243e9247721c8f675eecc87d5de8cdec7b Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Tue, 4 May 2021 13:47:28 -0400 Subject: [PATCH 34/47] Refactor to BrickBundle --- examples/game/breakout.rs | 54 +++++++++++++++++++++++++-------------- 1 file changed, 35 insertions(+), 19 deletions(-) diff --git a/examples/game/breakout.rs b/examples/game/breakout.rs index ad9a242188ed4..430a5f43784a1 100644 --- a/examples/game/breakout.rs +++ b/examples/game/breakout.rs @@ -6,6 +6,7 @@ use bevy::{ }; use components::*; +use config::{BRICK_HEIGHT, BRICK_WIDTH}; use resources::*; /// Constants that can be used to fine-tune the behavior of our game @@ -27,6 +28,8 @@ mod config { pub const WALL_COLOR: Color = Color::rgb(0.8, 0.8, 0.8); pub const BRICK_COLOR: Color = Color::rgb(0.5, 0.5, 1.0); + pub const BRICK_WIDTH: f32 = 150.0; + pub const BRICK_HEIGHT: f32 = 30.0; pub const SCOREBOARD_COLOR: Color = Color::rgb(0.5, 0.5, 1.0); pub const SCORE_COLOR: Color = Color::rgb(1.0, 0.5, 0.5); @@ -208,6 +211,28 @@ fn spawn_walls(mut commands: Commands, mut materials: ResMut) -> Self { + BrickBundle { + sprite_bundle: SpriteBundle { + material: material_handle.clone(), + transform: Transform::from_xyz(x, y, 0.0), + sprite: Sprite::new(Vec2::new(BRICK_WIDTH, BRICK_HEIGHT)), + ..Default::default() + }, + brick: Brick, + collides: Collides, + } + } +} fn spawn_bricks(mut commands: Commands, mut materials: ResMut>) { let brick_material = materials.add(config::BRICK_COLOR.into()); @@ -216,32 +241,23 @@ fn spawn_bricks(mut commands: Commands, mut materials: ResMut Date: Tue, 4 May 2021 13:48:38 -0400 Subject: [PATCH 35/47] Prettier glob imports for CONFIG --- examples/game/breakout.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/game/breakout.rs b/examples/game/breakout.rs index 430a5f43784a1..d500977ad3f82 100644 --- a/examples/game/breakout.rs +++ b/examples/game/breakout.rs @@ -6,7 +6,7 @@ use bevy::{ }; use components::*; -use config::{BRICK_HEIGHT, BRICK_WIDTH}; +use config::*; use resources::*; /// Constants that can be used to fine-tune the behavior of our game From 7a7da6e055ff9254dac6283a2848e41a4e9941e8 Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Tue, 4 May 2021 13:57:10 -0400 Subject: [PATCH 36/47] Const cleanup --- examples/game/breakout.rs | 46 +++++++++++++++++++-------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/examples/game/breakout.rs b/examples/game/breakout.rs index d500977ad3f82..481e229b65b8f 100644 --- a/examples/game/breakout.rs +++ b/examples/game/breakout.rs @@ -30,18 +30,23 @@ mod config { pub const BRICK_COLOR: Color = Color::rgb(0.5, 0.5, 1.0); pub const BRICK_WIDTH: f32 = 150.0; pub const BRICK_HEIGHT: f32 = 30.0; + pub const BRICK_ROWS: i8 = 4; + pub const BRICK_COLUMNS: i8 = 5; + pub const BRICK_SPACING: f32 = 20.0; pub const SCOREBOARD_COLOR: Color = Color::rgb(0.5, 0.5, 1.0); pub const SCORE_COLOR: Color = Color::rgb(1.0, 0.5, 0.5); pub const SCORE_FONT_SIZE: f32 = 40.0; pub const SCORE_PADDING: Val = Val::Px(5.0); + pub const SCOREBOARD_FONT_PATH: &str = "fonts/FiraSans-Bold.ttf"; + pub const SCORE_FONT_PATH: &str = "fonts/FiraSans-Bold.ttf"; } /// A simple implementation of the classic game "Breakout" fn main() { App::build() .add_plugins(DefaultPlugins) - .insert_resource(ClearColor(config::BACKGROUND_COLOR)) + .insert_resource(ClearColor(BACKGROUND_COLOR)) // This adds the Score resource with its default values: 0 .init_resource::() // These systems run only once, before all other systems @@ -54,7 +59,7 @@ fn main() { // These systems run repeatedly, whnever the FixedTimeStep's duration has elapsed .add_system_set( SystemSet::new() - .with_run_criteria(FixedTimestep::step(config::TIME_STEP as f64)) + .with_run_criteria(FixedTimestep::step(TIME_STEP as f64)) .with_system(kinematics.system().label("kinematics")) .with_system(ball_collision.system().before("kinematics")), ) @@ -107,13 +112,13 @@ fn spawn_paddle(mut commands: Commands, mut materials: ResMut>) { - let material_handle = materials.add(config::WALL_COLOR.into()); + let material_handle = materials.add(WALL_COLOR.into()); commands.spawn_bundle(WallBundle::new(Side::Top, &material_handle)); commands.spawn_bundle(WallBundle::new(Side::Bottom, &material_handle)); @@ -235,12 +240,7 @@ impl BrickBundle { } fn spawn_bricks(mut commands: Commands, mut materials: ResMut>) { - let brick_material = materials.add(config::BRICK_COLOR.into()); - - // Brick layout constants - const BRICK_ROWS: i8 = 4; - const BRICK_COLUMNS: i8 = 5; - const BRICK_SPACING: f32 = 20.0; + let brick_material = materials.add(BRICK_COLOR.into()); // Compute the total width that all of the bricks take const TOTAL_WIDTH: f32 = BRICK_COLUMNS as f32 * (BRICK_WIDTH + BRICK_SPACING) - BRICK_SPACING; @@ -270,17 +270,17 @@ fn spawn_scoreboard(mut commands: Commands, asset_server: Res) { TextSection { value: "Score: ".to_string(), style: TextStyle { - font: asset_server.load("fonts/FiraSans-Bold.ttf"), - font_size: config::SCORE_FONT_SIZE, - color: config::SCOREBOARD_COLOR, + font: asset_server.load(SCOREBOARD_FONT_PATH), + font_size: SCORE_FONT_SIZE, + color: SCOREBOARD_COLOR, }, }, TextSection { value: "".to_string(), style: TextStyle { - font: asset_server.load("fonts/FiraMono-Medium.ttf"), - font_size: config::SCORE_FONT_SIZE, - color: config::SCORE_COLOR, + font: asset_server.load(SCORE_FONT_PATH), + font_size: SCORE_FONT_SIZE, + color: SCORE_COLOR, }, }, ], @@ -289,8 +289,8 @@ fn spawn_scoreboard(mut commands: Commands, asset_server: Res) { style: Style { position_type: PositionType::Absolute, position: Rect { - top: config::SCORE_PADDING, - left: config::SCORE_PADDING, + top: SCORE_PADDING, + left: SCORE_PADDING, ..Default::default() }, ..Default::default() @@ -303,8 +303,8 @@ fn spawn_scoreboard(mut commands: Commands, asset_server: Res) { /// Moves everything with both a Transform and a Velocity accordingly fn kinematics(mut query: Query<(&mut Transform, &Velocity)>) { query.for_each_mut(|(mut transform, velocity)| { - transform.translation.x += velocity.x * config::TIME_STEP; - transform.translation.y += velocity.y * config::TIME_STEP; + transform.translation.x += velocity.x * TIME_STEP; + transform.translation.y += velocity.y * TIME_STEP; }); } From 2aa0742690e7fc7f85db2583df7da80df7c21507 Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Tue, 4 May 2021 14:35:06 -0400 Subject: [PATCH 37/47] Use spawn_batch to create many bricks --- examples/game/breakout.rs | 30 +++++++++++++++++++++--------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/examples/game/breakout.rs b/examples/game/breakout.rs index 481e229b65b8f..28de4bfa300f8 100644 --- a/examples/game/breakout.rs +++ b/examples/game/breakout.rs @@ -190,7 +190,7 @@ struct WallBundle { } impl WallBundle { - fn new(side: Side, material_handle: &Handle) -> Self { + fn new(side: Side, material_handle: Handle) -> Self { let arena_bounds: Vec2 = Vec2::new(900.0, 600.0); let bounds = arena_bounds; @@ -198,7 +198,7 @@ impl WallBundle { WallBundle { sprite_bundle: SpriteBundle { - material: material_handle.clone(), + material: material_handle, transform: side.wall_coord(bounds), sprite: Sprite::new(side.wall_size(bounds, thickness)), ..Default::default() @@ -211,10 +211,10 @@ impl WallBundle { fn spawn_walls(mut commands: Commands, mut materials: ResMut>) { let material_handle = materials.add(WALL_COLOR.into()); - commands.spawn_bundle(WallBundle::new(Side::Top, &material_handle)); - commands.spawn_bundle(WallBundle::new(Side::Bottom, &material_handle)); - commands.spawn_bundle(WallBundle::new(Side::Left, &material_handle)); - commands.spawn_bundle(WallBundle::new(Side::Right, &material_handle)); + commands.spawn_bundle(WallBundle::new(Side::Top, material_handle.clone())); + commands.spawn_bundle(WallBundle::new(Side::Bottom, material_handle.clone())); + commands.spawn_bundle(WallBundle::new(Side::Left, material_handle.clone())); + commands.spawn_bundle(WallBundle::new(Side::Right, material_handle.clone())); } #[derive(Bundle)] struct BrickBundle { @@ -225,10 +225,10 @@ struct BrickBundle { } impl BrickBundle { - fn new(x: f32, y: f32, material_handle: &Handle) -> Self { + fn new(x: f32, y: f32, material_handle: Handle) -> Self { BrickBundle { sprite_bundle: SpriteBundle { - material: material_handle.clone(), + material: material_handle, transform: Transform::from_xyz(x, y, 0.0), sprite: Sprite::new(Vec2::new(BRICK_WIDTH, BRICK_HEIGHT)), ..Default::default() @@ -250,9 +250,20 @@ fn spawn_bricks(mut commands: Commands, mut materials: ResMut) { From 680a251ff0b83c96bd5bd70cf1706a4012dfe3ce Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Tue, 4 May 2021 14:51:36 -0400 Subject: [PATCH 38/47] Const vec2 configs! --- examples/game/breakout.rs | 36 +++++++++++++++++------------------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/examples/game/breakout.rs b/examples/game/breakout.rs index 28de4bfa300f8..1e2fa7543817d 100644 --- a/examples/game/breakout.rs +++ b/examples/game/breakout.rs @@ -11,19 +11,25 @@ use resources::*; /// Constants that can be used to fine-tune the behavior of our game mod config { + // TODO: add various Transforms to this config module for clarity and consistency + use bevy::math::{const_vec2, Vec2}; use bevy::render::color::Color; use bevy::ui::Val; - // TODO: add various Vec2's and Transforms to this config module for clarity and consistency - // Blocked on https://github.com/bitshifter/glam-rs/issues/76 pub const TIME_STEP: f32 = 1.0 / 60.0; pub const BACKGROUND_COLOR: Color = Color::rgb(0.9, 0.9, 0.9); pub const PADDLE_COLOR: Color = Color::rgb(0.5, 0.5, 1.0); pub const PADDLE_SPEED: f32 = 500.0; + pub const PADDLE_SIZE: Vec2 = const_vec2!([120.0, 30.0]); pub const BALL_COLOR: Color = Color::rgb(1.0, 0.5, 0.5); + // Our ball is actually a square. Shhh... + pub const BALL_SIZE: Vec2 = const_vec2!([30.0, 30.0]); + pub const BALL_STARTING_DIRECTION: Vec2 = const_vec2!([0.5, -0.5]); + pub const BALL_STARTING_SPEED: f32 = 400.0; + pub const ARENA_BOUNDS: Vec2 = const_vec2!([900.0, 600.0]); pub const WALL_THICKNESS: f32 = 10.0; pub const WALL_COLOR: Color = Color::rgb(0.8, 0.8, 0.8); @@ -108,13 +114,12 @@ fn spawn_cameras(mut commands: Commands) { fn spawn_paddle(mut commands: Commands, mut materials: ResMut>) { let paddle_starting_location: Transform = Transform::from_xyz(0.0, -215.0, 0.0); - let paddle_size: Vec2 = Vec2::new(120.0, 30.0); commands .spawn_bundle(SpriteBundle { material: materials.add(PADDLE_COLOR.into()), transform: paddle_starting_location, - sprite: Sprite::new(paddle_size), + sprite: Sprite::new(PADDLE_SIZE), ..Default::default() }) .insert(Paddle { @@ -127,26 +132,23 @@ fn spawn_paddle(mut commands: Commands, mut materials: ResMut>) { // We set the z-value to one to ensure it appears on top of our other objects in case of overlap let ball_starting_location: Transform = Transform::from_xyz(0.0, -50.0, 1.0); - // Our ball is actually a square. Shhh... - let ball_size: Vec2 = Vec2::new(30.0, 30.0); - let ball_starting_direction: Vec2 = Vec2::new(0.5, -0.5).normalize(); - let ball_starting_speed: f32 = 400.0; + // .normalize is not a const fn, so we have to perform this operation at runtime + let normalized_direction = BALL_STARTING_DIRECTION.normalize(); let ball_starting_velocity: Velocity = Velocity { - x: ball_starting_direction.x * ball_starting_speed, - y: ball_starting_direction.y * ball_starting_speed, + x: normalized_direction.x * BALL_STARTING_SPEED, + y: normalized_direction.y * BALL_STARTING_SPEED, }; commands .spawn_bundle(SpriteBundle { material: materials.add(BALL_COLOR.into()), transform: ball_starting_location, - sprite: Sprite::new(ball_size), + sprite: Sprite::new(BALL_SIZE), ..Default::default() }) .insert(Ball) .insert(Collides) - // Adds a `Velocity` component with the value defined in the `config` module .insert(ball_starting_velocity); } @@ -191,16 +193,11 @@ struct WallBundle { impl WallBundle { fn new(side: Side, material_handle: Handle) -> Self { - let arena_bounds: Vec2 = Vec2::new(900.0, 600.0); - - let bounds = arena_bounds; - let thickness = WALL_THICKNESS; - WallBundle { sprite_bundle: SpriteBundle { material: material_handle, - transform: side.wall_coord(bounds), - sprite: Sprite::new(side.wall_size(bounds, thickness)), + transform: side.wall_coord(ARENA_BOUNDS), + sprite: Sprite::new(side.wall_size(ARENA_BOUNDS, WALL_THICKNESS)), ..Default::default() }, collides: Collides, @@ -216,6 +213,7 @@ fn spawn_walls(mut commands: Commands, mut materials: ResMut Date: Tue, 4 May 2021 15:15:29 -0400 Subject: [PATCH 39/47] Add useful FIXME notes about out-of-scope work --- examples/game/breakout.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/examples/game/breakout.rs b/examples/game/breakout.rs index 1e2fa7543817d..d1d5e4b7d2247 100644 --- a/examples/game/breakout.rs +++ b/examples/game/breakout.rs @@ -11,7 +11,8 @@ use resources::*; /// Constants that can be used to fine-tune the behavior of our game mod config { - // TODO: add various Transforms to this config module for clarity and consistency + // FIXME: add various Transforms to this config module for clarity and consistency + // Blocked on https://github.com/bevyengine/bevy/issues/2097 use bevy::math::{const_vec2, Vec2}; use bevy::render::color::Color; use bevy::ui::Val; @@ -134,6 +135,7 @@ fn spawn_ball(mut commands: Commands, mut materials: ResMut Date: Tue, 4 May 2021 15:28:05 -0400 Subject: [PATCH 40/47] Move PADDLE_BOUND to config const --- examples/game/breakout.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/examples/game/breakout.rs b/examples/game/breakout.rs index d1d5e4b7d2247..ab87404854361 100644 --- a/examples/game/breakout.rs +++ b/examples/game/breakout.rs @@ -23,6 +23,7 @@ mod config { pub const PADDLE_COLOR: Color = Color::rgb(0.5, 0.5, 1.0); pub const PADDLE_SPEED: f32 = 500.0; pub const PADDLE_SIZE: Vec2 = const_vec2!([120.0, 30.0]); + pub const PADDLE_BOUND: f32 = 380.0; pub const BALL_COLOR: Color = Color::rgb(1.0, 0.5, 0.5); // Our ball is actually a square. Shhh... @@ -338,14 +339,13 @@ fn paddle_input(keyboard_input: Res>, mut query: Query<(&Paddle, /// Ensures our paddle never goes out of bounds fn bound_paddle(mut query: Query<(&mut Transform, &mut Velocity), With>) { - const BOUND: f32 = 380.0; let (mut paddle_transform, mut paddle_velocity) = query.single_mut().unwrap(); - if paddle_transform.translation.x >= BOUND { - paddle_transform.translation.x = BOUND; + if paddle_transform.translation.x >= PADDLE_BOUND { + paddle_transform.translation.x = PADDLE_BOUND; paddle_velocity.x = 0.0; - } else if paddle_transform.translation.x <= -BOUND { - paddle_transform.translation.x = -BOUND; + } else if paddle_transform.translation.x <= -PADDLE_BOUND { + paddle_transform.translation.x = -PADDLE_BOUND; paddle_velocity.x = 0.0; } } From d3c0901ea411ac54d64d691900e1a02049a886b8 Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Tue, 4 May 2021 15:28:14 -0400 Subject: [PATCH 41/47] Improved comments --- examples/game/breakout.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/examples/game/breakout.rs b/examples/game/breakout.rs index ab87404854361..f814d27f0a676 100644 --- a/examples/game/breakout.rs +++ b/examples/game/breakout.rs @@ -55,7 +55,7 @@ fn main() { App::build() .add_plugins(DefaultPlugins) .insert_resource(ClearColor(BACKGROUND_COLOR)) - // This adds the Score resource with its default values: 0 + // This adds the Score resource with its default value of 0 .init_resource::() // These systems run only once, before all other systems .add_startup_system(spawn_cameras.system()) @@ -260,6 +260,8 @@ fn spawn_bricks(mut commands: Commands, mut materials: ResMut, mut query: Query<&mut Text, With>) { let mut scoreboard_text = query.single_mut().unwrap(); + // We need to access the second section, so we need to access the sections field at the [1] index + // (Rust is 0-indexed: https://medium.com/analytics-vidhya/array-indexing-0-based-or-1-based-dd89d631d11c) scoreboard_text.sections[1].value = format!("{}", score.0); } From bd0ab2b6a4e3ee80a6c8de23678f9f8091bc362f Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Tue, 4 May 2021 15:43:57 -0400 Subject: [PATCH 42/47] Added const Transforms Credit to @cart in https://github.com/bevyengine/bevy/issues/2097#issuecomment-832189695 --- examples/game/breakout.rs | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/examples/game/breakout.rs b/examples/game/breakout.rs index f814d27f0a676..10a3cc9e4ab31 100644 --- a/examples/game/breakout.rs +++ b/examples/game/breakout.rs @@ -11,10 +11,9 @@ use resources::*; /// Constants that can be used to fine-tune the behavior of our game mod config { - // FIXME: add various Transforms to this config module for clarity and consistency - // Blocked on https://github.com/bevyengine/bevy/issues/2097 - use bevy::math::{const_vec2, Vec2}; + use bevy::math::{const_quat, const_vec2, const_vec3, Vec2}; use bevy::render::color::Color; + use bevy::transform::components::Transform; use bevy::ui::Val; pub const TIME_STEP: f32 = 1.0 / 60.0; @@ -24,12 +23,25 @@ mod config { pub const PADDLE_SPEED: f32 = 500.0; pub const PADDLE_SIZE: Vec2 = const_vec2!([120.0, 30.0]); pub const PADDLE_BOUND: f32 = 380.0; + pub const PADDLE_STARTING_TRANSFORM: Transform = Transform { + translation: const_vec3!([0.0, -215.0, 0.0]), + // We don't want any rotation + rotation: const_quat!([0.0, 0.0, 0.0, 0.0]), + // We want the scale to be 1 in all directions + scale: const_vec3!([1.0, 1.0, 1.0]), + }; pub const BALL_COLOR: Color = Color::rgb(1.0, 0.5, 0.5); // Our ball is actually a square. Shhh... pub const BALL_SIZE: Vec2 = const_vec2!([30.0, 30.0]); pub const BALL_STARTING_DIRECTION: Vec2 = const_vec2!([0.5, -0.5]); pub const BALL_STARTING_SPEED: f32 = 400.0; + // We set the z-value to one to ensure it appears on top of our other objects in case of overlap + pub const BALL_STARTING_TRANSFORM: Transform = Transform { + translation: const_vec3!([0.0, -50.0, 1.0]), + rotation: const_quat!([0.0, 0.0, 0.0, 0.0]), + scale: const_vec3!([1.0, 1.0, 1.0]), + }; pub const ARENA_BOUNDS: Vec2 = const_vec2!([900.0, 600.0]); pub const WALL_THICKNESS: f32 = 10.0; @@ -115,12 +127,10 @@ fn spawn_cameras(mut commands: Commands) { } fn spawn_paddle(mut commands: Commands, mut materials: ResMut>) { - let paddle_starting_location: Transform = Transform::from_xyz(0.0, -215.0, 0.0); - commands .spawn_bundle(SpriteBundle { material: materials.add(PADDLE_COLOR.into()), - transform: paddle_starting_location, + transform: PADDLE_STARTING_TRANSFORM, sprite: Sprite::new(PADDLE_SIZE), ..Default::default() }) @@ -132,9 +142,6 @@ fn spawn_paddle(mut commands: Commands, mut materials: ResMut>) { - // We set the z-value to one to ensure it appears on top of our other objects in case of overlap - let ball_starting_location: Transform = Transform::from_xyz(0.0, -50.0, 1.0); - // .normalize is not a const fn, so we have to perform this operation at runtime // FIXME: Blocked on https://github.com/bitshifter/glam-rs/issues/76 let normalized_direction = BALL_STARTING_DIRECTION.normalize(); @@ -146,7 +153,7 @@ fn spawn_ball(mut commands: Commands, mut materials: ResMut Date: Tue, 4 May 2021 16:32:35 -0400 Subject: [PATCH 43/47] Satisfy clippy by removing redundant clone --- examples/game/breakout.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/game/breakout.rs b/examples/game/breakout.rs index 10a3cc9e4ab31..07969f052b984 100644 --- a/examples/game/breakout.rs +++ b/examples/game/breakout.rs @@ -218,10 +218,11 @@ impl WallBundle { fn spawn_walls(mut commands: Commands, mut materials: ResMut>) { let material_handle = materials.add(WALL_COLOR.into()); + // Each material handle must be uniquely owned as handles are ref-counted commands.spawn_bundle(WallBundle::new(Side::Top, material_handle.clone())); commands.spawn_bundle(WallBundle::new(Side::Bottom, material_handle.clone())); commands.spawn_bundle(WallBundle::new(Side::Left, material_handle.clone())); - commands.spawn_bundle(WallBundle::new(Side::Right, material_handle.clone())); + commands.spawn_bundle(WallBundle::new(Side::Right, material_handle)); } #[derive(Bundle)] From 46294c14d6d4682751387b822433d022feeaec51 Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Sat, 8 May 2021 18:21:20 -0400 Subject: [PATCH 44/47] Add comments explaining system order rationale Credit to @Kiiya for the idea! --- examples/game/breakout.rs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/examples/game/breakout.rs b/examples/game/breakout.rs index 07969f052b984..4cf94a6f7a9df 100644 --- a/examples/game/breakout.rs +++ b/examples/game/breakout.rs @@ -81,14 +81,23 @@ fn main() { SystemSet::new() .with_run_criteria(FixedTimestep::step(TIME_STEP as f64)) .with_system(kinematics.system().label("kinematics")) + // We need to check for collisions before handling movement + // to reduce the risk of the ball passing through objects .with_system(ball_collision.system().before("kinematics")), ) // Ordinary systems run every frame - .add_system(paddle_input.system().before("bound_paddle")) + // We need to handle input before we move our paddle, + // to ensure that we're responding to the most recent frame's events, + // avoiding input lag + // See https://github.com/bevyengine/bevy/blob/latest/examples/ecs/ecs_guide.rs + // for more information on system ordering + .add_system(paddle_input.system().before("bound_paddle").before("kinematics")) .add_system( bound_paddle .system() .label("bound_paddle") + // This system must run after kinematics, or the velocity will be set to 0 + // before the paddle moves, causing it to be stuck to the wall .after("kinematics"), ) .add_system(update_scoreboard.system()) From bd73201d3a15b7902bd697bde914d251f8c0102e Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Sun, 9 May 2021 19:23:36 -0400 Subject: [PATCH 45/47] Added exit_on_esc_system --- examples/game/breakout.rs | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/examples/game/breakout.rs b/examples/game/breakout.rs index 4cf94a6f7a9df..a1782751cbe65 100644 --- a/examples/game/breakout.rs +++ b/examples/game/breakout.rs @@ -1,5 +1,6 @@ use bevy::{ core::FixedTimestep, + input::system::exit_on_esc_system, prelude::*, render::pass::ClearColor, sprite::collide_aabb::{collide, Collision}, @@ -81,26 +82,34 @@ fn main() { SystemSet::new() .with_run_criteria(FixedTimestep::step(TIME_STEP as f64)) .with_system(kinematics.system().label("kinematics")) - // We need to check for collisions before handling movement + // We need to check for collisions before handling movement // to reduce the risk of the ball passing through objects .with_system(ball_collision.system().before("kinematics")), ) // Ordinary systems run every frame - // We need to handle input before we move our paddle, + // We need to handle input before we move our paddle, // to ensure that we're responding to the most recent frame's events, // avoiding input lag - // See https://github.com/bevyengine/bevy/blob/latest/examples/ecs/ecs_guide.rs + // See https://github.com/bevyengine/bevy/blob/latest/examples/ecs/ecs_guide.rs // for more information on system ordering - .add_system(paddle_input.system().before("bound_paddle").before("kinematics")) + .add_system( + paddle_input + .system() + .before("bound_paddle") + .before("kinematics"), + ) .add_system( bound_paddle .system() .label("bound_paddle") // This system must run after kinematics, or the velocity will be set to 0 - // before the paddle moves, causing it to be stuck to the wall + // before the paddle moves, causing it to be stuck to the wall .after("kinematics"), ) .add_system(update_scoreboard.system()) + // Exits the game when `KeyCode::Esc` is pressed + // This is a simple built-in system + .add_system(exit_on_esc_system.system()) .run(); } From d33564ef2a9c32f756fec2e921d9a76fafe74cfb Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Thu, 17 Jun 2021 18:34:07 -0400 Subject: [PATCH 46/47] Add delta_time scaling to paddle input --- examples/game/breakout.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/examples/game/breakout.rs b/examples/game/breakout.rs index a1782751cbe65..947f394e86718 100644 --- a/examples/game/breakout.rs +++ b/examples/game/breakout.rs @@ -21,7 +21,7 @@ mod config { pub const BACKGROUND_COLOR: Color = Color::rgb(0.9, 0.9, 0.9); pub const PADDLE_COLOR: Color = Color::rgb(0.5, 0.5, 1.0); - pub const PADDLE_SPEED: f32 = 500.0; + pub const PADDLE_SPEED: f32 = 20000.0; pub const PADDLE_SIZE: Vec2 = const_vec2!([120.0, 30.0]); pub const PADDLE_BOUND: f32 = 380.0; pub const PADDLE_STARTING_TRANSFORM: Transform = Transform { @@ -350,7 +350,11 @@ fn kinematics(mut query: Query<(&mut Transform, &Velocity)>) { } /// Turns left and right arrow key inputs to set paddle velocity -fn paddle_input(keyboard_input: Res>, mut query: Query<(&Paddle, &mut Velocity)>) { +fn paddle_input( + keyboard_input: Res>, + mut query: Query<(&Paddle, &mut Velocity)>, + time: Res