|
@@ -1,3 +1,255 @@
|
|
|
+use bevy::prelude::*;
|
|
|
+use bevy::sprite::MaterialMesh2dBundle;
|
|
|
+use std::time::Duration;
|
|
|
+
|
|
|
+const SUN_SIZE: f32 = 20.;
|
|
|
+
|
|
|
+#[derive(Event, Default)]
|
|
|
+struct PlayBoop;
|
|
|
+
|
|
|
+#[derive(Resource)]
|
|
|
+struct Boop(f32); // frequency in hz
|
|
|
+
|
|
|
+
|
|
|
+#[derive(Component)]
|
|
|
+struct Position(Vec2);
|
|
|
+
|
|
|
+#[derive(Component)]
|
|
|
+struct Velocity(Vec2);
|
|
|
+
|
|
|
+#[derive(Component)]
|
|
|
+struct Size(f32);
|
|
|
+
|
|
|
+#[derive(Component)]
|
|
|
+struct Mass(f32);
|
|
|
+
|
|
|
+#[derive(Component)]
|
|
|
+struct Planet {
|
|
|
+ focus: Position,
|
|
|
+}
|
|
|
+
|
|
|
+impl Planet {
|
|
|
+ fn new(fx:f32, fy:f32) -> Self {
|
|
|
+ Self {
|
|
|
+ focus: Position(Vec2::new(fx, fy)),
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+#[derive(Component)]
|
|
|
+struct Sun;
|
|
|
+
|
|
|
+#[derive(Bundle)]
|
|
|
+struct PlanetBundle {
|
|
|
+ planet: Planet,
|
|
|
+ size: Size,
|
|
|
+ mass: Mass,
|
|
|
+ position: Position,
|
|
|
+}
|
|
|
+
|
|
|
+impl PlanetBundle {
|
|
|
+ fn new(posx:f32, posy:f32, fx:f32, fy:f32) -> Self {
|
|
|
+ Self {
|
|
|
+ planet: Planet::new(fx, fy),
|
|
|
+ size: Size(20.),
|
|
|
+ mass: Mass(200.),
|
|
|
+ position: Position(Vec2::new(posx, posy)),
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+#[derive(Bundle)]
|
|
|
+struct SunBundle {
|
|
|
+ sun: Sun,
|
|
|
+ size: Size,
|
|
|
+ mass: Mass,
|
|
|
+ position: Position
|
|
|
+}
|
|
|
+
|
|
|
+impl SunBundle {
|
|
|
+ fn new(posx:f32, posy:f32) -> Self {
|
|
|
+ Self {
|
|
|
+ sun: Sun,
|
|
|
+ size: Size(2000.),
|
|
|
+ mass: Mass(20_000_000.),
|
|
|
+ position: Position(Vec2::new(posx, posy)),
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+fn spawn_camera(mut commands: Commands) {
|
|
|
+ commands.spawn_empty()
|
|
|
+ .insert(Camera2dBundle::default());
|
|
|
+}
|
|
|
+
|
|
|
+fn spawn_planet(
|
|
|
+ mut commands: Commands,
|
|
|
+ mut meshes: ResMut<Assets<Mesh>>,
|
|
|
+ mut materials: ResMut<Assets<ColorMaterial>>,
|
|
|
+) {
|
|
|
+ println!("Spawning planet!");
|
|
|
+
|
|
|
+ let shape = Mesh::from(Circle::new(5.));
|
|
|
+ let color = ColorMaterial::from(Color::srgb(1., 0.3, 0.3));
|
|
|
+
|
|
|
+ let mesh_handle = meshes.add(shape);
|
|
|
+ let material_handle = materials.add(color);
|
|
|
+
|
|
|
+ commands.spawn((
|
|
|
+ PlanetBundle::new(120., 150.,
|
|
|
+ 50., 0.),
|
|
|
+ MaterialMesh2dBundle {
|
|
|
+ mesh: mesh_handle.into(),
|
|
|
+ material: material_handle,
|
|
|
+ ..default()
|
|
|
+ },
|
|
|
+ ));
|
|
|
+
|
|
|
+ let shape2 = Mesh::from(Circle::new(3.));
|
|
|
+ let color2 = ColorMaterial::from(Color::srgb(0.2, 0.3, 1.));
|
|
|
+ let mesh2 = meshes.add(shape2);
|
|
|
+ let material2 = materials.add(color2);
|
|
|
+
|
|
|
+ commands.spawn((
|
|
|
+ PlanetBundle::new(80., -20.,
|
|
|
+ 10., 30.),
|
|
|
+ MaterialMesh2dBundle {
|
|
|
+ mesh: mesh2.into(),
|
|
|
+ material: material2,
|
|
|
+ ..default()
|
|
|
+ },
|
|
|
+ ));
|
|
|
+}
|
|
|
+
|
|
|
+fn spawn_sun(
|
|
|
+ mut commands: Commands,
|
|
|
+ mut meshes: ResMut<Assets<Mesh>>,
|
|
|
+ mut materials: ResMut<Assets<ColorMaterial>>,
|
|
|
+) {
|
|
|
+ println!("Spawning sun!");
|
|
|
+
|
|
|
+ let shape = Mesh::from(Circle::new(SUN_SIZE));
|
|
|
+ let color = ColorMaterial::from(Color::srgb(0.8, 0.9, 0.3));
|
|
|
+
|
|
|
+ let mesh_handle = meshes.add(shape);
|
|
|
+ let material_handle = materials.add(color);
|
|
|
+
|
|
|
+ commands.spawn((
|
|
|
+ SunBundle::new(0., 0.),
|
|
|
+ MaterialMesh2dBundle {
|
|
|
+ mesh: mesh_handle.into(),
|
|
|
+ material: material_handle,
|
|
|
+ ..default()
|
|
|
+ },
|
|
|
+ ));
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+fn move_planets(
|
|
|
+ time: Res<Time>,
|
|
|
+ mut planets: Query<(&mut Position, &mut Planet)>
|
|
|
+) {
|
|
|
+ for (mut position, mut planet) in &mut planets {
|
|
|
+ // okay
|
|
|
+ //
|
|
|
+ // so the line segment from sun to point
|
|
|
+ // and the line segment from focus to point
|
|
|
+ // converge at point
|
|
|
+ // we want to bisect the angle, then get the
|
|
|
+ // perpendicular
|
|
|
+
|
|
|
+ // xy0, xy1 - sun to point
|
|
|
+ // xy2, xy3 - focus to point
|
|
|
+
|
|
|
+ let x02 = 0. - planet.focus.0.x;
|
|
|
+ let y02 = 0. - planet.focus.0.y;
|
|
|
+ let x10 = position.0.x - 0.;
|
|
|
+ let y10 = position.0.y - 0.;
|
|
|
+ let x32 = position.0.x - planet.focus.0.x;
|
|
|
+ let y32 = position.0.y - planet.focus.0.y;
|
|
|
+
|
|
|
+ let l = y32 * x10 - x32 * y10;
|
|
|
+ // if l low, parallel (drop to perpendicular);
|
|
|
+ let l10 = (x10.powf(2.) + y10.powf(2.)).sqrt();
|
|
|
+ let l32 = (x32.powf(2.) + y32.powf(2.)).sqrt();
|
|
|
+
|
|
|
+ let speedfactor = l32 / l10;
|
|
|
+
|
|
|
+ let ti = (x32 * y02 - y32 * x02) / l;
|
|
|
+ let xi = 0. + ti * x10;
|
|
|
+ let yi = 0. + ti * y10;
|
|
|
+ let xitmp = xi + x10/l10 - x32/l32;
|
|
|
+ let yitmp = yi + y10/l10 - y32/l32;
|
|
|
+ let li = ((xitmp - xi).powf(2.) + (yitmp - yi).powf(2.)).sqrt();
|
|
|
+ let xi2 = xitmp + ((xitmp - xi) / li) * 60. * speedfactor;
|
|
|
+ let yi2 = yitmp + ((yitmp - yi) / li) * 60. * speedfactor;
|
|
|
+
|
|
|
+
|
|
|
+ if (position.0.y > 0.) {
|
|
|
+ position.0.x += (xi2 - xi).abs() * time.delta_seconds();
|
|
|
+ } else {
|
|
|
+ position.0.x -= (xi2 - xi).abs() * time.delta_seconds();
|
|
|
+ }
|
|
|
+
|
|
|
+ if (position.0.x > 0.) {
|
|
|
+ position.0.y -= (yi2 - yi).abs() * time.delta_seconds();
|
|
|
+ } else {
|
|
|
+ position.0.y += (yi2 - yi).abs() * time.delta_seconds();
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+fn project_positions(
|
|
|
+ mut positionables: Query<(&mut Transform, &Position)>
|
|
|
+) {
|
|
|
+ for (mut transform, position) in &mut positionables {
|
|
|
+ transform.translation = position.0.extend(0.);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+fn setup_audio(
|
|
|
+ mut commands: Commands
|
|
|
+) {
|
|
|
+ commands.insert_resource(Boop(440.));
|
|
|
+}
|
|
|
+
|
|
|
+fn play_boop(
|
|
|
+ mut boop_assets: ResMut<Assets<Pitch>>,
|
|
|
+ boop: Res<Boop>,
|
|
|
+ mut events: EventReader<PlayBoop>,
|
|
|
+ mut commands: Commands,
|
|
|
+) {
|
|
|
+ for _ in events.read() {
|
|
|
+ info!("playing pitch with frequency: {}", boop.0);
|
|
|
+ commands.spawn(PitchBundle {
|
|
|
+ source: boop_assets.add(Pitch::new(boop.0, Duration::from_millis(400))),
|
|
|
+ settings: PlaybackSettings::DESPAWN,
|
|
|
+ });
|
|
|
+ info!("number of pitch assets: {}", boop_assets.len());
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
fn main() {
|
|
|
- println!("Hello, world!");
|
|
|
+ App::new()
|
|
|
+ .add_plugins(DefaultPlugins)
|
|
|
+ .add_event::<PlayBoop>()
|
|
|
+ .add_systems(Startup,
|
|
|
+ (
|
|
|
+ spawn_camera,
|
|
|
+ spawn_sun,
|
|
|
+ spawn_planet,
|
|
|
+ setup_audio,
|
|
|
+ ))
|
|
|
+ .add_systems(Update,
|
|
|
+ (
|
|
|
+ play_boop,
|
|
|
+ move_planets,
|
|
|
+ project_positions.after(move_planets)
|
|
|
+ ),
|
|
|
+ )
|
|
|
+ .run();
|
|
|
}
|