~starkingdoms/starkingdoms

0f4a51dd91df5feb0706a11e9d977d9d70608a05 — ghostly_zsh 5 months ago e2fa180
particles are back (cmon git)
M crates/unified/src/client/mod.rs => crates/unified/src/client/mod.rs +1 -0
@@ 7,6 7,7 @@ mod starfield;
mod ui;
mod planet;
mod zoom;
mod particles;

use crate::client::incoming_particles::replicated_particles_plugin;
use crate::client::incoming_parts::incoming_parts_plugin;

M crates/unified/src/client/particles/mod.rs => crates/unified/src/client/particles/mod.rs +9 -0
@@ 0,0 1,9 @@
use bevy::app::{App, Plugin};

pub struct ParticlePlugin;

impl Plugin for ParticlePlugin {
    fn build(&self, app: &mut App) {
        
    }
}

A crates/unified/src/particle_editor/ecs.rs => crates/unified/src/particle_editor/ecs.rs +30 -0
@@ 0,0 1,30 @@
use std::time::Duration;

use bevy::{asset::Handle, ecs::component::Component, render::mesh::Mesh, sprite::ColorMaterial, time::{Timer, TimerMode}};

use crate::particles::ParticleEffect;

#[derive(Component)]
pub struct Particle;

#[derive(Component)]
pub struct LifetimeTimer(pub Timer);
impl LifetimeTimer {
    pub fn new(lifetime: f32) -> LifetimeTimer {
        LifetimeTimer(Timer::new(Duration::from_secs_f32(lifetime), TimerMode::Once))
    }
}

#[derive(Component)]
pub struct SpawnDelayTimer(pub Timer);
impl SpawnDelayTimer {
    pub fn new(delay: f32) -> SpawnDelayTimer {
        SpawnDelayTimer(Timer::new(Duration::from_secs_f32(delay), TimerMode::Once))
    }
}

#[derive(Component)]
pub struct CircleMesh(pub Handle<Mesh>, pub Handle<ColorMaterial>);

#[derive(Component)]
pub struct ParentEffect(pub ParticleEffect);

A crates/unified/src/particle_editor/hooks.rs => crates/unified/src/particle_editor/hooks.rs +18 -0
@@ 0,0 1,18 @@
use bevy::prelude::*;

use crate::{particle_editor::ecs::SpawnDelayTimer, particles::ParticleEffect};

pub fn hooks_plugin(app: &mut App) {
    app.add_systems(Update, init_particle_effect);
}

fn init_particle_effect(
    mut commands: Commands,
    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()))
        );
    }
}

M crates/unified/src/particle_editor/mod.rs => crates/unified/src/particle_editor/mod.rs +13 -4
@@ 1,16 1,24 @@
use std::collections::BTreeMap;
use bevy::prelude::*;
use bevy_egui::{egui, EguiContexts, EguiPlugin, EguiPrimaryContextPass};
use bevy_rapier2d::plugin::{NoUserData, RapierPhysicsPlugin};
use ordered_float::OrderedFloat;
use ron::ser::PrettyConfig;
use crate::particles::{LifetimeCurve, ParticleEffect, RandF32, RandUsize, RandVec2};
use crate::{particle_editor::{hooks::hooks_plugin, spawn::spawn_plugin}, particles::{LifetimeCurve, ParticleEffect, RandF32, RandUsize, RandVec2}};

mod spawn;
mod hooks;
mod ecs;

pub fn particle_editor_plugin(app: &mut App) {
    app.add_plugins(DefaultPlugins);
    app.add_plugins(EguiPlugin::default());
    app.add_plugins(RapierPhysicsPlugin::<NoUserData>::pixels_per_meter(10.0));
    app.add_systems(Startup, setup_camera_system);
    app.add_systems(EguiPrimaryContextPass, editor_ui);
    app.add_systems(Startup, setup_editor_effect);
    app.add_plugins(spawn_plugin);
    app.add_plugins(hooks_plugin);
    app.insert_resource(EditorResource {
        ser_field: String::new(),
        status: "Ready".to_string(),


@@ 50,12 58,13 @@ fn setup_editor_effect(mut commands: Commands) {
            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),
    ));
}

