M crates/unified/assets/config/planets.pc.toml => crates/unified/assets/config/planets.pc.toml +13 -10
@@ 1,3 1,6 @@
+[orbit]
+planet_spring_compliance = 0
+
[[planets]]
name = "Sun"
sprite = "textures/sun.png"
@@ 14,7 17,7 @@ radius = 666.66 # m
mass = 205_000_000.0 # kg
default_transform = [116_129.4, 0.0, 0.0]
planet_resource = { name = "Composite", color = { LinearRgba = { red = 0.7, green = 0.7, blue = 0.7, alpha = 1.0 } }, mining_speed = 5.0 }
-orbit = { orbiting = "Sun", eccentricity = 0.2056 }
+orbit = { orbiting = "Sun", eccentricity = 0.2056, period = 299.0 }
[[planets]]
name = "Venus"
@@ 23,7 26,7 @@ radius = 1899.8 # m
mass = 806_166_000.0 # kg
default_transform = [216_999.6, 0.0, 0.0]
planet_resource = { name = "Sulfur", color = { LinearRgba = { red = 0.7, green = 0.7, blue = 0.7, alpha = 1.0 } }, mining_speed = 5.0 }
-orbit = { orbiting = "Sun", eccentricity = 0.0068 }
+orbit = { orbiting = "Sun", eccentricity = 0.0068, period = 546.0 }
[[planets]]
name = "Earth"
@@ 33,7 36,7 @@ radius = 2000.0 # m
mass = 16_900_000_000.0 # kg
planet_resource = { name = "Carbon", color = { LinearRgba = { red = 1.0, green = 0.7, blue = 0.7, alpha = 1.0 } }, mining_speed = 2.0 }
default_transform = [300_000.0, 0.0, 0.0]
-orbit = { orbiting = "Sun", eccentricity = 0.0167 }
+orbit = { orbiting = "Sun", eccentricity = 0.0167, period = 900.0 }
[[planets]]
name = "Moon"
@@ 43,7 46,7 @@ radius = 545.4 # m
mass = 360_236_000.0 # kg
default_transform = [312_700.0, 0.0, 0.0]
planet_resource = { name = "Silicon", color = { LinearRgba = { red = 0.7, green = 0.7, blue = 0.7, alpha = 1.0 } }, mining_speed = 5.0 }
-orbit = { orbiting = "Earth", eccentricity = 0.0549 }
+orbit = { orbiting = "Earth", eccentricity = 0.0549, period = 256.0 }
[[planets]]
name = "Mars"
@@ 53,7 56,7 @@ radius = 1062.0 # m
mass = 525_857_000.0 # kg
default_transform = [430_000.0, 0.0, 0.0]
planet_resource = { name = "Iron", color = { LinearRgba = { red = 0.7, green = 0.7, blue = 0.7, alpha = 1.0 } }, mining_speed = 5.0 }
-orbit = { orbiting = "Sun", eccentricity = 0.0934 }
+orbit = { orbiting = "Sun", eccentricity = 0.0934, period = 1745.0 }
[[planets]]
name = "Jupiter"
@@ 62,7 65,7 @@ radius = 21946.0 # m
mass = 1_131_221_218_000.0 # kg
default_transform = [1_561_140.0, 0.0, 0.0]
planet_resource = { name = "Hydrogen", color = { LinearRgba = { red = 0.7, green = 0.7, blue = 0.7, alpha = 1.0 } }, mining_speed = 5.0 }
-orbit = { orbiting = "Sun", eccentricity = 0.0484 }
+orbit = { orbiting = "Sun", eccentricity = 0.0484, period = 11218.0 }
[[planets]]
name = "Saturn"
@@ 71,7 74,7 @@ radius = 18_280.4 # m
mass = 561_386_112_000.0 # kg
default_transform = [2_874_780.0, 0.0, 0.0]
planet_resource = { name = "Helium", color = { LinearRgba = { red = 0.7, green = 0.7, blue = 0.7, alpha = 1.0 } }, mining_speed = 5.0 }
-orbit = { orbiting = "Sun", eccentricity = 0.0541 }
+orbit = { orbiting = "Sun", eccentricity = 0.0541, period = 28297.0 }
[[planets]]
name = "Uranus"
@@ 80,7 83,7 @@ radius = 8014.0 # m
mass = 69_763_532_000.0 # kg
default_transform = [4_050_000.0, 0.0, 0.0]
planet_resource = { name = "Rubber", color = { LinearRgba = { red = 0.7, green = 0.7, blue = 0.7, alpha = 1.0 } }, mining_speed = 5.0 }
-orbit = { orbiting = "Sun", eccentricity = 0.0472 }
+orbit = { orbiting = "Sun", eccentricity = 0.0472, period = 46791.0 }
[[planets]]
name = "Neptune"
@@ 89,7 92,7 @@ radius = 7_766.0 # m
mass = 106_674_649_000.0 # kg
default_transform = [5_000_000.0, 0.0, 0.0]
planet_resource = { name = "Methane", color = { LinearRgba = { red = 0.7, green = 0.7, blue = 0.7, alpha = 1.0 } }, mining_speed = 5.0 }
-orbit = { orbiting = "Sun", eccentricity = 0.0086 }
+orbit = { orbiting = "Sun", eccentricity = 0.0086, period = 60454.0 }
[[planets]]
name = "Pluto"
@@ 98,4 101,4 @@ radius = 373.6 # m
mass = 10_817_000.0 # kg
default_transform = [5_922_300.0, 0.0, 0.0]
planet_resource = { name = "Ice", color = { LinearRgba = { red = 0.7, green = 0.7, blue = 0.7, alpha = 1.0 } }, mining_speed = 5.0 }
-orbit = { orbiting = "Sun", eccentricity = 0.2488 }
+orbit = { orbiting = "Sun", eccentricity = 0.2488, period = 118234.0 }
M crates/unified/src/client_plugins.rs => crates/unified/src/client_plugins.rs +1 -0
@@ 25,6 25,7 @@ impl PluginGroup for ClientPluginGroup {
.add(UiPlugin)
.add(InputDispatchPlugin)
.add(InputManagerPlugin::<crate::client::input::ClientAction>::default())
+ .add(PhysicsDebugPlugin)
}
}
M crates/unified/src/config/planet.rs => crates/unified/src/config/planet.rs +25 -1
@@ 6,7 6,7 @@ use serde::{Deserialize, Serialize};
#[derive(Deserialize, Asset, TypePath, Component, Serialize, Clone, Debug)]
#[require(
- RigidBody::Static,
+ RigidBody::Dynamic,
Replicated,
)]
pub struct Planet {
@@ 21,6 21,23 @@ pub struct Planet {
pub orbit: Option<OrbitData>
}
+#[derive(Component, Serialize, Deserialize)]
+#[require(
+ RigidBody::Static,
+ Replicated,
+)]
+pub struct PlanetSpring {
+ pub name: String
+}
+
+#[derive(Component, Serialize, Deserialize)]
+#[require(
+ Replicated,
+)]
+pub struct PlanetSpringJoint {
+ pub name: String
+}
+
#[derive(Deserialize, TypePath, Serialize, Clone, Debug)]
pub struct PlanetResource {
pub name: String,
@@ 32,6 49,7 @@ pub struct PlanetResource {
pub struct OrbitData {
pub orbiting: String,
pub eccentricity: f32,
+ pub period: f32
}
#[derive(Deserialize, TypePath, Serialize, Clone, Debug)]
@@ 50,4 68,10 @@ pub struct PlanetBundle {
#[derive(Deserialize, Asset, TypePath)]
pub struct PlanetConfigCollection {
pub planets: Vec<Planet>,
+ pub orbit: OrbitConfig
+}
+
+#[derive(Deserialize, Asset, TypePath, Clone, Debug)]
+pub struct OrbitConfig {
+ pub planet_spring_compliance: f32
}
M crates/unified/src/config/world.rs => crates/unified/src/config/world.rs +1 -1
@@ 6,7 6,7 @@ use serde::Deserialize;
pub struct GlobalWorldConfig {
pub world: WorldConfig,
pub part: WPartConfig,
- pub hearty: HeartyConfig,
+ pub hearty: HeartyConfig
}
#[derive(Deserialize, Asset, TypePath, Clone)]
M crates/unified/src/server/mod.rs => crates/unified/src/server/mod.rs +3 -0
@@ 8,6 8,7 @@ mod damping;
pub mod planets;
pub mod player;
mod system_sets;
+pub mod orbit;
use crate::server::craft::craft_plugin;
use crate::server::damping::damping_plugin;
@@ 29,6 30,7 @@ use aeronet_websocket::server::WebSocketServer;
use crate::prelude::*;
use bevy_replicon::prelude::Replicated;
use std::net::SocketAddr;
+use crate::server::orbit::OrbitPlugin;
use crate::server::player::thrust::server_thrust_plugin;
pub struct ServerPlugin {
@@ 63,6 65,7 @@ impl Plugin for ServerPlugin {
.add_plugins(heat_conduction_plugin)*/
.add_plugins(drill_plugin)
.add_plugins(craft_plugin)
+ .add_plugins(OrbitPlugin)
.add_plugins(damping_plugin)
.configure_sets(Update, WorldUpdateSet.before(PlayerInputSet));
//.configure_sets(Update, PlayerInputSet.before(PhysicsSet::SyncBackend));
A crates/unified/src/server/orbit/mod.rs => crates/unified/src/server/orbit/mod.rs +74 -0
@@ 0,0 1,74 @@
+use std::collections::HashMap;
+use avian2d::math::TAU;
+use avian2d::prelude::LinearVelocity;
+use bevy::log::debug;
+use bevy::math::ops::atan2;
+use bevy::prelude::{Component, Plugin, Transform};
+use bevy::time::Time;
+use serde::{Deserialize, Serialize};
+use crate::config::planet::{Planet, PlanetSpring, PlanetSpringJoint};
+use crate::prelude::{App, Query, Res, Update, Without};
+
+pub struct OrbitPlugin;
+impl Plugin for OrbitPlugin {
+ fn build(&self, app: &mut App) {
+ app.add_systems(Update, update_orbits);
+ }
+}
+
+fn update_orbits(
+ mut planets: Query<(&Planet, &Transform, &mut LinearVelocity), Without<PlanetSpring>>,
+ planets_2: Query<(&Planet, &Transform), Without<PlanetSpring>>,
+ mut planet_springs: Query<(&PlanetSpring, &mut Transform), Without<Planet>>,
+ time: Res<Time>
+) {
+ let parent_velocities = planets.iter().map(|u| (u.0.name.clone(), u.2.clone())).collect::<HashMap<String, LinearVelocity>>();
+ for (planet, _, mut vel) in planets.iter_mut() {
+ let Some(orbit_data) = &planet.orbit else { continue; };
+ // find parent
+ let Some(parent) = planets_2.iter().find(|u| u.0.name == orbit_data.orbiting) else { continue; };
+
+ let a = (planet.default_transform[0] - parent.0.default_transform[0]) / (1.0 - orbit_data.eccentricity);
+ let e = orbit_data.eccentricity;
+ let t = orbit_data.period;
+
+ let time = time.elapsed_secs();
+
+ // calculate position of the planet
+ let m = (TAU / t) * time;
+ let e_k = iterative_kepler(m, e);
+ let nu = 2.0 * atan2(
+ (1.0 + e).sqrt() * (e_k / 2.0).sin(),
+ (1.0 - e).sqrt() * (e_k / 2.0).cos(),
+ );
+ let r = a * (1.0 - e * e_k.cos());
+
+ let x = r * nu.cos();
+ let y = r * nu.sin();
+
+ // find the spring
+ let Some(mut planet_spring) = planet_springs.iter_mut().find(|u| u.0.name == planet.name) else { continue; };
+ planet_spring.1.translation.x = x + parent.1.translation.x;
+ planet_spring.1.translation.y = y + parent.1.translation.y;
+
+ let Some(parent_velocity) = parent_velocities.get(&orbit_data.orbiting) else { continue; };
+ let de_dt = (TAU / t) / (1.0 - e * e_k.cos());
+ let b_factor = (1.0 - e * e).sqrt();
+ let vx = -a * e_k.sin() * de_dt;
+ let vy = a * b_factor * e_k.cos() * de_dt;
+ vel.x = vx + parent_velocity.x;
+ vel.y = vy + parent_velocity.y;
+ }
+}
+
+fn iterative_kepler(m: f32, e: f32) -> f32 {
+ let mut output = m;
+ for _ in 0..100 {
+ let d = (m - output + e * output.sin()) / (1.0 - e * output.cos());
+ output += d;
+ if d.abs() < 1e-10 {
+ break;
+ }
+ }
+ output
+}<
\ No newline at end of file
M crates/unified/src/server/planets.rs => crates/unified/src/server/planets.rs +47 -12
@@ 2,6 2,7 @@ use crate::{config::planet::{Planet, PlanetBundle, PlanetConfigCollection}, ecs:
use bevy::asset::Handle;
use crate::prelude::*;
use bevy_replicon::prelude::Replicated;
+use crate::config::planet::{PlanetSpring, PlanetSpringJoint};
pub fn planets_plugin(app: &mut App) {
app.init_resource::<PlanetConfigResource>()
@@ 29,6 30,7 @@ pub fn update_planets(
&mut Transform,
&mut Mass,
)>,
+ mut planet_joint_springs: Query<(&PlanetSpringJoint, &mut FixedJoint)>
) {
let Some(handle) = planets.handle.as_ref() else {
return;
@@ 43,7 45,7 @@ pub fn update_planets(
debug!("planet config loaded - creating planets");
let planet_config = assets.get(*id).unwrap();
for planet in &planet_config.planets {
- commands
+ let mut planet_entity = commands
.spawn(PlanetBundle {
planet: planet.clone(),
transform: Transform::from_xyz(
@@ 53,14 55,31 @@ pub fn update_planets(
),
collider: Collider::circle(planet.radius),
mass: Mass(planet.mass)
- })
- .insert(Replicated)
- .with_child((
+ }).with_child((
Collider::circle(planet.radius+2.0),
Sensor,
PlanetSensor(planet.name.clone()),
CollisionEventsEnabled,
+ )).id();
+ if planet.orbit.is_some() {
+ let spring =commands.spawn((
+ PlanetSpring {
+ name: planet.name.clone()
+ },
+ Transform::from_xyz(
+ planet.default_transform[0],
+ planet.default_transform[1],
+ planet.default_transform[2],
+ )
+ )).id();
+ commands.spawn((
+ PlanetSpringJoint {
+ name: planet.name.clone()
+ },
+ FixedJoint::new(planet_entity, spring).with_point_compliance(planet_config.orbit.planet_spring_compliance)
));
+ }
+
trace!(?planet, "new planet spawned");
}
}
@@ 70,6 89,9 @@ pub fn update_planets(
trace!("planet config modified - reloading planets");
let planet_config = assets.get(*id).unwrap();
+ info!("updating compliance on all planet spring joints");
+ planet_joint_springs.iter_mut().for_each(|mut u| u.1.point_compliance = planet_config.orbit.planet_spring_compliance);
+
for planet in &planet_config.planets {
let existing_planet = q_planets
.iter_mut()
@@ 83,15 105,10 @@ pub fn update_planets(
.remove::<Collider>()
.insert(Collider::circle(planet.radius));
*e_planet = planet.clone();
- e_transform.translation = Vec3::new(
- planet.default_transform[0],
- planet.default_transform[1],
- planet.default_transform[2],
- );
*e_mass = Mass(planet.mass);
trace!(?planet, "planet hot-reloaded");
} else {
- commands
+ let planet_entity = commands
.spawn(PlanetBundle {
planet: planet.clone(),
transform: Transform::from_xyz(
@@ 103,8 120,26 @@ pub fn update_planets(
mass: Mass(
planet.mass,
),
- })
- .insert(Replicated);
+ }).id();
+
+ if planet.orbit.is_some() {
+ let spring =commands.spawn((
+ PlanetSpring {
+ name: planet.name.clone()
+ },
+ Transform::from_xyz(
+ planet.default_transform[0],
+ planet.default_transform[1],
+ planet.default_transform[2],
+ )
+ )).id();
+ commands.spawn((
+ PlanetSpringJoint {
+ name: planet.name.clone()
+ },
+ FixedJoint::new(planet_entity, spring).with_point_compliance(planet_config.orbit.planet_spring_compliance)
+ ));
+ }
trace!(?planet, "new planet spawned");
}
}
M crates/unified/src/shared_plugins.rs => crates/unified/src/shared_plugins.rs +3 -1
@@ 1,5 1,5 @@
use crate::attachment::{Joint, JointOf, PartInShip, Peer, Ship, SnapOf, SnapOfJoint};
-use crate::config::planet::{Planet, PlanetConfigCollection};
+use crate::config::planet::{Planet, PlanetConfigCollection, PlanetSpring, PlanetSpringJoint};
use crate::config::recipe::RecipesConfig;
use crate::ecs::{CanCraft, CraftPartRequest, DragRequestEvent, Drill, Hi, Part, Particles, Player, PlayerStorage, SingleStorage, Temperature, ToggleDrillEvent};
use bevy::app::{App, PluginGroup, PluginGroupBuilder};
@@ 60,6 60,8 @@ pub fn register_everything(app: &mut App) {
app.replicate::<Temperature>();
app.replicate::<Drill>();
app.replicate::<SingleStorage>();
+ app.replicate::<PlanetSpring>();
+ app.replicate::<PlanetSpringJoint>();
}
fn physics_setup_plugin(app: &mut App) {
M crates/unified/src/world_config.rs => crates/unified/src/world_config.rs +1 -0
@@ 1,5 1,6 @@
use crate::config::world::GlobalWorldConfig;
use bevy::asset::Handle;
+use crate::config::planet::PlanetSpringJoint;
use crate::prelude::*;
pub fn world_config_plugin(app: &mut App) {