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:
@@ -5,7 +5,7 @@ use crate::{
|
|||||||
heads_database::HeadsDatabase,
|
heads_database::HeadsDatabase,
|
||||||
physics_layers::GameLayer,
|
physics_layers::GameLayer,
|
||||||
protocol::{GltfSceneRoot, PlaySound},
|
protocol::{GltfSceneRoot, PlaySound},
|
||||||
utils::{global_observer, trail::Trail},
|
utils::{global_observer, trail::SpawnTrail},
|
||||||
};
|
};
|
||||||
use avian3d::prelude::*;
|
use avian3d::prelude::*;
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
@@ -15,7 +15,8 @@ use std::f32::consts::PI;
|
|||||||
const MAX_SHOT_AGES: f32 = 15.;
|
const MAX_SHOT_AGES: f32 = 15.;
|
||||||
const MISSLE_SPEED: f32 = 3.;
|
const MISSLE_SPEED: f32 = 3.;
|
||||||
|
|
||||||
#[derive(Component)]
|
#[derive(Component, Reflect)]
|
||||||
|
#[reflect(Component)]
|
||||||
struct MissileProjectile {
|
struct MissileProjectile {
|
||||||
time: f32,
|
time: f32,
|
||||||
damage: u32,
|
damage: u32,
|
||||||
@@ -40,7 +41,6 @@ fn on_trigger_missile(
|
|||||||
query_transform: Query<&Transform>,
|
query_transform: Query<&Transform>,
|
||||||
time: Res<Time>,
|
time: Res<Time>,
|
||||||
heads_db: Res<HeadsDatabase>,
|
heads_db: Res<HeadsDatabase>,
|
||||||
mut gizmo_assets: ResMut<Assets<GizmoAsset>>,
|
|
||||||
) {
|
) {
|
||||||
let state = trigger.event().0;
|
let state = trigger.event().0;
|
||||||
|
|
||||||
@@ -66,6 +66,13 @@ fn on_trigger_missile(
|
|||||||
time: time.elapsed_secs(),
|
time: time.elapsed_secs(),
|
||||||
damage: head.damage,
|
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.)),
|
Collider::capsule_endpoints(0.4, Vec3::new(0., 0., 2.), Vec3::new(0., 0., -2.)),
|
||||||
CollisionLayers::new(
|
CollisionLayers::new(
|
||||||
LayerMask(GameLayer::Projectile.to_bits()),
|
LayerMask(GameLayer::Projectile.to_bits()),
|
||||||
@@ -77,29 +84,11 @@ fn on_trigger_missile(
|
|||||||
Visibility::default(),
|
Visibility::default(),
|
||||||
transform,
|
transform,
|
||||||
Replicated,
|
Replicated,
|
||||||
children![
|
children![(
|
||||||
(
|
Transform::from_rotation(Quat::from_rotation_x(PI / 2.).inverse()),
|
||||||
Transform::from_rotation(Quat::from_rotation_x(PI / 2.).inverse()),
|
GltfSceneRoot::Projectile("missile".to_string()),
|
||||||
GltfSceneRoot::Projectile("missile".to_string()),
|
Replicated
|
||||||
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()
|
|
||||||
},
|
|
||||||
)
|
|
||||||
],
|
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ use crate::{
|
|||||||
animation::{AnimationController, AnimationFlags},
|
animation::{AnimationController, AnimationFlags},
|
||||||
heads_database::HeadsDatabase,
|
heads_database::HeadsDatabase,
|
||||||
loading_assets::GameAssets,
|
loading_assets::GameAssets,
|
||||||
utils::trail::Trail,
|
utils::trail::SpawnTrail,
|
||||||
};
|
};
|
||||||
use bevy::{
|
use bevy::{
|
||||||
animation::RepeatAnimation, ecs::system::SystemParam, platform::collections::HashMap,
|
animation::RepeatAnimation, ecs::system::SystemParam, platform::collections::HashMap,
|
||||||
@@ -125,7 +125,6 @@ fn find_marker_bones(
|
|||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
descendants: Query<&Children>,
|
descendants: Query<&Children>,
|
||||||
name: Query<&Name>,
|
name: Query<&Name>,
|
||||||
mut gizmo_assets: ResMut<Assets<GizmoAsset>>,
|
|
||||||
) {
|
) {
|
||||||
let entity = trigger.event().entity;
|
let entity = trigger.event().entity;
|
||||||
|
|
||||||
@@ -139,21 +138,12 @@ fn find_marker_bones(
|
|||||||
commands.entity(child).insert(ProjectileOrigin);
|
commands.entity(child).insert(ProjectileOrigin);
|
||||||
origin_found = true;
|
origin_found = true;
|
||||||
} else if name.as_str().starts_with("Trail") {
|
} else if name.as_str().starts_with("Trail") {
|
||||||
commands.entity(child).insert((
|
commands.entity(child).insert((SpawnTrail::new(
|
||||||
Trail::new(
|
20,
|
||||||
20,
|
LinearRgba::new(1., 1.0, 1., 0.5),
|
||||||
LinearRgba::new(1., 1.0, 1., 0.5),
|
LinearRgba::new(1., 1., 1., 0.5),
|
||||||
LinearRgba::new(1., 1., 1., 0.5),
|
24.,
|
||||||
),
|
),));
|
||||||
Gizmo {
|
|
||||||
handle: gizmo_assets.add(GizmoAsset::default()),
|
|
||||||
line_config: GizmoLineConfig {
|
|
||||||
width: 24.,
|
|
||||||
..default()
|
|
||||||
},
|
|
||||||
..default()
|
|
||||||
},
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,10 @@ use crate::{
|
|||||||
platforms::ActivePlatform,
|
platforms::ActivePlatform,
|
||||||
player::{Player, PlayerBodyMesh},
|
player::{Player, PlayerBodyMesh},
|
||||||
tick::GameTick,
|
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::{
|
use avian3d::prelude::{
|
||||||
AngularInertia, AngularVelocity, CenterOfMass, Collider, ColliderDensity, CollisionLayers,
|
AngularInertia, AngularVelocity, CenterOfMass, Collider, ColliderDensity, CollisionLayers,
|
||||||
@@ -96,6 +99,7 @@ pub fn plugin(app: &mut App) {
|
|||||||
.replicate_once::<Npc>()
|
.replicate_once::<Npc>()
|
||||||
.replicate::<SquishAnimation>()
|
.replicate::<SquishAnimation>()
|
||||||
.replicate_once::<Transform>()
|
.replicate_once::<Transform>()
|
||||||
|
.replicate_once::<SpawnTrail>()
|
||||||
.replicate::<UiActiveHeads>()
|
.replicate::<UiActiveHeads>()
|
||||||
.replicate_as::<Visibility, SerVisibility>();
|
.replicate_as::<Visibility, SerVisibility>();
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,36 @@
|
|||||||
use crate::GameState;
|
use crate::GameState;
|
||||||
use bevy::prelude::*;
|
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 {
|
pub struct Trail {
|
||||||
points: Vec<Vec3>,
|
points: Vec<Vec3>,
|
||||||
col_start: LinearRgba,
|
col_start: LinearRgba,
|
||||||
@@ -9,17 +38,19 @@ pub struct Trail {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Trail {
|
impl Trail {
|
||||||
pub fn new(points: usize, col_start: LinearRgba, col_end: LinearRgba) -> Self {
|
pub fn new(trail: SpawnTrail) -> Self {
|
||||||
Self {
|
Self {
|
||||||
points: Vec::with_capacity(points),
|
points: Vec::with_capacity(trail.points),
|
||||||
col_start,
|
col_start: trail.col_start,
|
||||||
col_end,
|
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;
|
let mut trail = self;
|
||||||
trail.add(pos);
|
if let Some(pos) = pos {
|
||||||
|
trail.add(pos);
|
||||||
|
}
|
||||||
trail
|
trail
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -36,6 +67,32 @@ pub fn plugin(app: &mut App) {
|
|||||||
FixedUpdate,
|
FixedUpdate,
|
||||||
update_trail.run_if(in_state(GameState::Playing)),
|
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(
|
fn update_trail(
|
||||||
|
|||||||
Reference in New Issue
Block a user