From 5a74ff6f5e3dda0c71e923d8e84ddcae8366380c Mon Sep 17 00:00:00 2001 From: Jerome Humbert Date: Tue, 20 Feb 2024 22:15:44 +0000 Subject: [PATCH] Update `split_screen` example with 4 cameras (#12010) # Objective Improve `split_screen` example to use 4 cameras. This serves as a visual regression test for #12006. ## Solution With the fix of #11968: ![image](https://github.com/bevyengine/bevy/assets/6532395/57e9e013-7d23-429f-98ac-c6542d6b4bea) Without (current `main`): ![image](https://github.com/bevyengine/bevy/assets/6532395/0b2a88a4-97f8-408d-8a0e-ce917efc668d) --- examples/3d/split_screen.rs | 162 +++++++++++++++--------------------- 1 file changed, 67 insertions(+), 95 deletions(-) diff --git a/examples/3d/split_screen.rs b/examples/3d/split_screen.rs index 6cc63ad7af0ed..5fe2900e206a1 100644 --- a/examples/3d/split_screen.rs +++ b/examples/3d/split_screen.rs @@ -50,81 +50,65 @@ fn setup( ..default() }); - // Left Camera - let left_camera = commands - .spawn(( - Camera3dBundle { - transform: Transform::from_xyz(0.0, 200.0, -100.0).looking_at(Vec3::ZERO, Vec3::Y), - ..default() - }, - LeftCamera, - )) - .id(); - - // Right Camera - let right_camera = commands - .spawn(( - Camera3dBundle { - transform: Transform::from_xyz(100.0, 100., 150.0).looking_at(Vec3::ZERO, Vec3::Y), - camera: Camera { - // Renders the right camera after the left camera, which has a default priority of 0 - order: 1, - // don't clear on the second camera because the first camera already cleared the window - clear_color: ClearColorConfig::None, - ..default() - }, - ..default() - }, - RightCamera, - )) - .id(); - - // Set up UI - commands - .spawn(( - TargetCamera(left_camera), - NodeBundle { - style: Style { - width: Val::Percent(100.), - height: Val::Percent(100.), - ..default() - }, - ..default() - }, - )) - .with_children(|parent| { - parent.spawn(TextBundle::from_section( - "Left", - TextStyle { - font_size: 20., + // Cameras and their dedicated UI + for (index, (camera_name, camera_pos)) in [ + ("Player 1", Vec3::new(0.0, 200.0, -150.0)), + ("Player 2", Vec3::new(150.0, 150., 50.0)), + ("Player 3", Vec3::new(100.0, 150., -150.0)), + ("Player 4", Vec3::new(-100.0, 80., 150.0)), + ] + .iter() + .enumerate() + { + let camera = commands + .spawn(( + Camera3dBundle { + transform: Transform::from_translation(*camera_pos) + .looking_at(Vec3::ZERO, Vec3::Y), + camera: Camera { + // Renders cameras with different priorities to prevent ambiguities + order: index as isize, + // Don't clear after the first camera because the first camera already cleared the entire window + clear_color: if index > 0 { + ClearColorConfig::None + } else { + ClearColorConfig::default() + }, + ..default() + }, ..default() }, - )); - buttons_panel(parent); - }); - - commands - .spawn(( - TargetCamera(right_camera), - NodeBundle { - style: Style { - width: Val::Percent(100.), - height: Val::Percent(100.), - ..default() + CameraPosition { + pos: UVec2::new((index % 2) as u32, (index / 2) as u32), }, - ..default() - }, - )) - .with_children(|parent| { - parent.spawn(TextBundle::from_section( - "Right", - TextStyle { - font_size: 20., + )) + .id(); + + // Set up UI + commands + .spawn(( + TargetCamera(camera), + NodeBundle { + style: Style { + width: Val::Percent(100.), + height: Val::Percent(100.), + padding: UiRect::all(Val::Px(20.)), + ..default() + }, ..default() }, - )); - buttons_panel(parent); - }); + )) + .with_children(|parent| { + parent.spawn(TextBundle::from_section( + *camera_name, + TextStyle { + font_size: 20., + ..default() + }, + )); + buttons_panel(parent); + }); + } fn buttons_panel(parent: &mut ChildBuilder) { parent @@ -179,10 +163,9 @@ fn setup( } #[derive(Component)] -struct LeftCamera; - -#[derive(Component)] -struct RightCamera; +struct CameraPosition { + pos: UVec2, +} #[derive(Component)] struct RotateCamera(Direction); @@ -195,33 +178,22 @@ enum Direction { fn set_camera_viewports( windows: Query<&Window>, mut resize_events: EventReader, - mut left_camera: Query<&mut Camera, (With, Without)>, - mut right_camera: Query<&mut Camera, With>, + mut query: Query<(&CameraPosition, &mut Camera)>, ) { // We need to dynamically resize the camera's viewports whenever the window size changes // so then each camera always takes up half the screen. // A resize_event is sent when the window is first created, allowing us to reuse this system for initial setup. for resize_event in resize_events.read() { let window = windows.get(resize_event.window).unwrap(); - let mut left_camera = left_camera.single_mut(); - left_camera.viewport = Some(Viewport { - physical_position: UVec2::new(0, 0), - physical_size: UVec2::new( - window.resolution.physical_width() / 2, - window.resolution.physical_height(), - ), - ..default() - }); - - let mut right_camera = right_camera.single_mut(); - right_camera.viewport = Some(Viewport { - physical_position: UVec2::new(window.resolution.physical_width() / 2, 0), - physical_size: UVec2::new( - window.resolution.physical_width() / 2, - window.resolution.physical_height(), - ), - ..default() - }); + let size = UVec2::new(window.physical_width(), window.physical_height()) / 2; + + for (camera_position, mut camera) in &mut query { + camera.viewport = Some(Viewport { + physical_position: camera_position.pos * size, + physical_size: size, + ..default() + }); + } } }