ball movement and reaction to paddle added

This commit is contained in:
brad 2019-04-22 23:31:35 -05:00
parent 87c8fe2b1b
commit 8d74a3ca40
5 changed files with 138 additions and 2 deletions

View File

@ -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)?;

View File

@ -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::<Paddle>();
world.register::<Ball>();
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<Self>;
}
pub struct Ball {
pub velocity: [f32; 2],
pub radius: f32,
}
impl Component for Ball {
type Storage = DenseVecStorage<Self>;
}
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

66
src/systems/bounce.rs Normal file
View File

@ -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
}

View File

@ -1,3 +1,7 @@
mod paddle;
mod move_balls;
mod bounce;
pub use self::paddle::PaddleSystem;
pub use self::bounce::BounceSystem;
pub use self::move_balls::MoveBallsSystem;

25
src/systems/move_balls.rs Normal file
View File

@ -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());
}
}
}