Added effect parameters

master
Mark 2024-01-06 16:01:02 -08:00
parent c1d50f0c45
commit 1992bd7bb8
Signed by: Mark
GPG Key ID: C6D63995FE72FD80
6 changed files with 204 additions and 87 deletions

View File

@ -3,32 +3,61 @@ sprite = "particle::explosion::small"
lifetime = "inherit" lifetime = "inherit"
inherit_velocity = "target" inherit_velocity = "target"
size = 8.0 size = 8.0
size_rng = 1.6
angle_rng = 360
velocity_scale_parent = 1.0
[effect."large explosion"] [effect."large explosion"]
sprite = "particle::explosion::large" sprite = "particle::explosion::large"
lifetime = "inherit" lifetime = "inherit"
inherit_velocity = "target" inherit_velocity = "target"
size = 25.0 size = 25.0
size_rng = 5.0
angle_rng = 360
velocity_scale_parent = 1.0
[effect."huge explosion"] [effect."huge explosion"]
sprite = "particle::explosion::huge" sprite = "particle::explosion::huge"
lifetime = "inherit" lifetime = "inherit"
inherit_velocity = "target" inherit_velocity = "target"
size = 50.0 size = 50.0
size_rng = 10.0
angle_rng = 360
velocity_scale_parent = 1.0
# Every effect has a parent, some effects have a target
[effect."blaster impact"] [effect."blaster impact"]
sprite = "particle::blaster" sprite = "particle::blaster"
lifetime = "inherit" lifetime = "inherit" # number in seconds or inherit from sprite
inherit_velocity = "target" lifetime_rng = 0.0 # Random variation of lifetime (up to this value)
size = 3.0
size = 3.0 # sprite size, in game units
size_rng = 1.0 # random size variation
angle = 0.0 # absolute starting angle. always added to parent angle.
angle_rng = 90.0 # Starting angle randomness (up to this value)
# Does not affect velocity, only sprite angle
angvel_rng = 0.0 # Angvel randomness, applied to angvel
angvel = 0.0 # Angular velocity at creation
# Total velocity is sum of parent + target velocities with scale applied
velocity_scale_parent = 0.0 # Multiply velocity by this value
velocity_scale_parent_rng = 0.0 # random variation of scale
velocity_scale_target = 1.0
velocity_scale_target_rng = 1.0
direction_rng = 1.0 # Random variation of travel direction, in degrees, applied to velocity vector (/2 each side?)
# TODO: # TODO:
# inherit velocity scale
# absolute velocity/angle (no inherit)
# random lifetime, velocity, angle, spin
# bullet bounce effect: inherit and change velocity
# effect probabilities & variants # effect probabilities & variants
# multiple particles in one effect # multiple particles in one effect
# fade # fade
# document: effect vs particle # document: effect vs particle
# sprite lifetime/fps variation (and effects inherit lifetime later)
# universal effect creator

View File

@ -33,5 +33,5 @@ projectile.impact_effect = "blaster impact"
projectile.expire_effect.sprite = "particle::blaster" projectile.expire_effect.sprite = "particle::blaster"
projectile.expire_effect.lifetime = "inherit" projectile.expire_effect.lifetime = "inherit"
projectile.expire_effect.inherit_velocity = "projectile"
projectile.expire_effect.size = 3.0 projectile.expire_effect.size = 3.0
projectile.expire_effect.velocity_scale_parent = 1.0

View File

