From 8d74a3ca408a4960ede11ea03508833002b0e005 Mon Sep 17 00:00:00 2001 From: brad Date: Mon, 22 Apr 2019 23:31:35 -0500 Subject: [PATCH] ball movement and reaction to paddle added --- src/main.rs | 4 ++- src/pong.rs | 39 +++++++++++++++++++++++ src/systems/bounce.rs | 66 +++++++++++++++++++++++++++++++++++++++ src/systems/mod.rs | 6 +++- src/systems/move_balls.rs | 25 +++++++++++++++ 5 files changed, 138 insertions(+), 2 deletions(-) create mode 100644 src/systems/bounce.rs create mode 100644 src/systems/move_balls.rs diff --git a/src/main.rs b/src/main.rs index f0fb88b..208dff7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -35,7 +35,9 @@ fn main() -> amethyst::Result<()> { .with_bundle(RenderBundle::new(pipe, Some(config)).with_sprite_sheet_processor())? .with_bundle(TransformBundle::new())? .with_bundle(input_bundle)? - .with(systems::PaddleSystem, "paddle_system", &["input_system"]); + .with(systems::PaddleSystem, "paddle_system", &["input_system"]) + .with(systems::MoveBallsSystem, "ball_system", &[]) + .with(systems::BounceSystem, "collision_system", &["paddle_system", "ball_system"],); let mut game = Application::new("./", Pong, game_data)?; diff --git a/src/pong.rs b/src/pong.rs index f7198c6..1a8fcd1 100644 --- a/src/pong.rs +++ b/src/pong.rs @@ -11,6 +11,9 @@ use amethyst::renderer::{ pub const ARENA_HEIGHT: f32 = 100.0; pub const ARENA_WIDTH: f32 = 100.0; pub const PADDLE_HEIGHT: f32 = 16.0; +pub const BALL_VELOCITY_X: f32 = 65.0; +pub const BALL_VELOCITY_Y: f32 = 40.0; +pub const BALL_RADIUS: f32 = 2.0; const PADDLE_WIDTH: f32 = 4.0; @@ -22,6 +25,10 @@ impl SimpleState for Pong { let sprite_sheet_handle = load_sprite_sheet(world); //world.register::(); + world.register::(); + + + initialise_ball(world, sprite_sheet_handle.clone()); initialise_paddles(world, sprite_sheet_handle); initialise_camera(world); } @@ -53,6 +60,15 @@ impl Component for Paddle { type Storage = DenseVecStorage; } +pub struct Ball { + pub velocity: [f32; 2], + pub radius: f32, +} + +impl Component for Ball { + type Storage = DenseVecStorage; +} + fn initialise_camera(world: &mut World) { let mut transform = Transform::default(); transform.set_z(1.0); @@ -101,6 +117,29 @@ fn initialise_paddles(world: &mut World, sprite_sheet: SpriteSheetHandle){ .build(); } +/// Initialises one ball in the middle-ish of the arena. +fn initialise_ball(world: &mut World, sprite_sheet_handle: SpriteSheetHandle) { + // Create the translation. + let mut local_transform = Transform::default(); + local_transform.set_xyz(ARENA_WIDTH / 2.0, ARENA_HEIGHT / 2.0, 0.0); + + // Assign the sprite for the ball + let sprite_render = SpriteRender { + sprite_sheet: sprite_sheet_handle, + sprite_number: 1, // ball is the second sprite on the sprite sheet + }; + + world + .create_entity() + .with(sprite_render) + .with(Ball { + radius: BALL_RADIUS, + velocity: [BALL_VELOCITY_X, BALL_VELOCITY_Y], + }) + .with(local_transform) + .build(); +} + fn load_sprite_sheet(world: &mut World) -> SpriteSheetHandle { // Load the sprite sheet necessary to render the graphics. // The texture is the pixel data diff --git a/src/systems/bounce.rs b/src/systems/bounce.rs new file mode 100644 index 0000000..6a52bde --- /dev/null +++ b/src/systems/bounce.rs @@ -0,0 +1,66 @@ +use amethyst::{ + core::transform::Transform, + ecs::prelude::{Join, ReadStorage, System, WriteStorage}, +}; + +use crate::pong::{Ball, Side, Paddle, ARENA_HEIGHT}; + +pub struct BounceSystem; + +impl<'s> System<'s> for BounceSystem { + type SystemData = ( + WriteStorage<'s, Ball>, + ReadStorage<'s, Paddle>, + ReadStorage<'s, Transform>, + ); + + fn run(&mut self, (mut balls, paddles, transforms): Self::SystemData) { + // Check whether a ball collided, and bounce off accordingly. + // + // We also check for the velocity of the ball every time, to prevent multiple collisions + // from occurring. + for (ball, transform) in (&mut balls, &transforms).join() { + let ball_x = transform.translation().x; + let ball_y = transform.translation().y; + + // Bounce at the top or the bottom of the arena. + if ball_y >= ARENA_HEIGHT - ball.radius && ball.velocity[1] > 0.0 { + ball.velocity[1] = -ball.velocity[1]; + } else if ball_y <= ball.radius && ball.velocity[1] < 0.0 { + ball.velocity[1] = -ball.velocity[1]; + } + + // Bounce at the paddles. + for (paddle, paddle_transform) in (&paddles, &transforms).join() { + let paddle_x = paddle_transform.translation().x - paddle.width * 0.5; + let paddle_y = paddle_transform.translation().y - paddle.height * 0.5; + + // To determine whether the ball has collided with a paddle, we create a larger + // rectangle around the current one, by subtracting the ball radius from the + // lowest coordinates, and adding the ball radius to the highest ones. The ball + // is then within the paddle if its centre is within the larger wrapper + // rectangle. + if point_in_rect( + ball_x, + ball_y, + paddle_x - ball.radius, + paddle_y - ball.radius, + paddle_x + paddle.width + ball.radius, + paddle_y + paddle.height + ball.radius, + ) { + if paddle.side == Side::Left && ball.velocity[0] < 0.0 { + ball.velocity[0] = -ball.velocity[0]; + } else if paddle.side == Side::Right && ball.velocity[0] > 0.0 { + ball.velocity[0] = -ball.velocity[0]; + } + } + } + } + } +} + +// A point is in a box when its coordinates are smaller or equal than the top +// right and larger or equal than the bottom left. +fn point_in_rect(x: f32, y: f32, left: f32, bottom: f32, right: f32, top: f32) -> bool { + x >= left && x <= right && y >= bottom && y <= top +} \ No newline at end of file diff --git a/src/systems/mod.rs b/src/systems/mod.rs index 725bb82..9828cbb 100644 --- a/src/systems/mod.rs +++ b/src/systems/mod.rs @@ -1,3 +1,7 @@ mod paddle; +mod move_balls; +mod bounce; -pub use self::paddle::PaddleSystem; \ No newline at end of file +pub use self::paddle::PaddleSystem; +pub use self::bounce::BounceSystem; +pub use self::move_balls::MoveBallsSystem; \ No newline at end of file diff --git a/src/systems/move_balls.rs b/src/systems/move_balls.rs new file mode 100644 index 0000000..a2979d8 --- /dev/null +++ b/src/systems/move_balls.rs @@ -0,0 +1,25 @@ +use amethyst::{ + core::timing::Time, + core::transform::Transform, + ecs::prelude::{Join, Read, ReadStorage, System, WriteStorage}, +}; + +use crate::pong::Ball; + +pub struct MoveBallsSystem; + +impl<'s> System<'s> for MoveBallsSystem { + type SystemData = ( + ReadStorage<'s, Ball>, + WriteStorage<'s, Transform>, + Read<'s, Time>, + ); + + fn run(&mut self, (balls, mut locals, time): Self::SystemData) { + // Move every ball according to its speed, and the time passed. + for (ball, local) in (&balls, &mut locals).join() { + local.translate_x(ball.velocity[0] * time.delta_seconds()); + local.translate_y(ball.velocity[1] * time.delta_seconds()); + } + } +}