beaming in characters
This commit is contained in:
BIN
assets/models/beaming.glb
Normal file
BIN
assets/models/beaming.glb
Normal file
Binary file not shown.
BIN
assets/textures/beaming.png
Normal file
BIN
assets/textures/beaming.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 463 B |
@@ -106,7 +106,7 @@ fn update(
|
||||
),
|
||||
)
|
||||
.insert((
|
||||
Billboard,
|
||||
Billboard::All,
|
||||
Transform::from_translation(first_hit.point1),
|
||||
NotShadowCaster,
|
||||
AnimationTimer::new(Timer::from_seconds(0.005, TimerMode::Repeating)),
|
||||
|
||||
@@ -195,7 +195,7 @@ fn shot_collision(
|
||||
.bundle_with_atlas(&mut sprite_params, texture_atlas),
|
||||
)
|
||||
.insert((
|
||||
Billboard,
|
||||
Billboard::All,
|
||||
Transform::from_translation(shot_pos),
|
||||
NotShadowCaster,
|
||||
AnimationTimer::new(Timer::from_seconds(0.01, TimerMode::Repeating)),
|
||||
|
||||
@@ -191,7 +191,7 @@ fn shot_collision(
|
||||
.bundle_with_atlas(&mut sprite_params, texture_atlas),
|
||||
)
|
||||
.insert((
|
||||
Billboard,
|
||||
Billboard::All,
|
||||
Transform::from_translation(shot_pos),
|
||||
NotShadowCaster,
|
||||
AnimationTimer::new(Timer::from_seconds(0.01, TimerMode::Repeating)),
|
||||
|
||||
@@ -192,7 +192,7 @@ fn shot_collision(
|
||||
.bundle_with_atlas(&mut sprite_params, texture_atlas),
|
||||
)
|
||||
.insert((
|
||||
Billboard,
|
||||
Billboard::All,
|
||||
Transform::from_translation(shot_pos),
|
||||
NotShadowCaster,
|
||||
AnimationTimer::new(Timer::from_seconds(0.01, TimerMode::Repeating)),
|
||||
|
||||
@@ -174,7 +174,7 @@ fn shot_collision(
|
||||
),
|
||||
)
|
||||
.insert((
|
||||
Billboard,
|
||||
Billboard::All,
|
||||
Transform::from_translation(shot_pos),
|
||||
NotShadowCaster,
|
||||
AnimationTimer::new(Timer::from_seconds(0.02, TimerMode::Repeating)),
|
||||
|
||||
@@ -42,7 +42,7 @@ fn marker_event(
|
||||
let id = commands
|
||||
.spawn((
|
||||
Name::new("aim-marker"),
|
||||
Billboard,
|
||||
Billboard::All,
|
||||
TargetMarker,
|
||||
Transform::default(),
|
||||
Sprite3dBuilder {
|
||||
|
||||
@@ -134,7 +134,7 @@ fn on_head_drop(
|
||||
|| drop.impulse,
|
||||
)
|
||||
.with_child((
|
||||
Billboard,
|
||||
Billboard::All,
|
||||
SquishAnimation(2.6),
|
||||
SceneRoot(mesh.scenes[0].clone()),
|
||||
));
|
||||
|
||||
@@ -62,7 +62,7 @@ fn update_effects(
|
||||
cmds.entity(e).with_child((
|
||||
Name::new("heal-particle"),
|
||||
SceneRoot(assets.mesh_heal_particle.clone()),
|
||||
Billboard,
|
||||
Billboard::All,
|
||||
Transform::from_translation(start_pos),
|
||||
HealParticle {
|
||||
start_scale,
|
||||
|
||||
@@ -43,7 +43,7 @@ fn on_spawn_key(trigger: Trigger<KeySpawn>, mut commands: Commands, assets: Res<
|
||||
Restitution::new(0.6),
|
||||
Children::spawn((
|
||||
Spawn((
|
||||
Billboard,
|
||||
Billboard::All,
|
||||
SquishAnimation(2.6),
|
||||
SceneRoot(assets.mesh_key.clone()),
|
||||
)),
|
||||
|
||||
@@ -110,6 +110,9 @@ pub struct GameAssets {
|
||||
#[asset(path = "models/medic_particle.glb#Scene0")]
|
||||
pub mesh_heal_particle: Handle<Scene>,
|
||||
|
||||
#[asset(path = "models/beaming.glb#Scene0")]
|
||||
pub beaming: Handle<Scene>,
|
||||
|
||||
#[asset(path = "models/projectiles", collection(mapped, typed))]
|
||||
pub projectiles: HashMap<AssetFileName, Handle<Gltf>>,
|
||||
|
||||
|
||||
50
src/npc.rs
50
src/npc.rs
@@ -9,10 +9,12 @@ use crate::{
|
||||
heads_database::HeadsDatabase,
|
||||
hitpoints::{Hitpoints, Kill},
|
||||
keys::KeySpawn,
|
||||
loading_assets::GameAssets,
|
||||
sounds::PlaySound,
|
||||
tb_entities::EnemySpawn,
|
||||
utils::billboards::Billboard,
|
||||
};
|
||||
use bevy::prelude::*;
|
||||
use bevy::{pbr::NotShadowCaster, prelude::*};
|
||||
use std::collections::HashMap;
|
||||
|
||||
#[derive(Component, Reflect)]
|
||||
@@ -25,14 +27,23 @@ struct NpcSpawning {
|
||||
spawn_index: u32,
|
||||
}
|
||||
|
||||
#[derive(Component, Reflect)]
|
||||
#[reflect(Component)]
|
||||
pub struct SpawningBeam(pub f32);
|
||||
|
||||
#[derive(Event)]
|
||||
struct OnCheckSpawns;
|
||||
|
||||
#[derive(Event)]
|
||||
pub struct SpawnCharacter(pub Vec3);
|
||||
|
||||
pub fn plugin(app: &mut App) {
|
||||
app.init_resource::<NpcSpawning>();
|
||||
app.add_systems(OnEnter(GameState::Playing), setup);
|
||||
app.add_systems(Update, update_beams.run_if(in_state(GameState::Playing)));
|
||||
|
||||
global_observer!(app, on_spawn_check);
|
||||
global_observer!(app, on_spawn);
|
||||
}
|
||||
|
||||
fn setup(mut commands: Commands) {
|
||||
@@ -43,7 +54,7 @@ fn setup(mut commands: Commands) {
|
||||
fn on_spawn_check(
|
||||
_trigger: Trigger<OnCheckSpawns>,
|
||||
mut commands: Commands,
|
||||
query: Query<(Entity, &EnemySpawn), Without<Npc>>,
|
||||
query: Query<(Entity, &EnemySpawn, &Transform), Without<Npc>>,
|
||||
heads_db: Res<HeadsDatabase>,
|
||||
spawning: Res<NpcSpawning>,
|
||||
) {
|
||||
@@ -53,7 +64,7 @@ fn on_spawn_check(
|
||||
names.insert(heads_db.head_key(i).to_string(), i);
|
||||
}
|
||||
|
||||
for (e, spawn) in query.iter() {
|
||||
for (e, spawn, transform) in query.iter() {
|
||||
if let Some(order) = spawn.spawn_order {
|
||||
if order > spawning.spawn_index {
|
||||
continue;
|
||||
@@ -79,6 +90,7 @@ fn on_spawn_check(
|
||||
.with_child((Name::from("body-rig"), AnimatedCharacter::new(id)))
|
||||
.observe(on_kill);
|
||||
|
||||
commands.trigger(SpawnCharacter(transform.translation));
|
||||
commands.trigger(PlaySound::Beaming);
|
||||
}
|
||||
}
|
||||
@@ -107,3 +119,35 @@ fn on_kill(
|
||||
commands.trigger(KeySpawn(transform.translation, enemy.key.clone()));
|
||||
}
|
||||
}
|
||||
|
||||
fn on_spawn(
|
||||
trigger: Trigger<SpawnCharacter>,
|
||||
mut commands: Commands,
|
||||
assets: Res<GameAssets>,
|
||||
time: Res<Time>,
|
||||
) {
|
||||
commands.spawn((
|
||||
Transform::from_translation(trigger.event().0 + Vec3::new(0., -2., 0.))
|
||||
.with_scale(Vec3::new(1., 40., 1.)),
|
||||
Billboard::XZ,
|
||||
NotShadowCaster,
|
||||
SpawningBeam(time.elapsed_secs()),
|
||||
SceneRoot(assets.beaming.clone()),
|
||||
));
|
||||
}
|
||||
|
||||
fn update_beams(
|
||||
mut commands: Commands,
|
||||
mut query: Query<(Entity, &SpawningBeam, &mut Transform)>,
|
||||
time: Res<Time>,
|
||||
) {
|
||||
for (entity, beam, mut transform) in query.iter_mut() {
|
||||
let age = time.elapsed_secs() - beam.0;
|
||||
|
||||
transform.scale.x = age.sin() * 2.;
|
||||
|
||||
if age > 3. {
|
||||
commands.entity(entity).despawn();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ use crate::{
|
||||
heads_database::{HeadControls, HeadsDatabase},
|
||||
hitpoints::{Hitpoints, Kill},
|
||||
loading_assets::AudioAssets,
|
||||
npc::SpawnCharacter,
|
||||
physics_layers::GameLayer,
|
||||
sounds::PlaySound,
|
||||
tb_entities::SpawnPoint,
|
||||
@@ -98,6 +99,8 @@ fn spawn(
|
||||
AudioPlayer::new(asset_server.load("sfx/heads/angry demonstrator.ogg")),
|
||||
PlaybackSettings::DESPAWN,
|
||||
));
|
||||
|
||||
commands.trigger(SpawnCharacter(transform.translation));
|
||||
}
|
||||
|
||||
fn on_kill(
|
||||
|
||||
@@ -2,9 +2,13 @@ use crate::camera::MainCamera;
|
||||
use bevy::prelude::*;
|
||||
use bevy_sprite3d::Sprite3dPlugin;
|
||||
|
||||
#[derive(Component, Reflect)]
|
||||
#[derive(Component, Reflect, Default, PartialEq, Eq)]
|
||||
#[reflect(Component)]
|
||||
pub struct Billboard;
|
||||
pub enum Billboard {
|
||||
#[default]
|
||||
All,
|
||||
XZ,
|
||||
}
|
||||
|
||||
pub fn plugin(app: &mut App) {
|
||||
if !app.is_plugin_added::<Sprite3dPlugin>() {
|
||||
@@ -18,8 +22,8 @@ pub fn plugin(app: &mut App) {
|
||||
fn face_camera(
|
||||
cam_query: Query<&GlobalTransform, With<MainCamera>>,
|
||||
mut query: Query<
|
||||
(&mut Transform, &ChildOf, &InheritedVisibility),
|
||||
(With<Billboard>, Without<MainCamera>),
|
||||
(&mut Transform, &ChildOf, &InheritedVisibility, &Billboard),
|
||||
Without<MainCamera>,
|
||||
>,
|
||||
parent_transform: Query<&GlobalTransform>,
|
||||
) {
|
||||
@@ -27,7 +31,7 @@ fn face_camera(
|
||||
return;
|
||||
};
|
||||
|
||||
for (mut transform, parent, visible) in query.iter_mut() {
|
||||
for (mut transform, parent, visible, billboard) in query.iter_mut() {
|
||||
if !matches!(*visible, InheritedVisibility::VISIBLE) {
|
||||
continue;
|
||||
}
|
||||
@@ -37,18 +41,34 @@ fn face_camera(
|
||||
};
|
||||
|
||||
let target = cam_transform.reparented_to(parent_global);
|
||||
transform.look_at(target.translation, Vec3::Y);
|
||||
|
||||
let target = match *billboard {
|
||||
Billboard::All => target.translation,
|
||||
Billboard::XZ => Vec3::new(
|
||||
target.translation.x,
|
||||
transform.translation.y,
|
||||
target.translation.z,
|
||||
),
|
||||
};
|
||||
|
||||
transform.look_at(target, Vec3::Y);
|
||||
}
|
||||
}
|
||||
|
||||
fn face_camera_no_parent(
|
||||
cam_query: Query<&GlobalTransform, With<MainCamera>>,
|
||||
mut query: Query<&mut Transform, (With<Billboard>, Without<MainCamera>, Without<ChildOf>)>,
|
||||
mut query: Query<(&mut Transform, &Billboard), (Without<MainCamera>, Without<ChildOf>)>,
|
||||
) {
|
||||
let Ok(cam_transform) = cam_query.single() else {
|
||||
return;
|
||||
};
|
||||
for mut transform in query.iter_mut() {
|
||||
transform.look_at(cam_transform.translation(), Vec3::Y);
|
||||
for (mut transform, billboard) in query.iter_mut() {
|
||||
let target = cam_transform.translation();
|
||||
let target = match *billboard {
|
||||
Billboard::All => cam_transform.translation(),
|
||||
Billboard::XZ => Vec3::new(target.x, transform.translation.y, target.z),
|
||||
};
|
||||
|
||||
transform.look_at(target, Vec3::Y);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user