@ -1,11 +1,12 @@
use anyhow::{Context, Result}; use anyhow::{Context, Result};
use serde::Deserialize; use cgmath::Rad;
use std::collections::HashMap; use std::collections::HashMap;
use crate::{handle::SpriteHandle, Content, ContentBuildContext, EffectHandle}; use crate::{handle::SpriteHandle, Content, ContentBuildContext, EffectHandle};
pub(crate) mod syntax { pub(crate) mod syntax {
use anyhow::{bail, Result}; use anyhow::{bail, Result};
use cgmath::Deg;
use serde::Deserialize; use serde::Deserialize;
use crate::{Content, ContentBuildContext, EffectHandle}; use crate::{Content, ContentBuildContext, EffectHandle};
@ -15,9 +16,26 @@ pub(crate) mod syntax {
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
pub struct Effect { pub struct Effect {
pub sprite: String, pub sprite: String,
pub lifetime: EffectLifetime,
pub inherit_velocity: super::ImpactInheritVelocity,
pub size: f32, pub size: f32,
pub size_rng: Option<f32>,
pub lifetime: TextOrFloat,
pub lifetime_rng: Option<f32>,
pub angle: Option<f32>,
pub angle_rng: Option<f32>,
pub angvel: Option<f32>,
pub angvel_rng: Option<f32>,
pub velocity_scale_parent: Option<f32>,
pub velocity_scale_parent_rng: Option<f32>,
pub velocity_scale_target: Option<f32>,
pub velocity_scale_target_rng: Option<f32>,
pub direction_rng: Option<f32>,
}
#[derive(Debug, Deserialize)]
#[serde(untagged)]
pub enum TextOrFloat {
Text(String),
Float(f32),
} }
// We implement building here instead of in super::Effect because // We implement building here instead of in super::Effect because
@ -34,8 +52,8 @@ pub(crate) mod syntax {
}; };
let lifetime = match self.lifetime { let lifetime = match self.lifetime {
EffectLifetime::Seconds(s) => s, TextOrFloat::Float(f) => f,
EffectLifetime::Inherit(s) => { TextOrFloat::Text(s) => {
if s == "inherit" { if s == "inherit" {
let sprite = content.get_sprite(sprite); let sprite = content.get_sprite(sprite);
sprite.fps * sprite.frames.len() as f32 sprite.fps * sprite.frames.len() as f32
@ -49,24 +67,27 @@ pub(crate) mod syntax {
index: content.effects.len(), index: content.effects.len(),
}; };
content.effects.push(super::Effect { content.effects.push(super::Effect {
sprite,
lifetime,
handle, handle,
inherit_velocity: self.inherit_velocity, sprite,
size: self.size, size: self.size,
size_rng: self.size_rng.unwrap_or(0.0),
lifetime,
lifetime_rng: self.lifetime_rng.unwrap_or(0.0),
angle: Deg(self.angle.unwrap_or(0.0) / 2.0).into(),
angle_rng: Deg(self.angle_rng.unwrap_or(0.0) / 2.0).into(),
angvel: Deg(self.angvel.unwrap_or(0.0)).into(),
angvel_rng: Deg(self.angle_rng.unwrap_or(0.0)).into(),
velocity_scale_parent: self.velocity_scale_parent.unwrap_or(0.0),
velocity_scale_parent_rng: self.velocity_scale_parent_rng.unwrap_or(0.0),
velocity_scale_target: self.velocity_scale_target.unwrap_or(0.0),
velocity_scale_target_rng: self.velocity_scale_target_rng.unwrap_or(0.0),
direction_rng: Deg(self.direction_rng.unwrap_or(0.0) / 2.0).into(),
}); });
return Ok(handle); return Ok(handle);
} }
} }
#[derive(Debug, Deserialize)]
#[serde(untagged)]
pub enum EffectLifetime {
Inherit(String),
Seconds(f32),
}
// This isn't used here, but is pulled in by other content items. // This isn't used here, but is pulled in by other content items.
/// A reference to an effect by name, or an inline definition. /// A reference to an effect by name, or an inline definition.
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
@ -95,43 +116,54 @@ pub(crate) mod syntax {
} }
} }
/// How we should set an effect's velocity
#[derive(Debug, Deserialize, Clone)]
pub enum ImpactInheritVelocity {
/// Don't inherit any velocity.
/// This impact particle will be still.
#[serde(rename = "don't")]
Dont,
/// Inherit target velocity.
/// This impact particle will stick to the object it hits.
#[serde(rename = "target")]
Target,
/// Inherit projectile velocity.
/// This impact particle will continue on its projectile's path.
#[serde(rename = "projectile")]
Projectile,
}
/// The particle a projectile will spawn when it hits something /// The particle a projectile will spawn when it hits something
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Effect { pub struct Effect {
/// The sprite to use for this particle.
/// This is most likely animated.
pub sprite: SpriteHandle,
/// This effect's handle /// This effect's handle
pub handle: EffectHandle, pub handle: EffectHandle,
/// The sprite to use for this particle.
pub sprite: SpriteHandle,
/// The height of this particle, in game units.
pub size: f32,
/// Random size variation
pub size_rng: f32,
/// How many seconds this particle should live /// How many seconds this particle should live
pub lifetime: f32, pub lifetime: f32,
/// How we should set this particle's velocity /// Random lifetime variation
pub inherit_velocity: ImpactInheritVelocity, pub lifetime_rng: f32,
/// The height of this particle, in game units. /// The angle this particle points once spawned
pub size: f32, pub angle: Rad<f32>,
/// Random angle variation
pub angle_rng: Rad<f32>,
/// How fast this particle spins
pub angvel: Rad<f32>,
/// Random angvel variation
pub angvel_rng: Rad<f32>,
/// The amount of this particle's parent's velocity to inherit
pub velocity_scale_parent: f32,
/// Parent velocity random variation
pub velocity_scale_parent_rng: f32,
/// The amount of this particle's parent's target velocity to inherit.
/// If there is no target, this is zero.
pub velocity_scale_target: f32,
/// Target velocity random variation
pub velocity_scale_target_rng: f32,
/// Travel direction random variation
pub direction_rng: Rad<f32>,
} }
impl crate::Build for Effect { impl crate::Build for Effect {

View File

@ -9,7 +9,7 @@ pub(crate) mod ship;
pub(crate) mod sprite; pub(crate) mod sprite;
pub(crate) mod system; pub(crate) mod system;
pub use effect::{Effect, ImpactInheritVelocity}; pub use effect::Effect;
pub use faction::{Faction, Relationship}; pub use faction::{Faction, Relationship};
pub use gun::{Gun, Projectile, ProjectileCollider}; pub use gun::{Gun, Projectile, ProjectileCollider};
pub use outfit::Outfit; pub use outfit::Outfit;

View File

@ -100,16 +100,48 @@ impl ShipCollapseSequence {
}; };
// Position, adjusted for ship rotation // Position, adjusted for ship rotation
let pos = let pos = ship_pos
Matrix2::from_angle(-ship_ang - Rad::from(Deg(90.0))) * pos; + Matrix2::from_angle(-ship_ang - Rad::from(Deg(90.0))) * pos;
let velocity = {
let a = self.rng.gen_range(
-effect.velocity_scale_parent_rng
..=effect.velocity_scale_parent_rng,
);
let velocity = (effect.velocity_scale_parent + a)
* rigid_body.velocity_at_point(&point![pos.x, pos.y]);
Matrix2::from_angle(Rad(self.rng.gen_range(
-effect.direction_rng.0..=effect.direction_rng.0,
))) * Vector2 {
x: velocity.x,
y: velocity.y,
}
};
particles.push(ParticleBuilder { particles.push(ParticleBuilder {
sprite: effect.sprite, sprite: effect.sprite,
pos: ship_pos + pos, pos,
velocity: Vector2::zero(), velocity,
angle: Deg::zero(),
lifetime: effect.lifetime, angle: effect.angle
size: effect.size, + Rad(self
.rng
.gen_range(-effect.angle_rng.0..=effect.angle_rng.0)),
angvel: Rad(effect.angvel.0
+ self
.rng
.gen_range(-effect.angvel_rng.0..=effect.angvel_rng.0)),
lifetime: effect.lifetime
+ self
.rng
.gen_range(-effect.lifetime_rng..=effect.lifetime_rng),
size: effect.size
+ self.rng.gen_range(-effect.size_rng..=effect.size_rng),
}); });
} }
} }
@ -145,13 +177,15 @@ impl ShipCollapseSequence {
}; };
// Position, adjusted for ship rotation // Position, adjusted for ship rotation
let pos = Matrix2::from_angle(-ship_ang - Rad::from(Deg(90.0))) * pos; let pos = ship_pos + Matrix2::from_angle(-ship_ang - Rad::from(Deg(90.0))) * pos;
let vel = rigid_body.velocity_at_point(&point![pos.x, pos.y]);
particles.push(ParticleBuilder { particles.push(ParticleBuilder {
sprite: effect.sprite, sprite: effect.sprite,
pos: ship_pos + pos, pos,
velocity: Vector2::zero(), velocity: Vector2 { x: vel.x, y: vel.y },
angle: Deg::zero(), angle: Rad::zero(),
angvel: Rad::zero(),
lifetime: effect.lifetime, lifetime: effect.lifetime,
size: effect.size, size: effect.size,
}); });