fn setup_camera_system(mut commands: Commands) {
    commands.spawn(Camera2d);
    commands.spawn((Camera2d, Transform::from_scale(Vec3::splat(0.1))));
}

#[derive(Resource)]


@@ 229,4 238,4 @@ fn draw_rand_usize(v: &mut RandUsize, l: &str, ui: &mut egui::Ui) {
    ui.label("variance:");
    ui.add(egui::DragValue::new(&mut v.randomness).speed(0.1));
    ui.end_row();
}
\ No newline at end of file
}

A crates/unified/src/particle_editor/spawn.rs => crates/unified/src/particle_editor/spawn.rs +61 -0
@@ 0,0 1,61 @@
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};

pub fn spawn_plugin(app: &mut App) {
    app.add_systems(Update, spawn_particles);
    app.add_systems(Update, lifetime_particles);
}

fn spawn_particles(
    mut commands: Commands,
    particle_effects: Query<(&Transform, &ParticleEffect, &mut SpawnDelayTimer)>,
    time: ResMut<Time>,
    mut meshes: ResMut<Assets<Mesh>>,
    mut materials: ResMut<Assets<ColorMaterial>>,
) {
    for (transform, effect, mut delay_timer) in particle_effects {
        delay_timer.0.tick(time.delta());
        let circle = CircleMesh(meshes.add(Circle::new(1.0)),
            materials.add(effect.color.sample(effect.color.clamp_time(0.0).unwrap()).unwrap()));
        if delay_timer.0.just_finished() {
            commands.spawn((
                RigidBody::Dynamic,
                Particle,
                transform.with_scale(Vec3::splat(1.0)),
                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)),
                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.reset();
        }
    }
}

fn lifetime_particles(
    mut commands: Commands,
    mut particles: Query<(Entity, &mut LifetimeTimer, &CircleMesh, &ParentEffect), With<Particle>>,
    time: ResMut<Time>,
    mut meshes: ResMut<Assets<Mesh>>,
    mut materials: ResMut<Assets<ColorMaterial>>,
) {
    for (entity, mut timer, circle, parent) in &mut particles {
        timer.0.tick(time.delta());
        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);
            materials.remove(&circle.1);
        }
    }
}

M crates/unified/src/particles.rs => crates/unified/src/particles.rs +12 -5
@@ 8,7 8,7 @@ use ordered_float::{FloatCore, OrderedFloat};
use rand::Rng;
use serde::{Deserialize, Serialize};

#[derive(Deserialize, Serialize, Component)]
#[derive(Deserialize, Serialize, Component, Clone)]
pub struct ParticleEffect {
    // -- lifetime / spawning -- //



@@ 37,7 37,7 @@ pub struct ParticleEffect {
    pub color: LifetimeCurve<Color>,
}

#[derive(Serialize, Deserialize)]
#[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 {


@@ 63,6 63,13 @@ impl<P: Lerp + Copy> LifetimeCurve<P> {
        // Lerp
        Some(a_val.lerp(b_val, t))
    }

    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 };

        Some(time.clamp(first.0.0, last.0.0))
    }
}




@@ 83,7 90,7 @@ impl Lerp for Color {
}


#[derive(Deserialize, Serialize)]
#[derive(Deserialize, Serialize, Clone, Copy)]
pub struct RandF32 {
    pub value: f32,
    pub randomness: f32


@@ 94,7 101,7 @@ impl RandF32 {
    }
}

#[derive(Deserialize, Serialize)]
#[derive(Deserialize, Serialize, Clone, Copy)]
pub struct RandUsize {
    pub value: usize,
    pub randomness: usize


@@ 107,7 114,7 @@ impl RandUsize {
}


#[derive(Deserialize, Serialize)]
#[derive(Deserialize, Serialize, Clone, Copy)]
pub struct RandVec2 {
    pub x: RandF32,
    pub y: RandF32,