From ad4c3ada928902fe4ad19867d0d3106ef32c8fef Mon Sep 17 00:00:00 2001 From: Leon Liu Date: Thu, 14 Aug 2025 12:41:56 +0900 Subject: [PATCH] update --- src/main.rs | 137 +++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 125 insertions(+), 12 deletions(-) diff --git a/src/main.rs b/src/main.rs index a2097fd..2b9882f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,12 +1,15 @@ use bevy::{ - core_pipeline::{bloom::Bloom, tonemapping::Tonemapping}, + core_pipeline::tonemapping::Tonemapping, input::mouse::{MouseScrollUnit, MouseWheel}, math::DVec3, prelude::*, - window::{WindowMode, WindowResolution}, + window::WindowResolution, }; -use bevy_inspector_egui::{bevy_egui::EguiPlugin, quick::WorldInspectorPlugin}; -use iyes_perf_ui::{PerfUiPlugin, prelude::PerfUiAllEntries}; +use bevy_inspector_egui::{ + bevy_egui::{EguiContextSettings, EguiPlugin}, + quick::WorldInspectorPlugin, +}; +use iyes_perf_ui::{PerfUiPlugin, prelude::*}; // Scaling factor to convert AU to game units // Neptune is approximately 30.1 astronomical units (AU) from the Sun @@ -76,6 +79,14 @@ struct CameraFollow { distance: f32, // Current zoom distance from target } +// Resource to manage UI scaling across different resolutions +#[derive(Resource)] +struct UiScale { + scale_factor: f32, + base_font_size: f32, + current_font_size: f32, +} + pub struct SolarRenderingPlugin; impl Plugin for SolarRenderingPlugin { @@ -84,6 +95,11 @@ impl Plugin for SolarRenderingPlugin { target: None, distance: 10.0, }) + .insert_resource(UiScale { + scale_factor: 1.0, + base_font_size: 16.0, + current_font_size: 16.0, + }) .add_systems(Startup, (setup_rendering, setup_ui)) .add_systems( FixedPostUpdate, @@ -95,7 +111,16 @@ impl Plugin for SolarRenderingPlugin { ), ) .add_systems(PostUpdate, update_label_positions) - .add_systems(Update, (handle_label_clicks, handle_scroll_zoom)); + .add_systems( + Update, + ( + handle_label_clicks, + handle_scroll_zoom, + handle_speed_controls, + update_ui_scale, + update_label_font_sizes.after(update_ui_scale), + ), + ); } } @@ -206,6 +231,7 @@ fn sync_name_labels( mut commands: Commands, objects_with_names: Query<(Entity, &Name, &Position), Changed>, existing_labels: Query<&ObjectLabel>, + ui_scale: Res, ) { for (entity, name, _position) in objects_with_names.iter() { // Check if label already exists for this entity @@ -214,12 +240,12 @@ fn sync_name_labels( .any(|label| label.target_entity == entity); if !has_label { - // Create new label + // Create new label with scaled font size commands.spawn(( Text::new(name.0.0.clone()), TextColor(Color::WHITE), TextFont { - font_size: 16.0, + font_size: ui_scale.current_font_size, ..default() }, Node { @@ -242,6 +268,7 @@ fn update_label_positions( mut label_query: Query<(&mut Node, &ObjectLabel, &mut Text), With>, objects_query: Query<(&GlobalTransform, &Name, &Radius)>, camera_query: Query<(&Camera, &GlobalTransform)>, + ui_scale: Res, ) { let Ok((camera, camera_transform)) = camera_query.single() else { return; @@ -251,9 +278,12 @@ fn update_label_positions( if let Ok((global_transform, name, _radius)) = objects_query.get(label.target_entity) { let world_pos = global_transform.translation(); if let Ok(screen_pos) = camera.world_to_viewport(camera_transform, world_pos) { + // Apply scale factor to label offset to maintain relative positioning + let scaled_offset = 10.0 * ui_scale.scale_factor; + // Position the label on screen, offset slightly to avoid overlapping the object - node.left = Val::Px(screen_pos.x); - node.top = Val::Px(screen_pos.y); + node.left = Val::Px(screen_pos.x + scaled_offset); + node.top = Val::Px(screen_pos.y - scaled_offset); // Update text in case name changed text.0 = name.0.0.clone(); @@ -345,6 +375,89 @@ fn handle_scroll_zoom( } } +fn handle_speed_controls( + keyboard_input: Res>, + mut time: ResMut>, +) { + // Pause/Unpause with SPACE + if keyboard_input.just_pressed(KeyCode::Space) { + if time.is_paused() { + time.unpause(); + println!("Simulation RESUMED (Speed: {}x)", time.relative_speed()); + } else { + time.pause(); + println!("Simulation PAUSED"); + } + } + + // Speed up with + or = key (double current speed, max 64x) + if keyboard_input.just_pressed(KeyCode::Equal) + || keyboard_input.just_pressed(KeyCode::NumpadAdd) + { + if !time.is_paused() { + let current_speed = time.relative_speed(); + let new_speed = (current_speed * 2.0).min(64.0); + time.set_relative_speed(new_speed); + println!("Speed: {}x", new_speed); + } + } + + // Speed down with - key (halve current speed, min 1.0x) + if keyboard_input.just_pressed(KeyCode::Minus) + || keyboard_input.just_pressed(KeyCode::NumpadSubtract) + { + if !time.is_paused() { + let current_speed = time.relative_speed(); + let new_speed = (current_speed * 0.5).max(1.0); + time.set_relative_speed(new_speed); + println!("Speed: {}x", new_speed); + } + } + + // Reset to normal speed with R + if keyboard_input.just_pressed(KeyCode::KeyR) { + time.set_relative_speed(1.0); + time.unpause(); + println!("Speed reset to 1x"); + } +} + +fn update_ui_scale( + windows: Query<&Window>, + mut ui_scale: ResMut, + egui_contexts: Query<&mut EguiContextSettings>, +) { + if let Ok(window) = windows.single() { + // Calculate scale factor based on physical resolution + // Use 1080p as base resolution (1920x1080) + let base_height = 1080.0; + let current_height = window.physical_height() as f32; + let new_scale_factor = current_height / base_height; + + // Update scale factor if it changed significantly + if (new_scale_factor - ui_scale.scale_factor).abs() > 0.01 { + ui_scale.scale_factor = new_scale_factor; + ui_scale.current_font_size = ui_scale.base_font_size * new_scale_factor; + + // Apply scaling to egui (affects WorldInspectorPlugin) + for mut egui_settings in egui_contexts { + egui_settings.scale_factor = new_scale_factor + } + } + } +} + +fn update_label_font_sizes( + mut label_query: Query<&mut TextFont, With>, + ui_scale: Res, +) { + if ui_scale.is_changed() { + for mut text_font in label_query.iter_mut() { + text_font.font_size = ui_scale.current_font_size; + } + } +} + fn initialize_camera_follow( earth_query: Query, With)>, mut camera_follow: ResMut, @@ -430,7 +543,7 @@ const STEPS: usize = 100; // Time step calculation: // FixedUpdate runs at 64fps, 1 game second = 7 days, 100 steps per FixedUpdate // DT = (7 days / 64 fps) / 100 steps = 7 / (64 * 100) = 0.00109375 days per step -const DT: f64 = 7.0 / (64.0 * STEPS as f64); // Time step in days +const DT: f64 = 1.0 / (64.0 * STEPS as f64); // Time step in days fn n_body(mut query: Query<(&Mass, &mut Position, &mut Velocity)>) { // Collect all bodies data (mass, position, velocity) @@ -489,8 +602,8 @@ fn main() { .add_plugins(DefaultPlugins.set(WindowPlugin { primary_window: Some(Window { title: "Solar Sim".to_string(), - mode: WindowMode::BorderlessFullscreen(MonitorSelection::Primary), - resolution: WindowResolution::default().with_scale_factor_override(2.0), + // mode: WindowMode::BorderlessFullscreen(MonitorSelection::Primary), + resolution: WindowResolution::default().with_scale_factor_override(1.0), ..default() }), ..default()