fix missile trails (#81)

added replicated `SpawnTrail` that acts as a marker and gets picked up by the client to create the visuals.
This commit is contained in:
extrawurst
2025-12-09 22:16:09 +01:00
committed by GitHub
parent 5c0e78cb8a
commit 4e169c1506
4 changed files with 91 additions and 51 deletions

View File

@@ -5,7 +5,7 @@ use crate::{
heads_database::HeadsDatabase,
physics_layers::GameLayer,
protocol::{GltfSceneRoot, PlaySound},
utils::{global_observer, trail::Trail},
utils::{global_observer, trail::SpawnTrail},
};
use avian3d::prelude::*;
use bevy::prelude::*;
@@ -15,7 +15,8 @@ use std::f32::consts::PI;
const MAX_SHOT_AGES: f32 = 15.;
const MISSLE_SPEED: f32 = 3.;
#[derive(Component)]
#[derive(Component, Reflect)]
#[reflect(Component)]
struct MissileProjectile {
time: f32,
damage: u32,
@@ -40,7 +41,6 @@ fn on_trigger_missile(
query_transform: Query<&Transform>,
time: Res<Time>,
heads_db: Res<HeadsDatabase>,
mut gizmo_assets: ResMut<Assets<GizmoAsset>>,
) {
let state = trigger.event().0;
@@ -66,6 +66,13 @@ fn on_trigger_missile(
time: time.elapsed_secs(),
damage: head.damage,
},
SpawnTrail::new(
12,
LinearRgba::rgb(1., 0.0, 0.),
LinearRgba::rgb(0.9, 0.9, 0.),
10.,
)
.init_with_pos(),
Collider::capsule_endpoints(0.4, Vec3::new(0., 0., 2.), Vec3::new(0., 0., -2.)),
CollisionLayers::new(
LayerMask(GameLayer::Projectile.to_bits()),
@@ -77,29 +84,11 @@ fn on_trigger_missile(
Visibility::default(),
transform,
Replicated,
children![
(
Transform::from_rotation(Quat::from_rotation_x(PI / 2.).inverse()),
GltfSceneRoot::Projectile("missile".to_string()),
Replicated,
),
(
Trail::new(
12,
LinearRgba::rgb(1., 0.0, 0.),
LinearRgba::rgb(0.9, 0.9, 0.)
)
.with_pos(transform.translation),
Gizmo {
handle: gizmo_assets.add(GizmoAsset::default()),
line_config: GizmoLineConfig {
width: 10.,
..default()
},
..default()
},
)
],
children![(
Transform::from_rotation(Quat::from_rotation_x(PI / 2.).inverse()),
GltfSceneRoot::Projectile("missile".to_string()),
Replicated
)],
));
}

View File

@@ -3,7 +3,7 @@ use crate::{
animation::{AnimationController, AnimationFlags},
heads_database::HeadsDatabase,
loading_assets::GameAssets,
utils::trail::Trail,
utils::trail::SpawnTrail,
};
use bevy::{
animation::RepeatAnimation, ecs::system::SystemParam, platform::collections::HashMap,
@@ -125,7 +125,6 @@ fn find_marker_bones(
mut commands: Commands,
descendants: Query<&Children>,
name: Query<&Name>,
mut gizmo_assets: ResMut<Assets<GizmoAsset>>,
) {
let entity = trigger.event().entity;
@@ -139,21 +138,12 @@ fn find_marker_bones(
commands.entity(child).insert(ProjectileOrigin);
origin_found = true;
} else if name.as_str().starts_with("Trail") {
commands.entity(child).insert((
Trail::new(
20,
LinearRgba::new(1., 1.0, 1., 0.5),
LinearRgba::new(1., 1., 1., 0.5),
),
Gizmo {
handle: gizmo_assets.add(GizmoAsset::default()),
line_config: GizmoLineConfig {
width: 24.,
..default()
},
..default()
},
));
commands.entity(child).insert((SpawnTrail::new(
20,
LinearRgba::new(1., 1.0, 1., 0.5),
LinearRgba::new(1., 1., 1., 0.5),
24.,
),));
}
}

View File

@@ -19,7 +19,10 @@ use crate::{
platforms::ActivePlatform,
player::{Player, PlayerBodyMesh},
tick::GameTick,
utils::{auto_rotate::AutoRotation, billboards::Billboard, squish_animation::SquishAnimation},
utils::{
auto_rotate::AutoRotation, billboards::Billboard, squish_animation::SquishAnimation,
trail::SpawnTrail,
},
};
use avian3d::prelude::{
AngularInertia, AngularVelocity, CenterOfMass, Collider, ColliderDensity, CollisionLayers,
@@ -96,6 +99,7 @@ pub fn plugin(app: &mut App) {
.replicate_once::<Npc>()
.replicate::<SquishAnimation>()
.replicate_once::<Transform>()
.replicate_once::<SpawnTrail>()
.replicate::<UiActiveHeads>()
.replicate_as::<Visibility, SerVisibility>();

View File

@@ -1,7 +1,36 @@
use crate::GameState;
use bevy::prelude::*;
use serde::{Deserialize, Serialize};
#[derive(Component)]
#[derive(Copy, Clone, Component, Reflect, Deserialize, Serialize)]
#[reflect(Component)]
pub struct SpawnTrail {
pub points: usize,
pub col_start: LinearRgba,
pub col_end: LinearRgba,
pub width: f32,
pub init_pos: bool,
}
impl SpawnTrail {
pub fn new(points: usize, col_start: LinearRgba, col_end: LinearRgba, width: f32) -> Self {
Self {
points,
col_start,
col_end,
width,
init_pos: false,
}
}
pub fn init_with_pos(mut self) -> Self {
self.init_pos = true;
self
}
}
#[derive(Component, Reflect)]
#[reflect(Component)]
pub struct Trail {
points: Vec<Vec3>,
col_start: LinearRgba,
@@ -9,17 +38,19 @@ pub struct Trail {
}
impl Trail {
pub fn new(points: usize, col_start: LinearRgba, col_end: LinearRgba) -> Self {
pub fn new(trail: SpawnTrail) -> Self {
Self {
points: Vec::with_capacity(points),
col_start,
col_end,
points: Vec::with_capacity(trail.points),
col_start: trail.col_start,
col_end: trail.col_end,
}
}
pub fn with_pos(self, pos: Vec3) -> Self {
pub fn with_pos(self, pos: Option<Vec3>) -> Self {
let mut trail = self;
trail.add(pos);
if let Some(pos) = pos {
trail.add(pos);
}
trail
}
@@ -36,6 +67,32 @@ pub fn plugin(app: &mut App) {
FixedUpdate,
update_trail.run_if(in_state(GameState::Playing)),
);
#[cfg(feature = "client")]
app.add_systems(Update, attach_trail.run_if(in_state(GameState::Playing)));
}
#[cfg(feature = "client")]
fn attach_trail(
mut commands: Commands,
query: Query<(Entity, &Transform, &SpawnTrail), Added<SpawnTrail>>,
mut gizmo_assets: ResMut<Assets<GizmoAsset>>,
) {
for (entity, transform, trail) in query.iter() {
let width = trail.width;
let init_pos = trail.init_pos.then_some(transform.translation);
let id = commands
.spawn((
Trail::new(*trail).with_pos(init_pos),
Gizmo {
handle: gizmo_assets.add(GizmoAsset::default()),
line_config: GizmoLineConfig { width, ..default() },
..default()
},
))
.id();
commands.entity(entity).add_child(id);
}
}
fn update_trail(