diff options
author | Henauxg <19689618+Henauxg@users.noreply.github.com> | 2022-11-03 16:57:55 +0100 |
---|---|---|
committer | Henauxg <19689618+Henauxg@users.noreply.github.com> | 2022-11-03 16:57:55 +0100 |
commit | a6f63d755caf51cd9866ba5aaf0c9fd3c0d56bde (patch) | |
tree | 8c9a9498ea877d6df02893caddf5717e57152c0b /examples | |
parent | 4103125aa0f3ad3368ccf97fbdae5eedbe6e7233 (diff) |
[breakout:example] Networked bricks spawn
Diffstat (limited to 'examples')
-rw-r--r-- | examples/breakout/breakout.rs | 5 | ||||
-rw-r--r-- | examples/breakout/client.rs | 125 | ||||
-rw-r--r-- | examples/breakout/protocol.rs | 7 | ||||
-rw-r--r-- | examples/breakout/server.rs | 97 |
4 files changed, 152 insertions, 82 deletions
diff --git a/examples/breakout/breakout.rs b/examples/breakout/breakout.rs index 1183dbb..96527c2 100644 --- a/examples/breakout/breakout.rs +++ b/examples/breakout/breakout.rs @@ -12,6 +12,8 @@ mod client; mod protocol; mod server; +const SERVER_PORT: u16 = 6000; + // Defines the amount of time that should elapse between each physics step. const TIME_STEP: f32 = 1.0 / 60.0; @@ -148,9 +150,6 @@ struct Collider; struct CollisionEvent; #[derive(Component)] -struct Brick; - -#[derive(Component)] struct Score; struct CollisionSound(Handle<AudioSource>); diff --git a/examples/breakout/client.rs b/examples/breakout/client.rs index a7b549b..6598ec4 100644 --- a/examples/breakout/client.rs +++ b/examples/breakout/client.rs @@ -20,10 +20,8 @@ use bevy_quinnet::{ use crate::{ protocol::{ClientMessage, PaddleInput, ServerMessage}, - Brick, Collider, CollisionEvent, CollisionSound, GameState, Score, Scoreboard, Velocity, - WallLocation, BALL_SIZE, BALL_SPEED, BOTTOM_WALL, BRICK_SIZE, GAP_BETWEEN_BRICKS, - GAP_BETWEEN_BRICKS_AND_CEILING, GAP_BETWEEN_BRICKS_AND_SIDES, GAP_BETWEEN_PADDLE_AND_BRICKS, - GAP_BETWEEN_PADDLE_AND_FLOOR, LEFT_WALL, PADDLE_SIZE, RIGHT_WALL, TIME_STEP, TOP_WALL, + Collider, CollisionEvent, CollisionSound, GameState, Score, Scoreboard, Velocity, WallLocation, + BALL_SIZE, BALL_SPEED, BRICK_SIZE, GAP_BETWEEN_BRICKS, PADDLE_SIZE, SERVER_PORT, TIME_STEP, }; const SCOREBOARD_FONT_SIZE: f32 = 40.0; @@ -62,6 +60,10 @@ pub(crate) struct Paddle; #[derive(Component)] pub(crate) struct Ball; +pub type BrickId = u64; +#[derive(Component)] +pub(crate) struct Brick(BrickId); + /// The buttons in the main menu. #[derive(Clone, Copy, Component)] pub(crate) enum MenuItem { @@ -82,7 +84,12 @@ struct WallBundle { pub(crate) fn start_connection(client: ResMut<Client>) { client .connect( - ClientConfigurationData::new("127.0.0.1".to_string(), 6000, "0.0.0.0".to_string(), 0), + ClientConfigurationData::new( + "127.0.0.1".to_string(), + SERVER_PORT, + "0.0.0.0".to_string(), + 0, + ), CertificateVerificationMode::SkipVerification, ) .unwrap(); @@ -128,6 +135,37 @@ fn spawn_ball(commands: &mut Commands, pos: &Vec3, direction: &Vec2) -> Entity { .id() } +pub(crate) fn spawn_bricks(commands: &mut Commands, offset: Vec2, rows: usize, columns: usize) { + let mut brick_id = 0; + for row in 0..rows { + for column in 0..columns { + let brick_position = Vec2::new( + offset.x + column as f32 * (BRICK_SIZE.x + GAP_BETWEEN_BRICKS), + offset.y + row as f32 * (BRICK_SIZE.y + GAP_BETWEEN_BRICKS), + ); + + // brick + commands + .spawn() + .insert(Brick(brick_id)) + .insert_bundle(SpriteBundle { + sprite: Sprite { + color: BRICK_COLOR, + ..default() + }, + transform: Transform { + translation: brick_position.extend(0.0), + scale: Vec3::new(BRICK_SIZE.x, BRICK_SIZE.y, 1.0), + ..default() + }, + ..default() + }) + .insert(Collider); + brick_id += 1; + } + } +} + pub(crate) fn handle_server_messages( mut commands: Commands, mut client: ResMut<Client>, @@ -136,6 +174,10 @@ pub(crate) fn handle_server_messages( mut game_state: ResMut<State<GameState>>, mut paddles: Query<&mut Transform, With<Paddle>>, mut balls: Query<(&mut Transform, &mut Velocity), (With<Ball>, Without<Paddle>)>, + mut bricks: Query< + (&mut Transform, &mut Velocity), + (With<Brick>, Without<Paddle>, Without<Ball>), + >, mut collision_events: EventWriter<CollisionEvent>, ) { while let Ok(Some(message)) = client.receive_message::<ServerMessage>() { @@ -159,6 +201,11 @@ pub(crate) fn handle_server_messages( let ball = spawn_ball(&mut commands, &position, &direction); entity_mapping.map.insert(entity, ball); } + ServerMessage::SpawnBricks { + offset, + rows, + columns, + } => spawn_bricks(&mut commands, offset, rows, columns), ServerMessage::StartGame {} => game_state.set(GameState::Running).unwrap(), ServerMessage::BrickDestroyed { client_id } => todo!(), ServerMessage::BallCollided { @@ -311,7 +358,6 @@ pub(crate) fn teardown_main_menu(mut commands: Commands, query: Query<Entity, Wi } } -// Add the game's entities to our world pub(crate) fn setup_breakout(mut commands: Commands, asset_server: Res<AssetServer>) { // Sound let ball_collision_sound = asset_server.load(COLLISION_SOUND_EFFECT); @@ -353,73 +399,6 @@ pub(crate) fn setup_breakout(mut commands: Commands, asset_server: Res<AssetServ commands.spawn_bundle(WallBundle::new(WallLocation::Right)); commands.spawn_bundle(WallBundle::new(WallLocation::Bottom)); commands.spawn_bundle(WallBundle::new(WallLocation::Top)); - - // Bricks - // Negative scales result in flipped sprites / meshes, - // which is definitely not what we want here - assert!(BRICK_SIZE.x > 0.0); - assert!(BRICK_SIZE.y > 0.0); - - let total_width_of_bricks = (RIGHT_WALL - LEFT_WALL) - 2. * GAP_BETWEEN_BRICKS_AND_SIDES; - let bottom_edge_of_bricks = - BOTTOM_WALL + GAP_BETWEEN_PADDLE_AND_FLOOR + GAP_BETWEEN_PADDLE_AND_BRICKS; - let available_height_for_bricks = TOP_WALL - - bottom_edge_of_bricks - - (GAP_BETWEEN_PADDLE_AND_FLOOR + GAP_BETWEEN_PADDLE_AND_BRICKS); - - assert!(total_width_of_bricks > 0.0); - assert!(available_height_for_bricks > 0.0); - - // Given the space available, compute how many rows and columns of bricks we can fit - let n_columns = (total_width_of_bricks / (BRICK_SIZE.x + GAP_BETWEEN_BRICKS)).floor() as usize; - let n_rows = - (available_height_for_bricks / (BRICK_SIZE.y + GAP_BETWEEN_BRICKS)).floor() as usize; - let height_occupied_by_bricks = - n_rows as f32 * (BRICK_SIZE.y + GAP_BETWEEN_BRICKS) - GAP_BETWEEN_BRICKS; - let n_vertical_gaps = n_columns - 1; - - // Because we need to round the number of columns, - // the space on the top and sides of the bricks only captures a lower bound, not an exact value - let center_of_bricks = (LEFT_WALL + RIGHT_WALL) / 2.0; - let left_edge_of_bricks = center_of_bricks - // Space taken up by the bricks - - (n_columns as f32 / 2.0 * BRICK_SIZE.x) - // Space taken up by the gaps - - n_vertical_gaps as f32 / 2.0 * GAP_BETWEEN_BRICKS; - - // In Bevy, the `translation` of an entity describes the center point, - // not its bottom-left corner - let offset_x = left_edge_of_bricks + BRICK_SIZE.x / 2.; - let offset_y = bottom_edge_of_bricks - + BRICK_SIZE.y / 2. - + (available_height_for_bricks - height_occupied_by_bricks) / 2.; // Offset so that both players are at an equal distance of the bricks - - for row in 0..n_rows { - for column in 0..n_columns { - let brick_position = Vec2::new( - offset_x + column as f32 * (BRICK_SIZE.x + GAP_BETWEEN_BRICKS), - offset_y + row as f32 * (BRICK_SIZE.y + GAP_BETWEEN_BRICKS), - ); - - // brick - commands - .spawn() - .insert(Brick) - .insert_bundle(SpriteBundle { - sprite: Sprite { - color: BRICK_COLOR, - ..default() - }, - transform: Transform { - translation: brick_position.extend(0.0), - scale: Vec3::new(BRICK_SIZE.x, BRICK_SIZE.y, 1.0), - ..default() - }, - ..default() - }) - .insert(Collider); - } - } } pub(crate) fn apply_velocity(mut query: Query<(&mut Transform, &Velocity), With<Ball>>) { diff --git a/examples/breakout/protocol.rs b/examples/breakout/protocol.rs index a42e0e4..4e2b309 100644 --- a/examples/breakout/protocol.rs +++ b/examples/breakout/protocol.rs @@ -32,7 +32,12 @@ pub(crate) enum ServerMessage { position: Vec3, direction: Vec2, }, - StartGame {}, + SpawnBricks { + offset: Vec2, + rows: usize, + columns: usize, + }, + StartGame, BrickDestroyed { client_id: ClientId, }, diff --git a/examples/breakout/server.rs b/examples/breakout/server.rs index 663cd00..fb430f4 100644 --- a/examples/breakout/server.rs +++ b/examples/breakout/server.rs @@ -15,9 +15,10 @@ use bevy_quinnet::{ use crate::{ protocol::{ClientMessage, PaddleInput, ServerMessage}, - Brick, Collider, Scoreboard, Velocity, BALL_SIZE, BALL_SPEED, BOTTOM_WALL, + Collider, Scoreboard, Velocity, BALL_SIZE, BALL_SPEED, BOTTOM_WALL, BRICK_SIZE, + GAP_BETWEEN_BRICKS, GAP_BETWEEN_BRICKS_AND_SIDES, GAP_BETWEEN_PADDLE_AND_BRICKS, GAP_BETWEEN_PADDLE_AND_FLOOR, LEFT_WALL, PADDLE_PADDING, PADDLE_SIZE, PADDLE_SPEED, RIGHT_WALL, - TIME_STEP, TOP_WALL, WALL_THICKNESS, + SERVER_PORT, TIME_STEP, TOP_WALL, WALL_THICKNESS, }; const GAP_BETWEEN_PADDLE_AND_BALL: f32 = 35.; @@ -58,13 +59,21 @@ pub(crate) struct Paddle { player_id: ClientId, } +pub type BrickId = u64; +#[derive(Component)] +pub(crate) struct Brick(BrickId); + #[derive(Component)] pub(crate) struct Ball; pub(crate) fn start_listening(mut server: ResMut<Server>) { server .start( - ServerConfigurationData::new("127.0.0.1".to_string(), 6000, "0.0.0.0".to_string()), + ServerConfigurationData::new( + "127.0.0.1".to_string(), + SERVER_PORT, + "0.0.0.0".to_string(), + ), CertificateRetrievalMode::GenerateSelfSigned, ) .unwrap(); @@ -219,7 +228,7 @@ pub(crate) fn apply_velocity(mut query: Query<(&mut Transform, &Velocity), With< } fn start_game(commands: &mut Commands, server: &mut ResMut<Server>, players: &ResMut<Players>) { - // Paddles + // Spawn paddles for (index, (client_id, _)) in players.map.iter().enumerate() { let paddle = spawn_paddle(commands, *client_id, &PADDLES_STARTING_POSITION[index]); server @@ -234,7 +243,7 @@ fn start_game(commands: &mut Commands, server: &mut ResMut<Server>, players: &Re .unwrap(); } - // Balls + // Spawn balls for (position, direction) in BALLS_STARTING_POSITION .iter() .zip(INITIAL_BALLS_DIRECTION.iter()) @@ -252,6 +261,84 @@ fn start_game(commands: &mut Commands, server: &mut ResMut<Server>, players: &Re .unwrap(); } + // Spawn bricks + // Negative scales result in flipped sprites / meshes, + // which is definitely not what we want here + assert!(BRICK_SIZE.x > 0.0); + assert!(BRICK_SIZE.y > 0.0); + + let total_width_of_bricks = (RIGHT_WALL - LEFT_WALL) - 2. * GAP_BETWEEN_BRICKS_AND_SIDES; + let bottom_edge_of_bricks = + BOTTOM_WALL + GAP_BETWEEN_PADDLE_AND_FLOOR + GAP_BETWEEN_PADDLE_AND_BRICKS; + let available_height_for_bricks = TOP_WALL + - bottom_edge_of_bricks + - (GAP_BETWEEN_PADDLE_AND_FLOOR + GAP_BETWEEN_PADDLE_AND_BRICKS); + + assert!(total_width_of_bricks > 0.0); + assert!(available_height_for_bricks > 0.0); + + // Given the space available, compute how many rows and columns of bricks we can fit + let n_columns = (total_width_of_bricks / (BRICK_SIZE.x + GAP_BETWEEN_BRICKS)).floor() as usize; + let n_rows = + (available_height_for_bricks / (BRICK_SIZE.y + GAP_BETWEEN_BRICKS)).floor() as usize; + let height_occupied_by_bricks = + n_rows as f32 * (BRICK_SIZE.y + GAP_BETWEEN_BRICKS) - GAP_BETWEEN_BRICKS; + let n_vertical_gaps = n_columns - 1; + + // Because we need to round the number of columns, + // the space on the top and sides of the bricks only captures a lower bound, not an exact value + let center_of_bricks = (LEFT_WALL + RIGHT_WALL) / 2.0; + let left_edge_of_bricks = center_of_bricks + // Space taken up by the bricks + - (n_columns as f32 / 2.0 * BRICK_SIZE.x) + // Space taken up by the gaps + - n_vertical_gaps as f32 / 2.0 * GAP_BETWEEN_BRICKS; + + // In Bevy, the `translation` of an entity describes the center point, + // not its bottom-left corner + let offset_x = left_edge_of_bricks + BRICK_SIZE.x / 2.; + let offset_y = bottom_edge_of_bricks + + BRICK_SIZE.y / 2. + + (available_height_for_bricks - height_occupied_by_bricks) / 2.; // Offset so that both players are at an equal distance of the bricks + + let mut brick_id = 0; + for row in 0..n_rows { + for column in 0..n_columns { + let brick_position = Vec2::new( + offset_x + column as f32 * (BRICK_SIZE.x + GAP_BETWEEN_BRICKS), + offset_y + row as f32 * (BRICK_SIZE.y + GAP_BETWEEN_BRICKS), + ); + + // brick + commands + .spawn() + .insert(Brick(brick_id)) + .insert_bundle(TransformBundle { + local: Transform { + translation: brick_position.extend(0.0), + scale: Vec3::new(BRICK_SIZE.x, BRICK_SIZE.y, 1.0), + ..default() + }, + ..default() + }) + .insert(Collider); + brick_id += 1; + } + } + server + .send_group_message( + players.map.keys().into_iter(), + ServerMessage::SpawnBricks { + offset: Vec2 { + x: offset_x, + y: offset_y, + }, + rows: n_rows, + columns: n_columns, + }, + ) + .unwrap(); + server .send_group_message(players.map.keys().into_iter(), ServerMessage::StartGame {}) .unwrap(); |