--- /dev/null
+use bevy::prelude::*;
+
+pub struct CameraCfg;
+
+#[derive(Component)]
+pub struct MainCamera;
+
+//#[derive(Event)]
+//pub struct PanCamera(pub Vec2);
+//#[derive(Event)]
+//pub struct ZoomCamera(pub f32);
+//#[derive(Event)]
+//pub struct RotateCamera(pub f32);
+
+#[derive(Event)]
+pub enum MoveCamera {
+ Pan(Vec2),
+ Zoom(f32),
+ Rotate(f32),
+ Center(Vec2),
+ Reset,
+}
+
+impl Plugin for CameraCfg {
+ fn build(&self, app: &mut App) {
+ //app.add_event::<PanCamera>();
+ //app.add_event::<ZoomCamera>();
+ //app.add_event::<RotateCamera>();
+ app.add_event::<MoveCamera>();
+ app.add_systems(Startup, setup);
+ app.add_systems(Update, (
+ move_camera.run_if(on_event::<MoveCamera>()),
+ //pan_camera.run_if(on_event::<PanCamera>()),
+ //zoom_camera.run_if(on_event::<ZoomCamera>()),
+ //rotate_camera.run_if(on_event::<RotateCamera>()),
+ ));
+ }
+}
+
+fn setup(
+ mut commands: Commands,
+) {
+ commands.spawn((Camera2dBundle::default(), MainCamera));
+}
+
+fn move_camera(
+ mut e_read: EventReader<MoveCamera>,
+ mut camera: Query<(&mut Transform, &mut OrthographicProjection), With<MainCamera>>
+) {
+ let Ok((mut trans, mut proj)) = camera.get_single_mut() else { return; };
+ let z = trans.translation.z;
+ for mov in e_read.read() {
+ match mov {
+ MoveCamera::Pan(p) => {
+ let r = trans.rotation.mul_vec3(Vec3::from((*p * proj.scale, z)));
+ trans.translation += r;
+ },
+ MoveCamera::Zoom(z) => proj.scale += z * proj.scale,
+ MoveCamera::Rotate(th) => trans.rotation *= Quat::from_rotation_z(*th),
+ MoveCamera::Center(p) => trans.translation = Vec3::from((*p, z)),
+ MoveCamera::Reset => {
+ trans.translation = Vec3::from((Vec2::ZERO, z));
+ trans.rotation = Quat::from_rotation_z(0.0);
+ proj.scale = 1.0;
+ },
+ }
+ }
+}
+
+//fn pan_camera(
+// mut e_read: EventReader<PanCamera>,
+// mut camera: Query<&mut Transform, With<MainCamera>>
+//) {
+// let Ok(mut camera) = camera.get_single_mut() else { return; };
+// let z = camera.translation.z;
+// for PanCamera(p) in e_read.read() {
+// camera.translation += Vec3::from((*p, z));
+// }
+//}
+//
+//fn zoom_camera(
+// mut e_read: EventReader<ZoomCamera>,
+// mut camera: Query<&mut OrthographicProjection, With<MainCamera>>
+//) {
+// //use bevy::render::camera::ScalingMode;
+// let Ok(mut camera) = camera.get_single_mut() else { return; };
+// for ZoomCamera(z) in e_read.read() {
+// camera.scale += z;
+// }
+//}
+//
+//fn rotate_camera(
+// mut e_read: EventReader<RotateCamera>,
+// mut camera: Query<&mut Transform, With<MainCamera>>
+//) {
+// let Ok(mut camera) = camera.get_single_mut() else { return; };
+// for RotateCamera(th) in e_read.read() {
+// camera.rotation *= Quat::from_rotation_z(*th);
+// }
+//}
--- /dev/null
+use bevy::prelude::*;
+use std::f32::consts::PI;
+
+use crate::camera::*;
+use crate::PausedState;
+
+const PAN_SLOW: f32 = 4.0;
+const ZOOM_SLOW: f32 = 1.0 / 128.0;
+const ROTATE_SLOW: f32 = PI / 512.0;
+const PAN_FAST: f32 = 1.0 / 32.0;
+const ZOOM_FAST: f32 = 0.03125;
+const ROTATE_FAST: f32 = PI / 128.0;
+
+pub struct Controls;
+
+impl Plugin for Controls {
+ fn build(&self, app: &mut App) {
+ app.add_systems(Update, (
+ handle_pause,
+ handle_camera_input,
+ ));
+ }
+}
+
+fn handle_pause(
+ keyboard_input: Res<ButtonInput<KeyCode>>,
+ curr_state: Res<State<PausedState>>,
+ mut next_state: ResMut<NextState<PausedState>>,
+) {
+ if keyboard_input.just_released(KeyCode::Space) {
+ next_state.set(match curr_state.get() {
+ PausedState::Running => PausedState::Paused,
+ PausedState::Paused => PausedState::Running,
+ });
+ }
+}
+
+fn handle_camera_input(
+ keyboard_input: Res<ButtonInput<KeyCode>>,
+ mut e_write: EventWriter<MoveCamera>,
+ //mut e_pan: EventWriter<PanCamera>,
+ //mut e_zoom: EventWriter<ZoomCamera>,
+ //mut e_rotate: EventWriter<RotateCamera>,
+) {
+ let fast = keyboard_input.pressed(KeyCode::ShiftLeft)
+ || keyboard_input.pressed(KeyCode::ShiftRight);
+
+ let p_rate = if fast { PAN_FAST } else { PAN_SLOW };
+ let z_rate = if fast { ZOOM_FAST } else { ZOOM_SLOW };
+ let r_rate = if fast { ROTATE_FAST } else { ROTATE_SLOW };
+
+ if keyboard_input.pressed(KeyCode::KeyW) {
+ e_write.send(MoveCamera::Pan(Vec2::Y * p_rate));
+ }
+ if keyboard_input.pressed(KeyCode::KeyS) {
+ e_write.send(MoveCamera::Pan(Vec2::NEG_Y * p_rate));
+ }
+ if keyboard_input.pressed(KeyCode::KeyA) {
+ e_write.send(MoveCamera::Pan(Vec2::NEG_X * p_rate));
+ }
+ if keyboard_input.pressed(KeyCode::KeyD) {
+ e_write.send(MoveCamera::Pan(Vec2::X * p_rate));
+ }
+
+ if keyboard_input.pressed(KeyCode::KeyR) {
+ e_write.send(MoveCamera::Zoom(-z_rate));
+ }
+ if keyboard_input.pressed(KeyCode::KeyV) {
+ e_write.send(MoveCamera::Zoom(z_rate));
+ }
+
+ if keyboard_input.pressed(KeyCode::KeyQ) {
+ e_write.send(MoveCamera::Rotate(r_rate));
+ }
+ if keyboard_input.pressed(KeyCode::KeyE) {
+ e_write.send(MoveCamera::Rotate(-r_rate));
+ }
+
+ if keyboard_input.pressed(KeyCode::Escape) {
+ e_write.send(MoveCamera::Reset);
+ }
+}
mod physics;
mod particle;
+mod camera;
+mod control;
use crate::physics::*;
use crate::particle::*;
+use crate::camera::*;
+use crate::control::*;
//#[derive(Component)]
//struct Head;
#[derive(Component)]
#[component(storage = "SparseSet")]
struct Selected;
-#[derive(Component)]
-struct MainCamera;
#[derive(Event)]
struct SelectAt(Vec2);
#[derive(Event)]
struct Select(Entity);
+#[derive(States, Default, Debug, Clone, PartialEq, Eq, Hash)]
+enum PausedState {
+ #[default]
+ Running,
+ Paused,
+}
+
fn main() {
App::new()
+ .add_plugins(DefaultPlugins)
.add_event::<SelectAt>()
.add_event::<Select>()
- .add_plugins(DefaultPlugins)
+ .init_state::<PausedState>()
.add_plugins(Physics)
- .add_plugins(Particles)
+ .add_plugins(Particles { count: 128 })
+ .add_plugins(CameraCfg)
+ .add_plugins(Controls)
.add_systems(Startup, setup)
.add_systems(Update, (
handle_click,
find_select_particle.run_if(on_event::<SelectAt>()),
process_select_particle.run_if(on_event::<Select>())
))
+ .configure_sets(Update, PhysicsSet.run_if(in_state(PausedState::Running)))
.run();
}
fn setup(
mut commands: Commands,
) {
- commands.spawn((Camera2dBundle::default(), MainCamera));
commands.spawn((
Diagnostics,
TextBundle::from_sections([
}
}
-pub struct Particles;
+pub struct Particles {
+ pub count: i32,
+}
impl Plugin for Particles {
fn build(&self, app: &mut App) {
- app.add_systems(Startup, setup);
+ let count = self.count;
+ app.add_systems(Startup,
+ move |commands: Commands, meshes: ResMut<Assets<Mesh>>, materials: ResMut<Assets<ColorMaterial>>|
+ setup(commands, meshes, materials, count));
}
}
mut commands: Commands,
mut meshes: ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<ColorMaterial>>,
+ count: i32,
) {
commands.insert_resource(ColorPalette {
default: materials.add(Color::srgb(1.0, 0.8, 0.2)),
commands.insert_resource(MeshPalette {
standard: meshes.add(Circle { radius: 10.0 }),
});
- commands.add(SpawnGroup { count: 128 });
+ commands.add(SpawnGroup { count });
}
struct SpawnGroup {
impl Command for SpawnGroup {
fn apply(self, world: &mut World) {
fn get_vel(u: f32, v: f32) -> Vec2 { Vec2::from_angle(2.0 * PI * u) * v * 5.0 }
- fn get_pos(u: f32, v: f32) -> Vec2 { Vec2::from_angle(2.0 * PI * u) * v * 500.0 }
+ fn get_pos(u: f32, v: f32) -> Vec2 { Vec2::from_angle(2.0 * PI * u) * v.sqrt() * 500.0 }
let mut i = 0;
while i < self.count {
let colors = world.get_resource::<ColorPalette>().unwrap();
pub momentum: Vec2,
}
+#[derive(SystemSet, Debug, Clone, PartialEq, Eq, Hash)]
+pub struct PhysicsSet;
+
pub struct Physics;
impl Plugin for Physics {
apply_velocity,
apply_position,
update_statistics,
- ).chain());
+ ).chain()
+ .in_set(PhysicsSet));
}
}
}
fn apply_position(mut query: Query<(&mut Transform, &Position)>) {
- for (mut transform, p) in &mut query {
+ for (mut transform, Position(p)) in &mut query {
let z = transform.translation.z;
- transform.translation = Vec3::from((p.0 * 1.0, z));
+ transform.translation = Vec3::from((*p * 1.0, z));
}
}