Skip to content

Instantly share code, notes, and snippets.

@Bendzae
Created September 7, 2025 10:13
Show Gist options
  • Select an option

  • Save Bendzae/e4a1fd1bcf1cee2e24639dc6e2ec0795 to your computer and use it in GitHub Desktop.

Select an option

Save Bendzae/e4a1fd1bcf1cee2e24639dc6e2ec0795 to your computer and use it in GitHub Desktop.
use crate::{debug_utils::egui_focus::EguiWantsFocus, input::ZoomEvent, player::LocalPlayerTag};
use bevy::{
core_pipeline::dof::DepthOfField,
math::NormedVectorSpace,
prelude::*,
};
use bevy_inspector_egui::egui::lerp;
use rand::Rng;
use std::time::Duration;
#[derive(Component, Default)]
struct ZoomData {
pub(crate) initial_offset: Vec3,
pub(crate) zoom: f32,
}
#[derive(Component, Default)]
pub struct CameraOffset {
pub(crate) offset: Vec3,
}
fn init_camera_system(
mut q_camera: Query<(Entity, &Transform, &mut CameraOffset), Added<CameraOffset>>,
mut commands: Commands,
) {
if let Ok((e, transform, mut camera_offset)) = q_camera.single_mut() {
camera_offset.offset = transform.translation;
commands.entity(e).insert(ZoomData {
initial_offset: camera_offset.offset,
zoom: 1.0,
});
}
}
fn camera_follow_system(
mut q_camera: Query<
(
&mut Transform,
Option<&mut DepthOfField>,
&CameraOffset,
),
Without<LocalPlayerTag>,
>,
mut q_player: Query<&Transform, With<LocalPlayerTag>>,
time: Res<Time>,
) {
if let Ok((mut transform, dof_settings, camera_offset)) = q_camera.single_mut() {
if let Ok(p_transform) = q_player.single_mut() {
let cam_speed = 2.0;
let target_translation =
camera_offset.offset + p_transform.translation * Vec3::new(1., 0., 1.);
transform.translation = lerp(
transform.translation..=target_translation,
time.delta_secs() * cam_speed,
);
if let Some(mut dof_settings) = dof_settings {
dof_settings.focal_distance = transform
.translation
.distance(p_transform.translation + Vec3::Y * 2.0);
}
}
}
}
fn camera_zoom_system(
egui_wants_focus: Res<EguiWantsFocus>,
mut events: EventReader<ZoomEvent>,
mut q_camera: Query<(&mut ZoomData, &mut CameraOffset)>,
) {
if egui_wants_focus.prev || egui_wants_focus.curr {
return;
}
let zoom_speed = 0.04;
let zoom_min = 0.2;
let zoom_max = 2.5;
for ev in events.read() {
if let Ok((mut zoom_data, mut offset)) = q_camera.single_mut() {
zoom_data.zoom = (zoom_data.zoom + ev.0 * zoom_speed).clamp(zoom_min, zoom_max);
offset.offset = zoom_data.zoom * zoom_data.initial_offset;
}
}
}
#[derive(Resource)]
pub struct ScreenShake {
timer: Timer,
strength: f32,
pub orginal_orientation: Quat,
pub random_offset: f32,
}
impl ScreenShake {
pub fn new(duration: f32, strength: f32) -> ScreenShake {
Self {
timer: Timer::from_seconds(duration, TimerMode::Once),
strength,
orginal_orientation: Quat::IDENTITY,
random_offset: rand::thread_rng().gen_range(0.0..10.0),
}
}
}
fn camera_shake_system(
mut q_camera: Query<(&mut Transform, &CameraOffset)>,
mut shake: ResMut<ScreenShake>,
time: Res<Time<Real>>,
mut commands: Commands,
) {
if let Ok((mut transform, _camera_offset)) = q_camera.single_mut() {
if shake.timer.elapsed() == Duration::ZERO {
shake.orginal_orientation = transform.rotation;
}
if shake.timer.just_finished() {
transform.rotation = shake.orginal_orientation;
commands.remove_resource::<ScreenShake>();
return;
}
let freq = 1.0 + shake.timer.fraction() * 3.0;
let p = shake.timer.fraction() * std::f32::consts::PI * freq + shake.random_offset;
// let p = shake.timer.percent() * std::f32::consts::PI * freq;
let shake_value = p.sin() * (shake.strength / freq);
transform.rotation =
shake.orginal_orientation * Quat::from_axis_angle(Vec3::Y, shake_value);
shake.timer.tick(time.delta());
}
}
pub struct ArpgCameraPlugin;
impl Plugin for ArpgCameraPlugin {
fn build(&self, app: &mut App) {
app.add_systems(
Update,
(
init_camera_system,
camera_zoom_system,
camera_follow_system,
camera_shake_system.run_if(resource_exists::<ScreenShake>),
),
);
}
fn name(&self) -> &str {
"CameraPlugin"
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment