diff --git a/font/square.ttf b/font/square.ttf new file mode 100644 index 0000000..9f1867e Binary files /dev/null and b/font/square.ttf differ diff --git a/resources/display_config.ron b/resources/display_config.ron index 3aa60f1..7999694 100644 --- a/resources/display_config.ron +++ b/resources/display_config.ron @@ -1,6 +1,6 @@ ( title: "Pong!", - dimensions: Some((875, 500)), + dimensions: Some((500, 500)), max_dimensions: None, min_dimensions: None, fullscreen: false, diff --git a/src/main.rs b/src/main.rs index 208dff7..fd5c926 100644 --- a/src/main.rs +++ b/src/main.rs @@ -9,6 +9,9 @@ use amethyst::renderer::{DisplayConfig, DrawFlat2D, Event, Pipeline, use amethyst::utils::application_root_dir; use amethyst::core::transform::TransformBundle; use amethyst::input::InputBundle; +use amethyst::{ + ui::{DrawUi, UiBundle}, +}; use crate::pong::Pong; fn main() -> amethyst::Result<()> { @@ -20,7 +23,8 @@ fn main() -> amethyst::Result<()> { .with_stage( Stage::with_backbuffer() .clear_target([0.00196, 0.23726, 0.11765, 1.0], 1.0) - .with_pass(DrawFlat2D::new()), + .with_pass(DrawFlat2D::new()) + .with_pass(DrawUi::new()), ); let binding_path = format!( @@ -35,9 +39,11 @@ fn main() -> amethyst::Result<()> { .with_bundle(RenderBundle::new(pipe, Some(config)).with_sprite_sheet_processor())? .with_bundle(TransformBundle::new())? .with_bundle(input_bundle)? + .with_bundle(UiBundle::::new())? .with(systems::PaddleSystem, "paddle_system", &["input_system"]) .with(systems::MoveBallsSystem, "ball_system", &[]) - .with(systems::BounceSystem, "collision_system", &["paddle_system", "ball_system"],); + .with(systems::BounceSystem, "collision_system", &["paddle_system", "ball_system"],) + .with(systems::WinnerSystem, "winner_system", &["ball_system"]);; let mut game = Application::new("./", Pong, game_data)?; diff --git a/src/pong.rs b/src/pong.rs index 1a8fcd1..d325f94 100644 --- a/src/pong.rs +++ b/src/pong.rs @@ -1,12 +1,13 @@ use amethyst::assets::{AssetStorage, Loader}; use amethyst::core::transform::Transform; -use amethyst::ecs::prelude::{Component, DenseVecStorage}; +use amethyst::ecs::prelude::{Component, DenseVecStorage, Entity}; use amethyst::prelude::*; use amethyst::renderer::{ Camera, PngFormat, Projection, SpriteRender, SpriteSheet, Flipped, SpriteSheetFormat, SpriteSheetHandle, Texture, TextureMetadata, }; +use amethyst::ui::{Anchor, TtfFormat, UiText, UiTransform}; pub const ARENA_HEIGHT: f32 = 100.0; pub const ARENA_WIDTH: f32 = 100.0; @@ -30,6 +31,7 @@ impl SimpleState for Pong { initialise_ball(world, sprite_sheet_handle.clone()); initialise_paddles(world, sprite_sheet_handle); + initialise_scoreboard(world); initialise_camera(world); } } @@ -69,6 +71,19 @@ impl Component for Ball { type Storage = DenseVecStorage; } +/// ScoreBoard contains the actual score data +#[derive(Default)] +pub struct ScoreBoard { + pub score_left: i32, + pub score_right: i32, +} + +/// ScoreText contains the ui text components that display the score +pub struct ScoreText { + pub p1_score: Entity, + pub p2_score: Entity, +} + fn initialise_camera(world: &mut World) { let mut transform = Transform::default(); transform.set_z(1.0); @@ -140,6 +155,47 @@ fn initialise_ball(world: &mut World, sprite_sheet_handle: SpriteSheetHandle) { .build(); } +/// Initialises a ui scoreboard +fn initialise_scoreboard(world: &mut World) { + let font = world.read_resource::().load( + "font/square.ttf", + TtfFormat, + Default::default(), + (), + &world.read_resource(), + ); + let p1_transform = UiTransform::new( + "P1".to_string(), Anchor::TopMiddle, + -50., -50., 1., 200., 50., 0, + ); + let p2_transform = UiTransform::new( + "P2".to_string(), Anchor::TopMiddle, + 50., -50., 1., 200., 50., 0, + ); + + let p1_score = world + .create_entity() + .with(p1_transform) + .with(UiText::new( + font.clone(), + "0".to_string(), + [1., 1., 1., 1.], + 50., + )).build(); + + let p2_score = world + .create_entity() + .with(p2_transform) + .with(UiText::new( + font.clone(), + "0".to_string(), + [1., 1., 1., 1.], + 50., + )).build(); + + world.add_resource(ScoreText { p1_score, p2_score }); +} + 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/mod.rs b/src/systems/mod.rs index 9828cbb..43d61e2 100644 --- a/src/systems/mod.rs +++ b/src/systems/mod.rs @@ -1,7 +1,9 @@ mod paddle; mod move_balls; mod bounce; +mod winner; +pub use self::winner::WinnerSystem; 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/paddle.rs b/src/systems/paddle.rs index 76e92c3..c659bad 100644 --- a/src/systems/paddle.rs +++ b/src/systems/paddle.rs @@ -21,7 +21,7 @@ impl<'s> System<'s> for PaddleSystem { Side::Right => input.axis_value("right_paddle"), }; if let Some(mv_amount) = movement { - let scaled_amount = 1.2 * mv_amount as f32; + let scaled_amount = 2.0 * mv_amount as f32; let paddle_y = transform.translation().y; transform.set_y( (paddle_y + scaled_amount) diff --git a/src/systems/winner.rs b/src/systems/winner.rs new file mode 100644 index 0000000..bb9352a --- /dev/null +++ b/src/systems/winner.rs @@ -0,0 +1,60 @@ +use amethyst::{ + core::transform::Transform, + ecs::prelude::{Join, ReadExpect, System, Write, WriteStorage}, + ui::UiText, +}; + +use crate::pong::{Ball, ScoreBoard, ScoreText, ARENA_WIDTH}; + +pub struct WinnerSystem; + +impl<'s> System<'s> for WinnerSystem { + type SystemData = ( + WriteStorage<'s, Ball>, + WriteStorage<'s, Transform>, + WriteStorage<'s, UiText>, + Write<'s, ScoreBoard>, + ReadExpect<'s, ScoreText>, + ); + + fn run(&mut self, ( + mut balls, + mut locals, + mut ui_text, + mut scores, + score_text + ): Self::SystemData) { + for (ball, transform) in (&mut balls, &mut locals).join() { + let ball_x = transform.translation().x; + + let did_hit = if ball_x <= ball.radius { + // Right player scored on the left side. + scores.score_right = (scores.score_right + 1) + .min(999); + if let Some(text) = ui_text.get_mut(score_text.p2_score) { + text.text = scores.score_right.to_string(); + } + true + } else if ball_x >= ARENA_WIDTH - ball.radius { + // Left player scored on the right side. + scores.score_left = (scores.score_left + 1) + .min(999); + if let Some(text) = ui_text.get_mut(score_text.p1_score) { + text.text = scores.score_left.to_string(); + } + true + } else { + false + }; + + if did_hit { + ball.velocity[0] = -ball.velocity[0]; // Reverse Direction + transform.set_x(ARENA_WIDTH / 2.0); // Reset Position + println!( + "Score: | {:^3} | {:^3} |", + scores.score_left, scores.score_right + ); + } + } + } +} \ No newline at end of file