View File

@ -133,6 +133,7 @@ impl<'a> World {
projectile_h: ColliderHandle, projectile_h: ColliderHandle,
ship_h: ColliderHandle, ship_h: ColliderHandle,
) { ) {
let mut rng = rand::thread_rng();
let projectile = self.projectiles.get(&projectile_h); let projectile = self.projectiles.get(&projectile_h);
let ship = self.ships.get_mut(&ship_h); let ship = self.ships.get_mut(&ship_h);
if projectile.is_none() || ship.is_none() { if projectile.is_none() || ship.is_none() {
@ -173,29 +174,38 @@ impl<'a> World {
None => {} None => {}
Some(x) => { Some(x) => {
let x = ct.get_effect(*x); let x = ct.get_effect(*x);
let velocity = match x.inherit_velocity {
content::ImpactInheritVelocity::Dont => Vector2 { x: 0.0, y: 0.0 }, let velocity = {
content::ImpactInheritVelocity::Projectile => util::rigidbody_velocity(pr),
content::ImpactInheritVelocity::Target => {
// Match target ship velocity.
// Particles will fly off if the ship is spinning fast, but I
// haven't found a good way to fix that.
let (_, sr) = self.get_ship_body(s).unwrap(); let (_, sr) = self.get_ship_body(s).unwrap();
let velocity = let target_velocity =
sr.velocity_at_point(&nalgebra::Point2::new(pos.x, pos.y)); sr.velocity_at_point(&nalgebra::Point2::new(pos.x, pos.y));
Vector2 { let a = rng
x: velocity.x, .gen_range(-x.velocity_scale_parent_rng..=x.velocity_scale_parent_rng);
y: velocity.y, let b = rng
} .gen_range(-x.velocity_scale_target_rng..=x.velocity_scale_target_rng);
}
let velocity = ((x.velocity_scale_parent + a)
* util::rigidbody_velocity(pr))
+ ((x.velocity_scale_target + b)
* Vector2 {
x: target_velocity.x,
y: target_velocity.y,
});
Matrix2::from_angle(Rad(
rng.gen_range(-x.direction_rng.0..=x.direction_rng.0)
)) * velocity
}; };
particles.push(ParticleBuilder { particles.push(ParticleBuilder {
sprite: x.sprite, sprite: x.sprite,
pos: Point2 { x: pos.x, y: pos.y }, pos: Point2 { x: pos.x, y: pos.y },
velocity, velocity,
angle: -angle, angle: Rad::from(-angle)
lifetime: x.lifetime, + Rad(rng.gen_range(-x.angle_rng.0..=x.angle_rng.0)),
size: x.size, angvel: Rad(x.angvel.0 + rng.gen_range(-x.angvel_rng.0..=x.angvel_rng.0)),
lifetime: x.lifetime + rng.gen_range(-x.lifetime_rng..=x.lifetime_rng),
size: x.size + rng.gen_range(-x.size_rng..=x.size_rng),
}); });
} }
}; };
@ -320,6 +330,8 @@ impl<'a> World {
to_remove.push(*c); to_remove.push(*c);
} }
} }
let mut rng = rand::thread_rng();
for c in to_remove { for c in to_remove {
let (pr, p) = self.remove_projectile(c).unwrap(); let (pr, p) = self.remove_projectile(c).unwrap();
@ -328,22 +340,32 @@ impl<'a> World {
Some(x) => { Some(x) => {
let x = ct.get_effect(*x); let x = ct.get_effect(*x);
let pos = util::rigidbody_position(&pr); let pos = util::rigidbody_position(&pr);
let vel = util::rigidbody_velocity(&pr);
let angle: Deg<f32> = util::rigidbody_rotation(&pr) let angle: Deg<f32> = util::rigidbody_rotation(&pr)
.angle(Vector2 { x: 1.0, y: 0.0 }) .angle(Vector2 { x: 1.0, y: 0.0 })
.into(); .into();
let velocity = match x.inherit_velocity { let velocity = {
content::ImpactInheritVelocity::Target let a = rng
| content::ImpactInheritVelocity::Dont => Vector2 { x: 0.0, y: 0.0 }, .gen_range(-x.velocity_scale_parent_rng..=x.velocity_scale_parent_rng);
content::ImpactInheritVelocity::Projectile => util::rigidbody_velocity(&pr),
let velocity = (x.velocity_scale_parent + a) * vel;
velocity
//Matrix2::from_angle(Rad(
// rng.gen_range(-x.direction_rng.0..=x.direction_rng.0)
//)) * velocity
}; };
particles.push(ParticleBuilder { particles.push(ParticleBuilder {
sprite: x.sprite, sprite: x.sprite,
pos: Point2 { x: pos.x, y: pos.y }, pos: Point2 { x: pos.x, y: pos.y },
velocity, velocity,
angle: -angle, angle: Rad::from(-angle)
lifetime: x.lifetime, + x.angle + Rad(rng.gen_range(-x.angle_rng.0..=x.angle_rng.0)),
size: x.size, angvel: Rad(x.angvel.0 + rng.gen_range(-x.angvel_rng.0..=x.angvel_rng.0)),
lifetime: x.lifetime + rng.gen_range(-x.lifetime_rng..=x.lifetime_rng),
size: x.size + rng.gen_range(-x.size_rng..=x.size_rng),
}); });
} }
}; };