Commit: 0944c13
Parent: 0010327

Center the camera on a virtual window

Mårten Åsberg committed on 2026-09-28 at 17:59
src/camera_controller.rs +13 -31
diff --git a/src/camera_controller.rs b/src/camera_controller.rs
index ad0aa97..5594d48 100644
@@ -3,7 +3,6 @@ use bevy::{
camera::Camera,
ecs::{
query::With,
resource::Resource,
schedule::IntoScheduleConfigs,
system::{If, Res, Single},
},
@@ -16,45 +15,28 @@ use bevy::{
transform::components::Transform,
};
pub struct CameraControllerPlugin {
origin: Vec3,
target: Vec3,
}
use crate::virtual_window::VirtualWindow;
impl CameraControllerPlugin {
pub fn new(origin: Vec3, target: Vec3) -> Self {
Self { origin, target }
}
}
pub struct CameraControllerPlugin;
impl Plugin for CameraControllerPlugin {
fn build(&self, app: &mut App) {
app.insert_resource(CameraSettings {
origin: self.origin,
target: self.target,
})
.add_systems(
app.add_systems(
Update,
(move_camera_with_keyboard, move_camera_with_controller).chain(),
);
}
}
#[derive(Resource)]
struct CameraSettings {
origin: Vec3,
target: Vec3,
}
fn move_camera_with_controller(
settings: Res<CameraSettings>,
mut camera: Single<&mut Transform, With<Camera>>,
camera: Single<(&mut Transform, &VirtualWindow), With<Camera>>,
gamepad: If<Single<&Gamepad>>,
) {
let mut origin = settings.origin;
let (mut camera, window) = camera.into_inner();
let mut origin = window.center + window.normal.as_vec3();
camera.translation = origin;
camera.look_at(settings.target, Vec3::Y);
camera.look_at(window.center, Vec3::Y);
origin += camera.local_z().as_vec3()
* (gamepad
@@ -65,18 +47,18 @@ fn move_camera_with_controller(
origin += camera.local_y().as_vec3() * gamepad.get(GamepadAxis::LeftStickY).unwrap_or_default();
camera.translation = origin;
camera.look_at(settings.target, Vec3::Y);
camera.look_at(window.center, Vec3::Y);
}
fn move_camera_with_keyboard(
settings: Res<CameraSettings>,
mut camera: Single<&mut Transform, With<Camera>>,
camera: Single<(&mut Transform, &VirtualWindow), With<Camera>>,
input: Res<ButtonInput<KeyCode>>,
) {
let mut origin = settings.origin;
let (mut camera, window) = camera.into_inner();
let mut origin = window.center + window.normal.as_vec3();
camera.translation = origin;
camera.look_at(settings.target, Vec3::Y);
camera.look_at(window.center, Vec3::Y);
if input.pressed(KeyCode::KeyS) {
origin += camera.local_z().as_vec3();
@@ -98,5 +80,5 @@ fn move_camera_with_keyboard(
}
camera.translation = origin;
camera.look_at(settings.target, Vec3::Y);
camera.look_at(window.center, Vec3::Y);
}
src/main.rs +8 -6
diff --git a/src/main.rs b/src/main.rs
index 81fea26..2230e99 100644
@@ -1,4 +1,5 @@
mod camera_controller;
mod virtual_window;
use std::f32::consts::FRAC_PI_4;
@@ -15,20 +16,17 @@ use bevy::{
image::Image,
light::{DirectionalLight, DirectionalLightShadowMap, Skybox},
log::error,
math::Vec3,
math::{Dir3, Vec3},
render::render_resource::{TextureViewDescriptor, TextureViewDimension},
transform::components::Transform,
world_serialization::WorldAssetRoot,
};
use crate::camera_controller::CameraControllerPlugin;
use crate::{camera_controller::CameraControllerPlugin, virtual_window::VirtualWindow};
fn main() {
App::new()
.add_plugins((
DefaultPlugins,
CameraControllerPlugin::new(Vec3::new(-7.0, 3.3, 14.0), Vec3::new(-6.5, 3.1, 13.1)),
))
.add_plugins((DefaultPlugins, CameraControllerPlugin))
.add_systems(Startup, setup)
.add_systems(Update, load_skybox)
.run();
@@ -39,6 +37,10 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
commands.insert_resource(SkyboxHandle(handle.clone(), false));
commands.spawn((
Camera3d::default(),
VirtualWindow {
center: Vec3::new(-6.5, 3.1, 13.1),
normal: Dir3::from_xyz_unchecked(-0.4767313, 0.19069253, 0.8581163),
},
Projection::from(PerspectiveProjection {
fov: FRAC_PI_4,
..Default::default()
src/virtual_window.rs +10 -0
diff --git a/src/virtual_window.rs b/src/virtual_window.rs
new file mode 100644
index 0000000..6c67d5f
@@ -0,0 +1,10 @@
use bevy::{
ecs::component::Component,
math::{Dir3, Vec3},
};
#[derive(Component)]
pub struct VirtualWindow {
pub center: Vec3,
pub normal: Dir3,
}