M crates/unified/src/attachment.rs => crates/unified/src/attachment.rs +3 -4
@@ 1,8 1,8 @@
-use std::ops::Deref;
use bevy::asset::processor::ErasedProcessor;
use bevy::ecs::entity::MapEntities;
use bevy::prelude::*;
use serde::{Deserialize, Serialize};
+use std::ops::Deref;
#[derive(Component, Serialize, Deserialize)]
/// The primary component for a ship structure
@@ 16,12 16,11 @@ pub struct Parts(#[entities] Vec<Entity>);
#[relationship(relationship_target = Parts)]
pub struct PartInShip(#[entities] Entity);
-
#[derive(Component, Serialize, Deserialize)]
#[require(Transform)]
pub struct Joint {
pub id: JointId,
- pub transform: Transform
+ pub transform: Transform,
}
#[derive(Component, Serialize, Deserialize)]
pub struct Peer(#[entities] Entity);
@@ 48,4 47,4 @@ impl JointId {
}
#[derive(Serialize, Deserialize, Component)]
-pub struct JointSnapFor(#[entities] pub Entity);>
\ No newline at end of file
+pub struct JointSnapFor(#[entities] pub Entity);
M crates/unified/src/client/key_input.rs => crates/unified/src/client/key_input.rs +24 -26
@@ 1,17 1,20 @@
-use bevy::{
- app::{App, Update},
- ecs::{event::EventWriter, system::Res},
- input::{ButtonInput, keyboard::KeyCode},
-};
+use crate::attachment::{Joint, JointSnapFor};
+use crate::ecs::{Part, ThrustEvent};
use bevy::color::palettes::css::{FUCHSIA, GREEN};
use bevy::dev_tools::picking_debug::DebugPickingMode;
use bevy::gizmos::AppGizmoBuilder;
use bevy::log::{debug, info};
use bevy::math::Vec3Swizzles;
-use bevy::prelude::{ChildOf, GizmoConfigGroup, Gizmos, GlobalTransform, IntoScheduleConfigs, Query, Reflect, ResMut, Resource, Transform, With};
+use bevy::prelude::{
+ ChildOf, GizmoConfigGroup, Gizmos, GlobalTransform, IntoScheduleConfigs, Query, Reflect,
+ ResMut, Resource, Transform, With,
+};
+use bevy::{
+ app::{App, Update},
+ ecs::{event::EventWriter, system::Res},
+ input::{ButtonInput, keyboard::KeyCode},
+};
use bevy_rapier2d::render::DebugRenderContext;
-use crate::attachment::{Joint, JointSnapFor};
-use crate::ecs::{Part, ThrustEvent};
pub fn key_input_plugin(app: &mut App) {
app.add_systems(Update, directional_keys)
@@ 40,11 43,7 @@ fn debug_render_keybind(
}
}
-
-fn directional_keys(
- keys: Res<ButtonInput<KeyCode>>,
- mut thrust_event: EventWriter<ThrustEvent>,
-) {
+fn directional_keys(keys: Res<ButtonInput<KeyCode>>, mut thrust_event: EventWriter<ThrustEvent>) {
if keys.just_pressed(KeyCode::KeyW) || keys.just_pressed(KeyCode::ArrowUp) {
thrust_event.write(ThrustEvent::Up(true));
} else if keys.just_released(KeyCode::KeyW) || keys.just_released(KeyCode::ArrowUp) {
@@ 70,20 69,19 @@ fn directional_keys(
}
}
-fn draw_attachment_debug(joints: Query<&GlobalTransform, With<Joint>>, snaps: Query<&GlobalTransform, With<JointSnapFor>>, mut gizmos: Gizmos, mut state: ResMut<AttachmentDebugRes>) {
- if !state.0 { return; }
+fn draw_attachment_debug(
+ joints: Query<&GlobalTransform, With<Joint>>,
+ snaps: Query<&GlobalTransform, With<JointSnapFor>>,
+ mut gizmos: Gizmos,
+ mut state: ResMut<AttachmentDebugRes>,
+) {
+ if !state.0 {
+ return;
+ }
for joint_target in joints.iter() {
- gizmos.cross_2d(
- joint_target.translation().xy(),
- 4.0,
- FUCHSIA
- );
+ gizmos.cross_2d(joint_target.translation().xy(), 4.0, FUCHSIA);
}
for joint_snap in snaps.iter() {
- gizmos.cross_2d(
- joint_snap.translation().xy(),
- 4.0,
- GREEN
- );
+ gizmos.cross_2d(joint_snap.translation().xy(), 4.0, GREEN);
}
-}>
\ No newline at end of file
+}
M crates/unified/src/client/mod.rs => crates/unified/src/client/mod.rs +13 -12
@@ 1,16 1,17 @@
mod colors;
-mod parts;
mod key_input;
mod net;
+mod particles;
+mod parts;
+mod planet;
mod starfield;
mod ui;
-mod planet;
mod zoom;
-mod particles;
-use crate::client::parts::parts_plugin;
-use planet::incoming_planets::incoming_planets_plugin;
use crate::client::key_input::key_input_plugin;
+use crate::client::net::set_config;
+use crate::client::parts::parts_plugin;
+use crate::client::planet::indicators::indicators_plugin;
use crate::client::starfield::starfield_plugin;
use crate::client::ui::ui_plugin;
use crate::client::zoom::zoom_plugin;
@@ 24,8 25,7 @@ use bevy::prelude::*;
use bevy::window::PrimaryWindow;
use bevy_egui::EguiPlugin;
use bevy_replicon::shared::server_entity_map::ServerEntityMap;
-use crate::client::net::set_config;
-use crate::client::planet::indicators::indicators_plugin;
+use planet::incoming_planets::incoming_planets_plugin;
pub struct ClientPlugin {
pub server: String,
@@ 71,15 71,16 @@ fn find_me(
for (entity, player, part) in q_clients.iter() {
if player.client == entity {
commands.entity(entity).insert(Me);
- let mut heart_sprite = Sprite::from_image(asset_server.load("sprites/hearty_heart.png"));
+ let mut heart_sprite =
+ Sprite::from_image(asset_server.load("sprites/hearty_heart.png"));
heart_sprite.custom_size = Some(Vec2::new(part.width, part.height));
heart_sprite.color = Color::srgb(20.0, 0.0, 0.0);
commands.spawn((
- ChildOf(entity),
- heart_sprite,
- Transform::from_xyz(0.0, 0.0, 10.0)
- ));
+ ChildOf(entity),
+ heart_sprite,
+ Transform::from_xyz(0.0, 0.0, 10.0),
+ ));
}
}
}
M crates/unified/src/client/net.rs => crates/unified/src/client/net.rs +0 -1
@@ 10,7 10,6 @@ pub fn set_config(mut q: Query<&mut TransportConfig, Added<TransportConfig>>) {
}
}
-
pub fn on_connecting(
trigger: Trigger<OnAdd, SessionEndpoint>,
names: Query<&Name>,
M crates/unified/src/client/particles/mod.rs => crates/unified/src/client/particles/mod.rs +1 -3
@@ 3,7 3,5 @@ use bevy::app::{App, Plugin};
pub struct ParticlePlugin;
impl Plugin for ParticlePlugin {
- fn build(&self, app: &mut App) {
-
- }
+ fn build(&self, app: &mut App) {}
}
M crates/unified/src/client/parts.rs => crates/unified/src/client/parts.rs +26 -10
@@ 1,9 1,9 @@
-use std::fmt::Debug;
+use crate::client::Me;
use crate::ecs::{CursorWorldCoordinates, DragRequestEvent, Part};
use bevy::prelude::*;
use bevy_rapier2d::dynamics::MassProperties;
use bevy_rapier2d::prelude::{AdditionalMassProperties, ReadMassProperties, RigidBody};
-use crate::client::Me;
+use std::fmt::Debug;
pub fn parts_plugin(app: &mut App) {
app.insert_resource(DragResource(None));
@@ 63,20 63,36 @@ fn handle_updated_parts(
#[derive(Resource)]
struct DragResource(Option<Entity>);
-
-fn on_part_click(ev: Trigger<Pointer<Pressed>>, sprites: Query<&Sprite, Without<Me>>, mut drag: ResMut<DragResource>) {
- if ev.button != PointerButton::Primary { return; };
- let Ok(sprite) = sprites.get(ev.target()) else { return; };
+fn on_part_click(
+ ev: Trigger<Pointer<Pressed>>,
+ sprites: Query<&Sprite, Without<Me>>,
+ mut drag: ResMut<DragResource>,
+) {
+ if ev.button != PointerButton::Primary {
+ return;
+ };
+ let Ok(sprite) = sprites.get(ev.target()) else {
+ return;
+ };
drag.0 = Some(ev.target());
}
-fn on_part_release(ev: Trigger<Pointer<Released>>, mut drag: ResMut<DragResource>, mut events: EventWriter<DragRequestEvent>, cursor: Res<CursorWorldCoordinates>) {
- if ev.button != PointerButton::Primary { return; };
+fn on_part_release(
+ ev: Trigger<Pointer<Released>>,
+ mut drag: ResMut<DragResource>,
+ mut events: EventWriter<DragRequestEvent>,
+ cursor: Res<CursorWorldCoordinates>,
+) {
+ if ev.button != PointerButton::Primary {
+ return;
+ };
- if let Some(e) = drag.0 && let Some(c) = cursor.0 {
+ if let Some(e) = drag.0
+ && let Some(c) = cursor.0
+ {
debug!(?e, ?c, "sending drag request");
events.write(DragRequestEvent(e, c));
}
drag.0 = None;
-}>
\ No newline at end of file
+}
M crates/unified/src/client/planet/incoming_planets.rs => crates/unified/src/client/planet/incoming_planets.rs +11 -10
@@ 11,7 11,7 @@ fn handle_incoming_planets(
new_planets: Query<(Entity, &Planet), Added<Planet>>,
asset_server: Res<AssetServer>,
meshes: ResMut<Assets<Mesh>>,
- materials: ResMut<Assets<ColorMaterial>>
+ materials: ResMut<Assets<ColorMaterial>>,
) {
for (new_entity, new_planet) in new_planets.iter() {
let mut sprite = Sprite::from_image(asset_server.load(&new_planet.sprite));
@@ 21,11 21,11 @@ fn handle_incoming_planets(
sprite.color = c;
}
- let mut commands = commands
- .entity(new_entity);
+ let mut commands = commands.entity(new_entity);
- commands.insert(AdditionalMassProperties::Mass(new_planet.mass))
- .insert(sprite);
+ commands
+ .insert(AdditionalMassProperties::Mass(new_planet.mass))
+ .insert(sprite);
trace!(?new_planet, "prepared new planet");
}
@@ 35,7 35,7 @@ fn handle_updated_planets(
updated_planets: Query<(Entity, &Planet), Changed<Planet>>,
asset_server: Res<AssetServer>,
meshes: ResMut<Assets<Mesh>>,
- materials: ResMut<Assets<ColorMaterial>>
+ materials: ResMut<Assets<ColorMaterial>>,
) {
for (updated_entity, updated_planet) in updated_planets.iter() {
let mut sprite = Sprite::from_image(asset_server.load(&updated_planet.sprite));
@@ 44,18 44,19 @@ fn handle_updated_planets(
updated_planet.radius * 2.0,
));
- if let Some(SpecialSpriteProperties::ForceColor(c)) = updated_planet.special_sprite_properties {
+ if let Some(SpecialSpriteProperties::ForceColor(c)) =
+ updated_planet.special_sprite_properties
+ {
sprite.color = c;
}
let mut commands = commands.entity(updated_entity);
- commands.remove::<AdditionalMassProperties>()
+ commands
+ .remove::<AdditionalMassProperties>()
.insert(AdditionalMassProperties::Mass(updated_planet.mass))
.remove::<Sprite>()
.insert(sprite);
-
-
trace!(?updated_planet, "updated planet");
}
}
M crates/unified/src/client/planet/indicators.rs => crates/unified/src/client/planet/indicators.rs +49 -20
@@ 1,8 1,8 @@
-use bevy::prelude::*;
-use bevy::window::PrimaryWindow;
use crate::client::Me;
use crate::config::planet::Planet;
use crate::ecs::MainCamera;
+use bevy::prelude::*;
+use bevy::window::PrimaryWindow;
pub fn indicators_plugin(app: &mut App) {
app.add_systems(PreUpdate, (add_indicators, update_indicators))
@@ 14,27 14,45 @@ struct PlanetIndicator(String);
#[derive(Component)]
struct HasIndicator(Entity);
-fn add_indicators(planets_wo_indicators: Query<(Entity, &Planet), Without<HasIndicator>>, player: Query<Entity, With<Me>>, asset_server: Res<AssetServer>, mut commands: Commands) {
- let Ok(me) = player.single() else { return; };
+fn add_indicators(
+ planets_wo_indicators: Query<(Entity, &Planet), Without<HasIndicator>>,
+ player: Query<Entity, With<Me>>,
+ asset_server: Res<AssetServer>,
+ mut commands: Commands,
+) {
+ let Ok(me) = player.single() else {
+ return;
+ };
for (planet, planet_data) in &planets_wo_indicators {
- let Some(indicator_url) = &planet_data.indicator_sprite else { continue };
+ let Some(indicator_url) = &planet_data.indicator_sprite else {
+ continue;
+ };
let mut sprite = Sprite::from_image(asset_server.load(indicator_url));
sprite.custom_size = Some(Vec2::new(25.0, 25.0));
- let indicator = commands.spawn((
- ChildOf(me),
- PlanetIndicator(planet_data.name.clone()),
- sprite,
- Transform::from_xyz(0.0, 0.0, 0.0)
- )).id();
+ let indicator = commands
+ .spawn((
+ ChildOf(me),
+ PlanetIndicator(planet_data.name.clone()),
+ sprite,
+ Transform::from_xyz(0.0, 0.0, 0.0),
+ ))
+ .id();
commands.entity(planet).insert(HasIndicator(indicator));
}
}
-fn update_indicators(changed_planets_w_indicators: Query<(&Planet, &HasIndicator), Changed<Planet>>, asset_server: Res<AssetServer>, mut commands: Commands) {
+fn update_indicators(
+ changed_planets_w_indicators: Query<(&Planet, &HasIndicator), Changed<Planet>>,
+ asset_server: Res<AssetServer>,
+ mut commands: Commands,
+) {
for (planet_data, indicator) in changed_planets_w_indicators.iter() {
- let Some(indicator_sprite) = &planet_data.indicator_sprite else { continue; };
+ let Some(indicator_sprite) = &planet_data.indicator_sprite else {
+ continue;
+ };
let mut sprite = Sprite::from_image(asset_server.load(indicator_sprite));
sprite.custom_size = Some(Vec2::new(50.0, 50.0));
- commands.entity(indicator.0)
+ commands
+ .entity(indicator.0)
.remove::<Sprite>()
.insert(sprite);
}
@@ 42,12 60,21 @@ fn update_indicators(changed_planets_w_indicators: Query<(&Planet, &HasIndicator
fn update_indicators_position(
planets_w_indicator: Query<(&Transform, &HasIndicator), Without<PlanetIndicator>>,
player: Query<&Transform, (With<Me>, Without<PlanetIndicator>)>,
- mut indicators: Query<(&mut Transform, &mut Sprite), (With<PlanetIndicator>, Without<HasIndicator>, Without<Me>, Without<MainCamera>)>,
+ mut indicators: Query<
+ (&mut Transform, &mut Sprite),
+ (
+ With<PlanetIndicator>,
+ Without<HasIndicator>,
+ Without<Me>,
+ Without<MainCamera>,
+ ),
+ >,
window: Query<&Window, With<PrimaryWindow>>,
camera: Single<&Transform, (With<MainCamera>, Without<PlanetIndicator>)>,
-)
-{
- let Ok(player_position) = player.single() else { return; };
+) {
+ let Ok(player_position) = player.single() else {
+ return;
+ };
let Ok(window) = window.single() else { return };
for (planet_position, indicator_id) in &planets_w_indicator {
@@ 60,7 87,9 @@ fn update_indicators_position(
offset.x = offset.x.clamp(-half_window_width, half_window_width);
offset.y = offset.y.clamp(-half_window_height, half_window_height);
- let Ok((mut this_indicator, mut this_sprite)) = indicators.get_mut(indicator_id.0) else { continue; };
+ let Ok((mut this_indicator, mut this_sprite)) = indicators.get_mut(indicator_id.0) else {
+ continue;
+ };
this_sprite.custom_size = Some(Vec2::splat(sprite_size));
@@ 68,4 97,4 @@ fn update_indicators_position(
this_indicator.translation = inv_rot.mul_vec3(Vec3::new(offset.x, offset.y, 0.0));
this_indicator.rotation = inv_rot;
}
-}>
\ No newline at end of file
+}
M crates/unified/src/client/planet/mod.rs => crates/unified/src/client/planet/mod.rs +1 -1
@@ 1,2 1,2 @@
pub mod incoming_planets;
-pub mod indicators;>
\ No newline at end of file
+pub mod indicators;
M crates/unified/src/client/starfield.rs => crates/unified/src/client/starfield.rs +43 -10
@@ 168,12 168,18 @@ pub fn resize_starfield(
camera: Single<&Transform, With<MainCamera>>,
) {
for event in resize_event.read() {
- starfield_back.single_mut().unwrap().custom_size =
- Some(Vec2::new(event.width, event.height) * camera.scale.z + Vec2::splat(BACK_STARFIELD_SIZE * 2.0));
- starfield_mid.single_mut().unwrap().custom_size =
- Some(Vec2::new(event.width, event.height) * camera.scale.z + Vec2::splat(MID_STARFIELD_SIZE * 2.0));
- starfield_front.single_mut().unwrap().custom_size =
- Some(Vec2::new(event.width, event.height) * camera.scale.z + Vec2::splat(FRONT_STARFIELD_SIZE * 2.0));
+ starfield_back.single_mut().unwrap().custom_size = Some(
+ Vec2::new(event.width, event.height) * camera.scale.z
+ + Vec2::splat(BACK_STARFIELD_SIZE * 2.0),
+ );
+ starfield_mid.single_mut().unwrap().custom_size = Some(
+ Vec2::new(event.width, event.height) * camera.scale.z
+ + Vec2::splat(MID_STARFIELD_SIZE * 2.0),
+ );
+ starfield_front.single_mut().unwrap().custom_size = Some(
+ Vec2::new(event.width, event.height) * camera.scale.z
+ + Vec2::splat(FRONT_STARFIELD_SIZE * 2.0),
+ );
}
}
@@ 206,7 212,16 @@ pub fn update_starfield(
),
>,
window: Single<&Window>,
- camera: Single<&Transform, (With<MainCamera>, Without<Me>, Without<StarfieldFront>, Without<StarfieldMid>, Without<StarfieldBack>)>,
+ camera: Single<
+ &Transform,
+ (
+ With<MainCamera>,
+ Without<Me>,
+ Without<StarfieldFront>,
+ Without<StarfieldMid>,
+ Without<StarfieldBack>,
+ ),
+ >,
player: Query<&Transform, (With<Me>, Without<StarfieldFront>)>,
) {
let Some(player) = player.iter().next() else {
@@ 218,17 233,35 @@ pub fn update_starfield(
//starfield_pos.translation = (player.translation / STARFIELD_SIZE).round() * STARFIELD_SIZE;
starfield_back_pos.translation = player.translation
+ (-player.translation / 3.0) % BACK_STARFIELD_SIZE
- + (Vec3::new(window.resolution.width(), -window.resolution.height() + BACK_STARFIELD_SIZE, 0.0)*camera.scale.z/2.0) % BACK_STARFIELD_SIZE
+ + (Vec3::new(
+ window.resolution.width(),
+ -window.resolution.height() + BACK_STARFIELD_SIZE,
+ 0.0,
+ ) * camera.scale.z
+ / 2.0)
+ % BACK_STARFIELD_SIZE
+ Vec3::new(0.0, BACK_STARFIELD_SIZE, 0.0)
- Vec3::new(0.0, 0.0, 5.0);
starfield_mid_pos.translation = player.translation
+ (-player.translation / 2.5) % MID_STARFIELD_SIZE
- + (Vec3::new(window.resolution.width(), -window.resolution.height() + MID_STARFIELD_SIZE, 0.0)*camera.scale.z/2.0) % MID_STARFIELD_SIZE
+ + (Vec3::new(
+ window.resolution.width(),
+ -window.resolution.height() + MID_STARFIELD_SIZE,
+ 0.0,
+ ) * camera.scale.z
+ / 2.0)
+ % MID_STARFIELD_SIZE
+ Vec3::new(0.0, MID_STARFIELD_SIZE, 0.0)
- Vec3::new(0.0, 0.0, 4.5);
starfield_front_pos.translation = player.translation
+ (-player.translation / 2.0) % FRONT_STARFIELD_SIZE
- + (Vec3::new(window.resolution.width(), -window.resolution.height() + FRONT_STARFIELD_SIZE, 0.0)*camera.scale.z/2.0) % FRONT_STARFIELD_SIZE
+ + (Vec3::new(
+ window.resolution.width(),
+ -window.resolution.height() + FRONT_STARFIELD_SIZE,
+ 0.0,
+ ) * camera.scale.z
+ / 2.0)
+ % FRONT_STARFIELD_SIZE
+ Vec3::new(0.0, FRONT_STARFIELD_SIZE, 0.0)
- Vec3::new(0.0, 0.0, 4.0);
}
M crates/unified/src/client/zoom.rs => crates/unified/src/client/zoom.rs +43 -7
@@ 1,6 1,15 @@
-use bevy::{input::mouse::{MouseScrollUnit, MouseWheel}, prelude::*};
+use bevy::{
+ input::mouse::{MouseScrollUnit, MouseWheel},
+ prelude::*,
+};
-use crate::{client::{starfield::{BACK_STARFIELD_SIZE, FRONT_STARFIELD_SIZE, MID_STARFIELD_SIZE}, Me}, ecs::{MainCamera, StarfieldBack, StarfieldFront, StarfieldMid}};
+use crate::{
+ client::{
+ Me,
+ starfield::{BACK_STARFIELD_SIZE, FRONT_STARFIELD_SIZE, MID_STARFIELD_SIZE},
+ },
+ ecs::{MainCamera, StarfieldBack, StarfieldFront, StarfieldMid},
+};
pub fn zoom_plugin(app: &mut App) {
app.add_systems(Update, on_scroll);
@@ 33,8 42,26 @@ fn on_scroll(
Without<StarfieldMid>,
),
>,
- mut camera: Single<&mut Transform, (With<MainCamera>, Without<Me>, Without<StarfieldFront>, Without<StarfieldMid>, Without<StarfieldBack>)>,
- player: Single<&Transform, (With<Me>, Without<StarfieldFront>, Without<StarfieldMid>, Without<StarfieldBack>, Without<MainCamera>)>,
+ mut camera: Single<
+ &mut Transform,
+ (
+ With<MainCamera>,
+ Without<Me>,
+ Without<StarfieldFront>,
+ Without<StarfieldMid>,
+ Without<StarfieldBack>,
+ ),
+ >,
+ player: Single<
+ &Transform,
+ (
+ With<Me>,
+ Without<StarfieldFront>,
+ Without<StarfieldMid>,
+ Without<StarfieldBack>,
+ Without<MainCamera>,
+ ),
+ >,
) {
let (mut starfield_back, mut starfield_back_pos) = starfield_back.into_inner();
let (mut starfield_mid, mut starfield_mid_pos) = starfield_mid.into_inner();
@@ 55,17 82,26 @@ fn on_scroll(
Some(window.size() * camera.scale.z + Vec2::splat(FRONT_STARFIELD_SIZE * 2.0));
starfield_back_pos.translation = player.translation
+ (-player.translation / 3.0) % BACK_STARFIELD_SIZE
- + (Vec3::new(window.resolution.width(), -window.resolution.height(), 0.0)*camera.scale.z/2.0) % BACK_STARFIELD_SIZE
+ + (Vec3::new(window.resolution.width(), -window.resolution.height(), 0.0)
+ * camera.scale.z
+ / 2.0)
+ % BACK_STARFIELD_SIZE
+ Vec3::new(0.0, BACK_STARFIELD_SIZE, 0.0)
- Vec3::new(0.0, 0.0, 5.0);
starfield_mid_pos.translation = player.translation
+ (-player.translation / 2.5) % MID_STARFIELD_SIZE
- + (Vec3::new(window.resolution.width(), -window.resolution.height(), 0.0)*camera.scale.z/2.0) % MID_STARFIELD_SIZE
+ + (Vec3::new(window.resolution.width(), -window.resolution.height(), 0.0)
+ * camera.scale.z
+ / 2.0)
+ % MID_STARFIELD_SIZE
+ Vec3::new(0.0, MID_STARFIELD_SIZE, 0.0)
- Vec3::new(0.0, 0.0, 4.5);
starfield_front_pos.translation = player.translation
+ (-player.translation / 2.0) % FRONT_STARFIELD_SIZE
- + (Vec3::new(window.resolution.width(), -window.resolution.height(), 0.0)*camera.scale.z/2.0) % FRONT_STARFIELD_SIZE
+ + (Vec3::new(window.resolution.width(), -window.resolution.height(), 0.0)
+ * camera.scale.z
+ / 2.0)
+ % FRONT_STARFIELD_SIZE
+ Vec3::new(0.0, FRONT_STARFIELD_SIZE, 0.0)
- Vec3::new(0.0, 0.0, 4.0);
}
M crates/unified/src/config/mod.rs => crates/unified/src/config/mod.rs +1 -1
@@ 1,3 1,3 @@
+pub mod part;
pub mod planet;
pub mod world;
-pub mod part;
M crates/unified/src/config/part.rs => crates/unified/src/config/part.rs +3 -3
@@ 7,7 7,7 @@ use serde::{Deserialize, Serialize};
pub struct PartConfig {
pub part: PartPartConfig,
pub physics: PartPhysicsConfig,
- pub joints: Vec<JointConfig>
+ pub joints: Vec<JointConfig>,
}
#[derive(Deserialize, TypePath, Serialize, Clone, Debug, PartialEq)]
pub struct PartPartConfig {
@@ 19,7 19,7 @@ pub struct PartPartConfig {
pub struct PartPhysicsConfig {
pub width: f32,
pub height: f32,
- pub mass: f32
+ pub mass: f32,
}
#[derive(Deserialize, TypePath, Serialize, Clone, Debug, PartialEq)]
pub struct JointConfig {
@@ 40,4 40,4 @@ impl From<JointOffset> for Transform {
..Default::default()
}
}
- }>
\ No newline at end of file
+}
M crates/unified/src/config/planet.rs => crates/unified/src/config/planet.rs +1 -2
@@ 18,10 18,9 @@ pub struct Planet {
#[derive(Deserialize, TypePath, Serialize, Clone, Debug)]
pub enum SpecialSpriteProperties {
- ForceColor(Color)
+ ForceColor(Color),
}
-
#[derive(Bundle)]
pub struct PlanetBundle {
pub planet: Planet,
M crates/unified/src/config/world.rs => crates/unified/src/config/world.rs +2 -2
@@ 13,14 13,14 @@ pub struct GlobalWorldConfig {
pub struct WorldConfig {
pub gravity: f32,
pub spawn_parts_at: String,
- pub spawn_parts_interval_secs: f32
+ pub spawn_parts_interval_secs: f32,
}
#[derive(Deserialize, Asset, TypePath, Clone)]
pub struct PartConfig {
pub default_width: f32,
pub default_height: f32,
- pub default_mass: f32
+ pub default_mass: f32,
}
#[derive(Deserialize, Asset, TypePath, Clone)]
M crates/unified/src/ecs.rs => crates/unified/src/ecs.rs +8 -3
@@ 21,7 21,6 @@ pub struct StarfieldBack;
#[derive(Resource, Default)]
pub struct CursorWorldCoordinates(pub Option<Vec2>);
-
#[derive(Debug, Deserialize, Event, Serialize)]
pub enum ThrustEvent {
Up(bool),
@@ 31,7 30,13 @@ pub enum ThrustEvent {
}
#[derive(Component, Serialize, Deserialize, Debug)]
-#[require(ReadMassProperties, RigidBody::Dynamic, ExternalForce, ExternalImpulse, Replicated)]
+#[require(
+ ReadMassProperties,
+ RigidBody::Dynamic,
+ ExternalForce,
+ ExternalImpulse,
+ Replicated
+)]
pub struct Part {
pub sprite: String,
pub width: f32,
@@ 68,4 73,4 @@ pub struct Particles {
}
#[derive(Serialize, Deserialize, Event, Debug, MapEntities, Clone)]
-pub struct DragRequestEvent(#[entities] pub Entity, pub Vec2);>
\ No newline at end of file
+pub struct DragRequestEvent(#[entities] pub Entity, pub Vec2);
M crates/unified/src/lib.rs => crates/unified/src/lib.rs +4 -4
@@ 15,16 15,16 @@ pub mod wasm_entrypoint;
#[cfg(target_arch = "wasm32")]
pub use wasm_entrypoint::*;
+pub mod attachment;
pub mod client;
pub mod client_plugins;
pub mod config;
-pub mod particles;
pub mod ecs;
+#[cfg(all(not(target_arch = "wasm32"), feature = "particle_editor"))]
+pub mod particle_editor;
+pub mod particles;
#[cfg(all(not(target_arch = "wasm32"), feature = "native"))]
pub mod server;
#[cfg(all(not(target_arch = "wasm32"), feature = "native"))]
pub mod server_plugins;
pub mod shared_plugins;
-#[cfg(all(not(target_arch = "wasm32"), feature = "particle_editor"))]
-pub mod particle_editor;
-pub mod attachment;>
\ No newline at end of file
M crates/unified/src/main.rs => crates/unified/src/main.rs +3 -4
@@ 29,7 29,7 @@ enum Cli {
max_clients: usize,
},
#[cfg(all(not(target_arch = "wasm32"), feature = "particle_editor"))]
- ParticleEditor {}
+ ParticleEditor {},
}
fn main() -> AppExit {
@@ 38,8 38,7 @@ fn main() -> AppExit {
tracing_subscriber::fmt()
.with_env_filter(
- EnvFilter::from_default_env()
- .add_directive(Directive::from_str("naga=error").unwrap())
+ EnvFilter::from_default_env().add_directive(Directive::from_str("naga=error").unwrap()),
)
.finish()
.init();
@@ 66,7 65,7 @@ fn main() -> AppExit {
max_clients,
});
app.add_plugins(SharedPluginGroup);
- },
+ }
#[cfg(all(not(target_arch = "wasm32"), feature = "particle_editor"))]
Cli::ParticleEditor {} => {
app.add_plugins(starkingdoms::particle_editor::particle_editor_plugin);
M crates/unified/src/particle_editor/ecs.rs => crates/unified/src/particle_editor/ecs.rs +11 -2
@@ 1,6 1,12 @@
use std::time::Duration;
-use bevy::{asset::Handle, ecs::component::Component, render::mesh::Mesh, sprite::ColorMaterial, time::{Timer, TimerMode}};
+use bevy::{
+ asset::Handle,
+ ecs::component::Component,
+ render::mesh::Mesh,
+ sprite::ColorMaterial,
+ time::{Timer, TimerMode},
+};
use crate::particles::ParticleEffect;
@@ 11,7 17,10 @@ pub struct Particle;
pub struct LifetimeTimer(pub Timer);
impl LifetimeTimer {
pub fn new(lifetime: f32) -> LifetimeTimer {
- LifetimeTimer(Timer::new(Duration::from_secs_f32(lifetime), TimerMode::Once))
+ LifetimeTimer(Timer::new(
+ Duration::from_secs_f32(lifetime),
+ TimerMode::Once,
+ ))
}
}
M crates/unified/src/particle_editor/hooks.rs => crates/unified/src/particle_editor/hooks.rs +6 -3
@@ 11,8 11,11 @@ fn init_particle_effect(
particle_effect: Query<(Entity, &ParticleEffect), Added<ParticleEffect>>,
) {
for (entity, effect) in particle_effect {
- commands.get_entity(entity).unwrap().insert(
- SpawnDelayTimer::new(effect.batch_spawn_delay_seconds.sample(&mut rand::rng()))
- );
+ commands
+ .get_entity(entity)
+ .unwrap()
+ .insert(SpawnDelayTimer::new(
+ effect.batch_spawn_delay_seconds.sample(&mut rand::rng()),
+ ));
}
}
M crates/unified/src/particle_editor/mod.rs => crates/unified/src/particle_editor/mod.rs +230 -157
@@ 1,14 1,17 @@
-use std::collections::BTreeMap;
+use crate::{
+ particle_editor::{hooks::hooks_plugin, spawn::spawn_plugin},
+ particles::{LifetimeCurve, ParticleEffect, RandF32, RandUsize, RandVec2},
+};
use bevy::prelude::*;
-use bevy_egui::{egui, EguiContexts, EguiPlugin, EguiPrimaryContextPass};
+use bevy_egui::{EguiContexts, EguiPlugin, EguiPrimaryContextPass, egui};
use bevy_rapier2d::plugin::{NoUserData, RapierPhysicsPlugin};
use ordered_float::OrderedFloat;
use ron::ser::PrettyConfig;
-use crate::{particle_editor::{hooks::hooks_plugin, spawn::spawn_plugin}, particles::{LifetimeCurve, ParticleEffect, RandF32, RandUsize, RandVec2}};
+use std::collections::BTreeMap;
-mod spawn;
-mod hooks;
mod ecs;
+mod hooks;
+mod spawn;
pub fn particle_editor_plugin(app: &mut App) {
app.add_plugins(DefaultPlugins);
@@ 26,40 29,46 @@ pub fn particle_editor_plugin(app: &mut App) {
add_scale_v: 0.0,
add_color_t: 0.0,
add_color_v: [0u8; 4],
- scale_curve: LifetimeCurve::new(&[
- (0.0f32, 5.0),
- (2.0, 0.0)
- ]).0.iter().map(|u| (u.0.clone(), u.1.clone())).collect::<Vec<_>>(),
- color_curve: vec![(OrderedFloat(0.0f32), [255, 0, 0, 255])]
+ scale_curve: LifetimeCurve::new(&[(0.0f32, 5.0), (2.0, 0.0)])
+ .0
+ .iter()
+ .map(|u| (u.0.clone(), u.1.clone()))
+ .collect::<Vec<_>>(),
+ color_curve: vec![(OrderedFloat(0.0f32), [255, 0, 0, 255])],
});
}
-fn setup_editor_effect(
- mut commands: Commands,
-) {
+fn setup_editor_effect(mut commands: Commands) {
commands.spawn((
ParticleEffect {
lifetime_seconds: RandF32 {
value: 2.0,
randomness: 0.1,
},
- batch_spawn_delay_seconds: RandF32 { value: 0.1, randomness: 0.05 },
+ batch_spawn_delay_seconds: RandF32 {
+ value: 0.1,
+ randomness: 0.05,
+ },
particles_in_batch: RandUsize {
value: 1,
- randomness: 1
+ randomness: 1,
},
initial_linear_velocity: RandVec2 {
- x: RandF32 { value: 0.0, randomness: 0.5 },
- y: RandF32 { value: 10.0, randomness: 1.0 }
+ x: RandF32 {
+ value: 0.0,
+ randomness: 0.5,
+ },
+ y: RandF32 {
+ value: 10.0,
+ randomness: 1.0,
+ },
},
- initial_angular_velocity: RandF32 { value: 1.0, randomness: 0.5 },
- scale: LifetimeCurve::new(&[
- (0.0f32, 5.0),
- (2.0, 0.0)
- ]),
- color: LifetimeCurve::new(&[
- (0.0f32, Srgba::new(1.0, 0.0, 0.0, 1.0).into()),
- ]),
+ initial_angular_velocity: RandF32 {
+ value: 1.0,
+ randomness: 0.5,
+ },
+ scale: LifetimeCurve::new(&[(0.0f32, 5.0), (2.0, 0.0)]),
+ color: LifetimeCurve::new(&[(0.0f32, Srgba::new(1.0, 0.0, 0.0, 1.0).into())]),
},
Transform::from_xyz(0.0, 0.0, 0.0),
));
@@ 81,149 90,213 @@ struct EditorResource {
add_color_v: [u8; 4],
scale_curve: Vec<(OrderedFloat<f32>, f32)>,
- color_curve: Vec<(OrderedFloat<f32>, [u8; 4])>
+ color_curve: Vec<(OrderedFloat<f32>, [u8; 4])>,
}
-
-fn editor_ui(mut contexts: EguiContexts, effect: Single<&mut ParticleEffect>, mut editor_resource: ResMut<EditorResource>) -> Result {
+fn editor_ui(
+ mut contexts: EguiContexts,
+ effect: Single<&mut ParticleEffect>,
+ mut editor_resource: ResMut<EditorResource>,
+) -> Result {
let mut effect = effect.into_inner();
- egui::Window::new("Particle Effect").resizable(false).show(contexts.ctx_mut()?, |ui| {
- egui::Grid::new("effect").striped(true).show(ui, |ui| {
- draw_rand_f32(&mut effect.lifetime_seconds, "Lifetime (seconds): ", ui);
- draw_rand_f32(&mut effect.batch_spawn_delay_seconds, "Delay in between batches (seconds): ", ui);
- draw_rand_usize(&mut effect.particles_in_batch, "Number of particles in batch: ", ui);
-
- draw_rand_f32(&mut effect.initial_linear_velocity.x, "Linear velocity (x-axis, m/s): ", ui);
- draw_rand_f32(&mut effect.initial_linear_velocity.y, "Linear velocity (y-axis, m/s): ", ui);
- draw_rand_f32(&mut effect.initial_angular_velocity, "Angular velocity (radians/second): ", ui);
-
- ui.separator();
- ui.label("Scale curve");
- if ui.button("sort").clicked() {
- editor_resource.scale_curve.sort_by_key(|u|u.0);
- }
- ui.end_row();
-
- editor_resource.scale_curve.retain_mut(|(k, v)| {
- ui.label("scale t=");
- ui.add(egui::DragValue::new(k.as_mut()).speed(0.01).range(0.0f32..=effect.lifetime_seconds.value));
+ egui::Window::new("Particle Effect")
+ .resizable(false)
+ .show(contexts.ctx_mut()?, |ui| {
+ egui::Grid::new("effect").striped(true).show(ui, |ui| {
+ draw_rand_f32(&mut effect.lifetime_seconds, "Lifetime (seconds): ", ui);
+ draw_rand_f32(
+ &mut effect.batch_spawn_delay_seconds,
+ "Delay in between batches (seconds): ",
+ ui,
+ );
+ draw_rand_usize(
+ &mut effect.particles_in_batch,
+ "Number of particles in batch: ",
+ ui,
+ );
+
+ draw_rand_f32(
+ &mut effect.initial_linear_velocity.x,
+ "Linear velocity (x-axis, m/s): ",
+ ui,
+ );
+ draw_rand_f32(
+ &mut effect.initial_linear_velocity.y,
+ "Linear velocity (y-axis, m/s): ",
+ ui,
+ );
+ draw_rand_f32(
+ &mut effect.initial_angular_velocity,
+ "Angular velocity (radians/second): ",
+ ui,
+ );
+
+ ui.separator();
+ ui.label("Scale curve");
+ if ui.button("sort").clicked() {
+ editor_resource.scale_curve.sort_by_key(|u| u.0);
+ }
+ ui.end_row();
+
+ editor_resource.scale_curve.retain_mut(|(k, v)| {
+ ui.label("scale t=");
+ ui.add(
+ egui::DragValue::new(k.as_mut())
+ .speed(0.01)
+ .range(0.0f32..=effect.lifetime_seconds.value),
+ );
+ ui.label("v=");
+ ui.add(egui::DragValue::new(v).speed(0.01));
+ let r = ui.button("-");
+ ui.end_row();
+ !r.clicked()
+ });
+
+ ui.separator();
+ ui.end_row();
+
+ ui.label("new scale: t=");
+ ui.add(
+ egui::DragValue::new(&mut editor_resource.add_scale_t)
+ .speed(0.01)
+ .range(0.0f32..=effect.lifetime_seconds.value),
+ );
ui.label("v=");
- ui.add(egui::DragValue::new(v).speed(0.01));
- let r = ui.button("-");
+ ui.add(egui::DragValue::new(&mut editor_resource.add_scale_v).speed(0.01));
+ if ui.button("+").clicked() {
+ let new_v = (
+ OrderedFloat(editor_resource.add_scale_t),
+ editor_resource.add_scale_v,
+ )
+ .clone();
+ editor_resource.scale_curve.push(new_v);
+ }
ui.end_row();
- !r.clicked()
- });
- ui.separator();
- ui.end_row();
-
- ui.label("new scale: t=");
- ui.add(egui::DragValue::new(&mut editor_resource.add_scale_t).speed(0.01).range(0.0f32..=effect.lifetime_seconds.value));
- ui.label("v=");
- ui.add(egui::DragValue::new(&mut editor_resource.add_scale_v).speed(0.01));
- if ui.button("+").clicked() {
- let new_v = (OrderedFloat(editor_resource.add_scale_t), editor_resource.add_scale_v).clone();
- editor_resource.scale_curve.push(new_v);
- }
- ui.end_row();
-
- effect.scale = LifetimeCurve(BTreeMap::from_iter(editor_resource.scale_curve.iter().copied()));
-
- ui.separator();
- ui.end_row();
-
- ui.separator();
- ui.label("Color curve");
- if ui.button("sort").clicked() {
- editor_resource.color_curve.sort_by_key(|u|u.0);
- }
- ui.end_row();
-
- editor_resource.color_curve.retain_mut(|(k, v)| {
- ui.label("color t=");
- ui.add(egui::DragValue::new(k.as_mut()).speed(0.01).range(0.0f32..=effect.lifetime_seconds.value));
+ effect.scale = LifetimeCurve(BTreeMap::from_iter(
+ editor_resource.scale_curve.iter().copied(),
+ ));
+
+ ui.separator();
+ ui.end_row();
+
+ ui.separator();
+ ui.label("Color curve");
+ if ui.button("sort").clicked() {
+ editor_resource.color_curve.sort_by_key(|u| u.0);
+ }
+ ui.end_row();
+
+ editor_resource.color_curve.retain_mut(|(k, v)| {
+ ui.label("color t=");
+ ui.add(
+ egui::DragValue::new(k.as_mut())
+ .speed(0.01)
+ .range(0.0f32..=effect.lifetime_seconds.value),
+ );
+ ui.label("v=");
+ ui.color_edit_button_srgba_unmultiplied(v);
+ let r = ui.button("-");
+ ui.end_row();
+ !r.clicked()
+ });
+
+ ui.separator();
+ ui.end_row();
+
+ ui.label("new color: t=");
+ ui.add(
+ egui::DragValue::new(&mut editor_resource.add_color_t)
+ .speed(0.01)
+ .range(0.0f32..=effect.lifetime_seconds.value),
+ );
ui.label("v=");
- ui.color_edit_button_srgba_unmultiplied(v);
- let r = ui.button("-");
+ ui.color_edit_button_srgba_unmultiplied(&mut editor_resource.add_color_v);
+ if ui.button("+").clicked() {
+ let new_v = (
+ OrderedFloat(editor_resource.add_color_t),
+ editor_resource.add_color_v,
+ )
+ .clone();
+ editor_resource.color_curve.push(new_v);
+ }
ui.end_row();
- !r.clicked()
- });
- ui.separator();
- ui.end_row();
-
- ui.label("new color: t=");
- ui.add(egui::DragValue::new(&mut editor_resource.add_color_t).speed(0.01).range(0.0f32..=effect.lifetime_seconds.value));
- ui.label("v=");
- ui.color_edit_button_srgba_unmultiplied(&mut editor_resource.add_color_v);
- if ui.button("+").clicked() {
- let new_v = (OrderedFloat(editor_resource.add_color_t), editor_resource.add_color_v).clone();
- editor_resource.color_curve.push(new_v);
- }
- ui.end_row();
-
- let curve_copied = editor_resource.color_curve.clone();
- effect.color = LifetimeCurve(BTreeMap::from_iter(curve_copied.iter().map(
- |(u, v)| (
- *u,
- Color::Srgba(Srgba::new(
- v[0] as f32 / 256.0,
- v[1] as f32 / 256.0,
- v[2] as f32 / 256.0,
- v[3] as f32 / 256.0
- ))
- )
- )));
-
- ui.separator();
- ui.end_row();
- });
- ui.horizontal(|ui| {
- if ui.button("Generate").clicked() {
- effect.scale = LifetimeCurve(BTreeMap::from_iter(editor_resource.scale_curve.iter().copied()));
let curve_copied = editor_resource.color_curve.clone();
- effect.color = LifetimeCurve(BTreeMap::from_iter(curve_copied.iter().map(
- |(u, v)| (
- *u,
- Color::Srgba(Srgba::new(
- v[0] as f32 / 256.0,
- v[1] as f32 / 256.0,
- v[2] as f32 / 256.0,
- v[3] as f32 / 256.0
- ))
- )
- )));
-
- editor_resource.ser_field = ron::ser::to_string(effect.as_ref()).unwrap();
- editor_resource.status = "Ready; Generated OK".to_string();
- }
- if ui.button("Load").clicked() {
- match ron::from_str(&editor_resource.ser_field) {
- Ok(e) => {
- *effect = e;
- editor_resource.scale_curve = effect.scale.0.iter().map(|u| (u.0.clone(), u.1.clone())).collect::<Vec<_>>();
- editor_resource.color_curve = effect.color.0.iter().map(|u| (
- u.0.clone(),
- {
- let mut r = [0u8; 4];
- let srgba: Srgba = (*u.1).into();
- r[0] = (srgba.red * 256.0).floor() as u8;
- r[1] = (srgba.green * 256.0).floor() as u8;
- r[2] = (srgba.blue * 256.0).floor() as u8;
- r[3] = (srgba.alpha * 256.0).floor() as u8;
- r
- }
- )).collect::<Vec<_>>();
- },
- Err(e) => {
- editor_resource.status = e.to_string();
- }
- };
- }
+ effect.color =
+ LifetimeCurve(BTreeMap::from_iter(curve_copied.iter().map(|(u, v)| {
+ (
+ *u,
+ Color::Srgba(Srgba::new(
+ v[0] as f32 / 256.0,
+ v[1] as f32 / 256.0,
+ v[2] as f32 / 256.0,
+ v[3] as f32 / 256.0,
+ )),
+ )
+ })));
+
+ ui.separator();
+ ui.end_row();
+ });
+ ui.horizontal(|ui| {
+ if ui.button("Generate").clicked() {
+ effect.scale = LifetimeCurve(BTreeMap::from_iter(
+ editor_resource.scale_curve.iter().copied(),
+ ));
+ let curve_copied = editor_resource.color_curve.clone();
+ effect.color =
+ LifetimeCurve(BTreeMap::from_iter(curve_copied.iter().map(|(u, v)| {
+ (
+ *u,
+ Color::Srgba(Srgba::new(
+ v[0] as f32 / 256.0,
+ v[1] as f32 / 256.0,
+ v[2] as f32 / 256.0,
+ v[3] as f32 / 256.0,
+ )),
+ )
+ })));
+
+ editor_resource.ser_field = ron::ser::to_string(effect.as_ref()).unwrap();
+ editor_resource.status = "Ready; Generated OK".to_string();
+ }
+ if ui.button("Load").clicked() {
+ match ron::from_str(&editor_resource.ser_field) {
+ Ok(e) => {
+ *effect = e;
+ editor_resource.scale_curve = effect
+ .scale
+ .0
+ .iter()
+ .map(|u| (u.0.clone(), u.1.clone()))
+ .collect::<Vec<_>>();
+ editor_resource.color_curve = effect
+ .color
+ .0
+ .iter()
+ .map(|u| {
+ (u.0.clone(), {
+ let mut r = [0u8; 4];
+ let srgba: Srgba = (*u.1).into();
+ r[0] = (srgba.red * 256.0).floor() as u8;
+ r[1] = (srgba.green * 256.0).floor() as u8;
+ r[2] = (srgba.blue * 256.0).floor() as u8;
+ r[3] = (srgba.alpha * 256.0).floor() as u8;
+ r
+ })
+ })
+ .collect::<Vec<_>>();
+ }
+ Err(e) => {
+ editor_resource.status = e.to_string();
+ }
+ };
+ }
+ });
+ ui.text_edit_multiline(&mut editor_resource.ser_field);
+ ui.text_edit_multiline(&mut editor_resource.status.as_str());
});
- ui.text_edit_multiline(&mut editor_resource.ser_field);
- ui.text_edit_multiline(&mut editor_resource.status.as_str());
- });
Ok(())
}
M crates/unified/src/particle_editor/spawn.rs => crates/unified/src/particle_editor/spawn.rs +48 -9
@@ 3,7 3,10 @@ use std::time::Duration;
use bevy::prelude::*;
use bevy_rapier2d::prelude::{RigidBody, Velocity};
-use crate::{particle_editor::ecs::{CircleMesh, LifetimeTimer, ParentEffect, Particle, SpawnDelayTimer}, particles::ParticleEffect};
+use crate::{
+ particle_editor::ecs::{CircleMesh, LifetimeTimer, ParentEffect, Particle, SpawnDelayTimer},
+ particles::ParticleEffect,
+};
pub fn spawn_plugin(app: &mut App) {
app.add_systems(Update, spawn_particles);
@@ 21,24 24,41 @@ fn spawn_particles(
delay_timer.0.tick(time.delta());
if delay_timer.0.just_finished() {
for _ in 0..effect.particles_in_batch.sample(&mut rand::rng()) {
- let circle = CircleMesh(meshes.add(Circle::new(1.0)),
- materials.add(effect.color.sample(effect.color.clamp_time(0.0).unwrap()).unwrap()));
+ let circle = CircleMesh(
+ meshes.add(Circle::new(1.0)),
+ materials.add(
+ effect
+ .color
+ .sample(effect.color.clamp_time(0.0).unwrap())
+ .unwrap(),
+ ),
+ );
commands.spawn((
RigidBody::Dynamic,
Particle,
- transform.with_scale(Vec3::splat(effect.scale.sample(effect.scale.clamp_time(0.0).unwrap()).unwrap())),
+ transform.with_scale(Vec3::splat(
+ effect
+ .scale
+ .sample(effect.scale.clamp_time(0.0).unwrap())
+ .unwrap(),
+ )),
Mesh2d(circle.0.clone()),
MeshMaterial2d(circle.1.clone()),
Velocity {
linvel: effect.initial_linear_velocity.sample(&mut rand::rng()),
angvel: effect.initial_angular_velocity.sample(&mut rand::rng()),
},
- LifetimeTimer(Timer::from_seconds(effect.lifetime_seconds.sample(&mut rand::rng()), TimerMode::Once)),
+ LifetimeTimer(Timer::from_seconds(
+ effect.lifetime_seconds.sample(&mut rand::rng()),
+ TimerMode::Once,
+ )),
circle,
ParentEffect(effect.clone()),
));
}
- delay_timer.0.set_duration(Duration::from_secs_f32(effect.batch_spawn_delay_seconds.sample(&mut rand::rng())));
+ delay_timer.0.set_duration(Duration::from_secs_f32(
+ effect.batch_spawn_delay_seconds.sample(&mut rand::rng()),
+ ));
delay_timer.0.reset();
}
}
@@ 46,15 66,34 @@ fn spawn_particles(
fn lifetime_particles(
mut commands: Commands,
- mut particles: Query<(Entity, &mut LifetimeTimer, &mut Transform, &CircleMesh, &ParentEffect), With<Particle>>,
+ mut particles: Query<
+ (
+ Entity,
+ &mut LifetimeTimer,
+ &mut Transform,
+ &CircleMesh,
+ &ParentEffect,
+ ),
+ With<Particle>,
+ >,
time: ResMut<Time>,
mut meshes: ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<ColorMaterial>>,
) {
for (entity, mut timer, mut transform, circle, parent) in &mut particles {
timer.0.tick(time.delta());
- transform.scale = Vec3::splat(parent.0.scale.sample(parent.0.scale.clamp_time(timer.0.elapsed_secs()).unwrap()).unwrap());
- materials.get_mut(&circle.1).unwrap().color = parent.0.color.sample(parent.0.color.clamp_time(timer.0.elapsed_secs()).unwrap()).unwrap();
+ transform.scale = Vec3::splat(
+ parent
+ .0
+ .scale
+ .sample(parent.0.scale.clamp_time(timer.0.elapsed_secs()).unwrap())
+ .unwrap(),
+ );
+ materials.get_mut(&circle.1).unwrap().color = parent
+ .0
+ .color
+ .sample(parent.0.color.clamp_time(timer.0.elapsed_secs()).unwrap())
+ .unwrap();
if timer.0.just_finished() {
commands.entity(entity).despawn();
meshes.remove(&circle.0);
M crates/unified/src/particles.rs => crates/unified/src/particles.rs +23 -17
@@ 1,16 1,15 @@
-use std::collections::BTreeMap;
-use std::ops::Bound::{Included, Excluded, Unbounded};
use bevy::color::{Color, LinearRgba};
use bevy::math::Vec2;
use bevy::prelude::Component;
use ordered_float::{FloatCore, OrderedFloat};
use rand::Rng;
use serde::{Deserialize, Serialize};
+use std::collections::BTreeMap;
+use std::ops::Bound::{Excluded, Included, Unbounded};
#[derive(Deserialize, Serialize, Component, Clone)]
pub struct ParticleEffect {
// -- lifetime / spawning -- //
-
/// Particle lifetime in seconds
pub lifetime_seconds: RandF32,
/// Delay inbetween each batch of particles spawned
@@ 19,7 18,6 @@ pub struct ParticleEffect {
pub particles_in_batch: RandUsize,
// -- velocity -- //
-
/// Initial linear velocity added to the particle's velocity when it is spawned
pub initial_linear_velocity: RandVec2,
/// Initial angular velocity added to the particle's rotation when it is spawned
@@ 39,15 37,22 @@ pub struct ParticleEffect {
#[derive(Serialize, Deserialize, Clone)]
pub struct LifetimeCurve<P: Lerp>(pub BTreeMap<OrderedFloat<f32>, P>);
impl<P: Lerp + Copy> LifetimeCurve<P> {
- pub fn new<'a>(points: impl IntoIterator<Item = &'a (f32, P)>) -> Self where P: 'a {
- Self(BTreeMap::from_iter(points.into_iter().map(|u| (OrderedFloat(u.0), u.1.clone()))))
+ pub fn new<'a>(points: impl IntoIterator<Item = &'a (f32, P)>) -> Self
+ where
+ P: 'a,
+ {
+ Self(BTreeMap::from_iter(
+ points.into_iter().map(|u| (OrderedFloat(u.0), u.1.clone())),
+ ))
}
/// Sample for the value at T. Returns None if the curve has no points, or if T is outside
/// the domain of the points specified.
pub fn sample(&self, at: f32) -> Option<P> {
- if self.0.is_empty() { return None; }
-
+ if self.0.is_empty() {
+ return None;
+ }
+
if self.0.len() == 1 && at == self.0.iter().nth(0).unwrap().0.0 {
return Some(*self.0.iter().nth(0).unwrap().1);
}
@@ 78,14 83,17 @@ impl<P: Lerp + Copy> LifetimeCurve<P> {
/// Given an input time value, use the ends of the spline's time to clamp
/// the input value.
pub fn clamp_time(&self, time: f32) -> Option<f32> {
- let Some(first) = self.0.iter().nth(0) else { return None; };
- let Some(last) = self.0.iter().last() else { return None; };
+ let Some(first) = self.0.iter().nth(0) else {
+ return None;
+ };
+ let Some(last) = self.0.iter().last() else {
+ return None;
+ };
Some(time.clamp(first.0.0, last.0.0))
}
}
-
pub trait Lerp {
fn lerp(&self, other: &Self, t: f32) -> Self;
}
@@ 102,31 110,29 @@ impl Lerp for Color {
}
}
-
#[derive(Deserialize, Serialize, Clone, Copy)]
pub struct RandF32 {
pub value: f32,
- pub randomness: f32
+ pub randomness: f32,
}
impl RandF32 {
pub fn sample(&self, rng: &mut impl Rng) -> f32 {
- rng.random_range(self.value-self.randomness .. self.value+self.randomness)
+ rng.random_range(self.value - self.randomness..self.value + self.randomness)
}
}
#[derive(Deserialize, Serialize, Clone, Copy)]
pub struct RandUsize {
pub value: usize,
- pub randomness: usize
+ pub randomness: usize,
}
impl RandUsize {
pub fn sample(&self, rng: &mut impl Rng) -> usize {
let lower_bound = self.value.checked_sub(self.randomness).unwrap_or(0);
- rng.random_range(lower_bound .. self.value+self.randomness)
+ rng.random_range(lower_bound..self.value + self.randomness)
}
}
-
#[derive(Deserialize, Serialize, Clone, Copy)]
pub struct RandVec2 {
pub x: RandF32,
M crates/unified/src/server/earth_parts.rs => crates/unified/src/server/earth_parts.rs +18 -16
@@ 1,34 1,33 @@
+use crate::config::planet::Planet;
+use crate::ecs::{Part, PartBundle};
+use crate::server::part::SpawnPartRequest;
+use crate::server::world_config::WorldConfigResource;
use bevy::app::App;
use bevy::math::Vec2;
+use bevy::prelude::*;
use bevy::prelude::{Commands, Query, Res, Transform};
use bevy::time::Time;
use bevy_rapier2d::dynamics::{AdditionalMassProperties, MassProperties};
use bevy_rapier2d::geometry::Collider;
use bevy_replicon::prelude::Replicated;
-use crate::config::planet::Planet;
-use crate::ecs::{Part, PartBundle};
-use crate::server::world_config::WorldConfigResource;
-use bevy::prelude::*;
-use crate::server::part::SpawnPart;
#[derive(Resource, Default)]
struct PartTimerRes {
- timer: Timer
+ timer: Timer,
}
-
pub fn spawn_parts_plugin(app: &mut App) {
app.init_resource::<PartTimerRes>()
.add_systems(Update, spawn_parts_on_earth);
}
-
pub fn spawn_parts_on_earth(
mut commands: Commands,
world_config: Res<WorldConfigResource>,
planets: Query<(&Transform, &Planet)>,
mut timer: ResMut<PartTimerRes>,
- time: Res<Time>
+ asset_server: Res<AssetServer>,
+ time: Res<Time>,
) {
let Some(wc) = &world_config.config else {
return;
@@ 41,17 40,20 @@ pub fn spawn_parts_on_earth(
timer.timer = Timer::from_seconds(wc.world.spawn_parts_interval_secs, TimerMode::Once);
// find earth
- let Some((spawn_planet_pos, spawn_planet)) = planets
- .iter()
- .find(|p| p.1.name == wc.hearty.spawn_at) else { return; };
+ let Some((spawn_planet_pos, spawn_planet)) =
+ planets.iter().find(|p| p.1.name == wc.hearty.spawn_at)
+ else {
+ return;
+ };
let angle = rand::random::<f32>() * std::f32::consts::TAU;
let offset = spawn_planet.radius + 150.0;
- let mut new_transform =
- Transform::from_xyz(angle.cos() * offset, angle.sin() * offset, 0.0);
+ let mut new_transform = Transform::from_xyz(angle.cos() * offset, angle.sin() * offset, 0.0);
new_transform.rotate_z(angle);
new_transform.translation += spawn_planet_pos.translation;
commands
- .spawn(SpawnPart("config/parts/chassis.part.toml".to_string()))
+ .spawn(SpawnPartRequest(
+ asset_server.load("config/parts/chassis.part.toml"),
+ ))
.insert(new_transform);
-}>
\ No newline at end of file
+}
M crates/unified/src/server/gravity.rs => crates/unified/src/server/gravity.rs +0 -2
@@ 18,8 18,6 @@ fn update_gravity(
return;
};
-
-
for (part_transform, part_mass, mut forces) in &mut part_query {
forces.force = Vec2::ZERO;
forces.torque = 0.0;
M crates/unified/src/server/mod.rs => crates/unified/src/server/mod.rs +5 -5
@@ 1,11 1,13 @@
+mod earth_parts;
mod gravity;
+mod part;
pub mod planets;
pub mod player;
mod world_config;
-mod earth_parts;
-mod part;
+use crate::server::earth_parts::spawn_parts_plugin;
use crate::server::gravity::newtonian_gravity_plugin;
+use crate::server::part::part_management_plugin;
use crate::server::planets::planets_plugin;
use crate::server::player::player_management_plugin;
use crate::server::world_config::world_config_plugin;
@@ 17,8 19,6 @@ use aeronet_websocket::server::WebSocketServer;
use bevy::prelude::*;
use bevy_replicon::prelude::Replicated;
use std::net::SocketAddr;
-use crate::server::earth_parts::spawn_parts_plugin;
-use crate::server::part::part_config_plugin;
pub struct ServerPlugin {
pub bind: SocketAddr,
@@ 47,7 47,7 @@ impl Plugin for ServerPlugin {
.add_plugins(newtonian_gravity_plugin)
.add_plugins(player_management_plugin)
.add_plugins(spawn_parts_plugin)
- .add_plugins(part_config_plugin);
+ .add_plugins(part_management_plugin);
}
}
impl ServerPlugin {
M crates/unified/src/server/part.rs => crates/unified/src/server/part.rs +4 -134
@@ 1,138 1,8 @@
-use bevy::asset::Handle;
+use crate::config::world::PartConfig;
+use bevy::prelude::Component;
use bevy::prelude::*;
-use bevy_rapier2d::prelude::{AdditionalMassProperties, Collider};
-use bevy_replicon::prelude::Replicated;
-use crate::attachment::{Joint, JointId, JointOf, JointSnapFor, JointSnaps, Joints};
-use crate::config::part::{JointOffset, PartConfig};
-use crate::ecs::Part;
-pub fn part_config_plugin(app: &mut App) {
- app.add_systems(Update, handle_spawn_part_requests)
- // delay 1 tick
- .add_systems(PreUpdate, update_part_requests);
-}
+pub fn part_management_plugin(app: &mut App) {}
-#[derive(Component, Debug)]
-#[require(Transform, Replicated)]
-pub struct SpawnPart(pub String);
#[derive(Component)]
-struct LoadingPart(Handle<PartConfig>);
-#[derive(Component, Debug)]
-struct PartType(AssetId<PartConfig>);
-#[derive(Component)]
-/// STOP DELETING MY ASSET BEVY
-struct LiveConfigHandle(Handle<PartConfig>);
-
-// watch for SpawnPart components and start loading their config files
-fn handle_spawn_part_requests(new_parts: Query<(Entity, &SpawnPart), (With<GlobalTransform>)>, mut commands: Commands, asset_server: Res<AssetServer>, assets: Res<Assets<PartConfig>>, parts: Query<(&Joints, &JointSnaps), With<Part>>,transform: Query<&GlobalTransform>) {
- for (new_part, request) in &new_parts {
- trace!(?new_part, ?request, "answering part request");
-
- let hdl: Handle<PartConfig> = asset_server.load(request.0.clone());
-
- commands.entity(new_part)
- .insert(LiveConfigHandle(hdl.clone()))
- .remove::<SpawnPart>();
-
- if let Some(cfg) = assets.get(&hdl) {
- spawn_part(commands.reborrow(), new_part, cfg, &hdl.id(), parts, transform,false);
- } else {
- commands.entity(new_part)
- .insert(LoadingPart(hdl.clone()));
- }
- }
-}
-fn update_part_requests(
- mut ev_config: EventReader<AssetEvent<PartConfig>>,
- loading_parts: Query<(Entity, &LoadingPart)>,
- existing_parts: Query<(Entity, &PartType)>,
- mut assets: ResMut<Assets<PartConfig>>,
- mut commands: Commands,
- parts: Query<(&Joints, &JointSnaps), With<Part>>,
- transform: Query<&GlobalTransform>
-) {
- for ev in ev_config.read() {
- match ev {
- AssetEvent::Added { id } => {
- trace!(?id, "asset added");
- for (loading_part, req) in &loading_parts {
- if req.0.id() == *id {
- let Some(asset) = assets.get(*id) else { continue; };
-
- spawn_part(commands.reborrow(), loading_part, asset, id, parts, transform,false);
- }
- }
- },
- AssetEvent::Modified { id } => {
- trace!(?id, "updating part");
- for (existing_part, ptype) in &existing_parts {
- if ptype.0 == *id {
- let Some(asset) = assets.get(ptype.0) else { continue; };
- spawn_part(commands.reborrow(), existing_part, asset, id, parts, transform,true);
- }
- }
- }
- _ => {}
- }
- }
-}
-
-fn spawn_part(mut commands: Commands, entity: Entity, part: &PartConfig, id: &AssetId<PartConfig>, parts: Query<(&Joints, &JointSnaps), With<Part>>, transform: Query<&GlobalTransform>, is_update: bool) {
- commands.entity(entity)
- .remove::<LoadingPart>()
- .insert(Part {
- sprite: part.part.sprite_disconnected.clone(),
- width: part.physics.width,
- height: part.physics.height,
- mass: part.physics.mass,
- })
- .insert(Collider::cuboid(part.physics.width / 2.0, part.physics.height / 2.0))
- .insert(AdditionalMassProperties::Mass(part.physics.mass))
- .insert(PartType(*id))
- .insert(Replicated);
-
- for joint in &part.joints {
- if is_update {
- // find all entities
- for (joints, snaps) in &parts {
- for joint_entity in joints.iter() {
- commands.entity(joint_entity).insert((
- ChildOf(entity),
- Joint {
- id: JointId::from_part_and_joint_id(part.part.name.clone(), joint.id.clone()),
- transform: joint.target.into()
- },
- <JointOffset as Into<Transform>>::into(joint.target),
- JointOf(entity),
- Replicated,
- ));
- }
- for snap_entity in snaps.iter() {
- commands.entity(snap_entity).insert((
- <JointOffset as Into<Transform>>::into(joint.snap),
- ));
- }
- }
- } else {
- let e = commands.spawn((
- ChildOf(entity),
- Joint {
- id: JointId::from_part_and_joint_id(part.part.name.clone(), joint.id.clone()),
- transform: joint.target.into()
- },
- <JointOffset as Into<Transform>>::into(joint.target),
- JointOf(entity),
- Replicated,
- )).id();
- trace!(?e, "spawned joint");
-
- let e = commands.spawn((
- ChildOf(entity),
- JointSnapFor(e),
- <JointOffset as Into<Transform>>::into(joint.snap),
- Replicated,
- )).id();
- trace!(?e, "spawned jointsnap");
- }
- }
-}>
\ No newline at end of file
+pub struct SpawnPartRequest(pub Handle<PartConfig>);
M crates/unified/src/server/player.rs => crates/unified/src/server/player.rs +23 -13
@@ 1,7 1,10 @@
use std::f32::consts::PI;
use crate::config::planet::Planet;
-use crate::ecs::{DragRequestEvent, Part, PartBundle, Particles, Player, PlayerThrust, ThrustEvent};
+use crate::ecs::{
+ DragRequestEvent, Part, PartBundle, Particles, Player, PlayerThrust, ThrustEvent,
+};
+use crate::server::part::SpawnPartRequest;
use crate::server::world_config::WorldConfigResource;
use crate::server::{ConnectedGameEntity, ConnectedNetworkEntity};
use bevy::prelude::*;
@@ 9,14 12,15 @@ use bevy_rapier2d::prelude::{
AdditionalMassProperties, Collider, ExternalForce, ExternalImpulse, MassProperties,
};
use bevy_replicon::prelude::{FromClient, Replicated};
-use crate::server::part::{SpawnPart};
pub fn player_management_plugin(app: &mut App) {
- app
- .add_systems(Update, (handle_new_players, player_thrust, dragging));
+ app.add_systems(Update, (handle_new_players, player_thrust, dragging));
}
-fn dragging(mut events: EventReader<FromClient<DragRequestEvent>>, mut parts: Query<&mut Transform, With<Part>>) {
+fn dragging(
+ mut events: EventReader<FromClient<DragRequestEvent>>,
+ mut parts: Query<&mut Transform, With<Part>>,
+) {
for event in events.read() {
let mut part = parts.get_mut(event.event.0).unwrap();
part.translation.x = event.event.1.x;
@@ 31,7 35,7 @@ fn handle_new_players(
planets: Query<(&Transform, &Planet)>,
asset_server: Res<AssetServer>,
gt: Query<&GlobalTransform>,
- child_of: Query<&ChildOf>
+ child_of: Query<&ChildOf>,
) {
let Some(wc) = &world_config.config else {
return;
@@ 42,7 46,12 @@ fn handle_new_players(
let (spawn_planet_pos, spawn_planet) = planets
.iter()
.find(|p| p.1.name == wc.hearty.spawn_at)
- .unwrap_or_else(|| panic!("spawn planet {} is missing? (check that the planet is named exactly '{}')", wc.hearty.spawn_at, wc.hearty.spawn_at));
+ .unwrap_or_else(|| {
+ panic!(
+ "spawn planet {} is missing? (check that the planet is named exactly '{}')",
+ wc.hearty.spawn_at, wc.hearty.spawn_at
+ )
+ });
let angle = rand::random::<f32>() * std::f32::consts::TAU;
let offset = spawn_planet.radius + 150.0;
let mut new_transform =
@@ 52,14 61,15 @@ fn handle_new_players(
info!(?new_transform, ?joined_player, "set player's position!");
- commands.entity(joined_player)
- .insert(new_transform);
-
- commands.entity(joined_player)
- .insert(SpawnPart("config/parts/hearty.part.toml".to_string()))
+ commands
+ .entity(joined_player)
+ .insert(new_transform)
+ .insert(SpawnPartRequest(
+ asset_server.load("config/parts/hearty.part.toml"),
+ ))
.insert(PlayerThrust::default())
.insert(Player {
- client: joined_player
+ client: joined_player,
});
}
}
M crates/unified/src/server_plugins.rs => crates/unified/src/server_plugins.rs +8 -10
@@ 1,19 1,21 @@
+use crate::config::part::PartConfig;
use crate::config::planet::PlanetConfigCollection;
-use crate::config::world::{GlobalWorldConfig};
+use crate::config::world::GlobalWorldConfig;
use aeronet_replicon::server::AeronetRepliconServerPlugin;
use aeronet_websocket::server::WebSocketServerPlugin;
-use bevy::app::{App, PluginGroup, PluginGroupBuilder, ScheduleRunnerPlugin, Startup, TaskPoolPlugin};
+use bevy::app::{
+ App, PluginGroup, PluginGroupBuilder, ScheduleRunnerPlugin, Startup, TaskPoolPlugin,
+};
use bevy::asset::AssetPlugin;
use bevy::diagnostic::FrameCountPlugin;
+use bevy::math::Vec2;
+use bevy::prelude::Query;
use bevy::time::TimePlugin;
use bevy_common_assets::toml::TomlAssetPlugin;
+use bevy_rapier2d::plugin::{NoUserData, RapierConfiguration, RapierPhysicsPlugin};
use bevy_replicon::RepliconPlugins;
use std::net::SocketAddr;
use std::time::Duration;
-use bevy::math::Vec2;
-use bevy::prelude::Query;
-use bevy_rapier2d::plugin::{NoUserData, RapierConfiguration, RapierPhysicsPlugin};
-use crate::config::part::PartConfig;
pub struct ServerPluginGroup {
pub bind: SocketAddr,
@@ 32,18 34,14 @@ impl PluginGroup for ServerPluginGroup {
.add_group(RepliconPlugins)
.add(WebSocketServerPlugin)
.add(AeronetRepliconServerPlugin)
-
/* Assets */
.add(AssetPlugin::default())
.add(TomlAssetPlugin::<GlobalWorldConfig>::new(&["wc.toml"]))
.add(TomlAssetPlugin::<PlanetConfigCollection>::new(&["pc.toml"]))
.add(TomlAssetPlugin::<PartConfig>::new(&["part.toml"]))
-
.add(crate::server::ServerPlugin {
bind: self.bind,
max_clients: self.max_clients,
})
}
}
-
-
M crates/unified/src/shared_plugins.rs => crates/unified/src/shared_plugins.rs +2 -5
@@ 1,10 1,10 @@
+use crate::attachment::{Joint, JointOf, JointSnapFor, PartInShip, Peer, Ship};
use crate::config::planet::Planet;
use crate::ecs::{DragRequestEvent, Part, Particles, Player, ThrustEvent};
use bevy::app::{App, PluginGroup, PluginGroupBuilder};
use bevy::prelude::*;
use bevy_rapier2d::prelude::*;
use bevy_replicon::prelude::{AppRuleExt, Channel, ClientEventAppExt};
-use crate::attachment::{Joint, JointOf, JointSnapFor, PartInShip, Peer, Ship};
pub struct SharedPluginGroup;
@@ 13,14 13,12 @@ impl PluginGroup for SharedPluginGroup {
PluginGroupBuilder::start::<Self>()
.add(RapierPhysicsPlugin::<NoUserData>::pixels_per_meter(100.0))
.add(physics_setup_plugin)
-
.add(register_everything)
}
}
pub fn register_everything(app: &mut App) {
- app
- .add_client_event::<ThrustEvent>(Channel::Ordered)
+ app.add_client_event::<ThrustEvent>(Channel::Ordered)
.add_mapped_client_event::<DragRequestEvent>(Channel::Ordered)
.replicate::<Transform>()
.replicate::<GlobalTransform>()
@@ 31,7 29,6 @@ pub fn register_everything(app: &mut App) {
.replicate::<Player>()
.replicate::<Particles>()
.replicate::<ChildOf>()
-
.replicate::<Ship>()
.replicate::<PartInShip>()
.replicate::<Joint>()