Bevy 0.16 upgrade (#24)
This commit is contained in:
1212
Cargo.lock
generated
1212
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
24
Cargo.toml
24
Cargo.toml
@@ -11,7 +11,7 @@ opt-level = 3
|
||||
dbg = ["avian3d/debug-plugin", "dep:bevy-inspector-egui"]
|
||||
|
||||
[dependencies]
|
||||
avian3d = { version = "0.2.1", default-features = false, features = [
|
||||
avian3d = { version = "0.2", default-features = false, features = [
|
||||
"3d",
|
||||
"f32",
|
||||
"parry-f32",
|
||||
@@ -20,23 +20,22 @@ avian3d = { version = "0.2.1", default-features = false, features = [
|
||||
"bevy_picking", # todo: Consider if this one is necessary
|
||||
"parallel",
|
||||
] }
|
||||
bevy = "0.15.3"
|
||||
bevy = { version = "0.16.0", features = ["track_location"] }
|
||||
bevy_trenchbroom = { version = "0.7", features = [
|
||||
"auto_register",
|
||||
"avian",
|
||||
"client",
|
||||
] }
|
||||
nil = "0.14.0"
|
||||
bevy_asset_loader = "0.22.0"
|
||||
bevy_sprite3d = "4.0.0"
|
||||
bevy_asset_loader = "0.23.0-rc.3"
|
||||
bevy_sprite3d = "5.0.0"
|
||||
rand = "=0.8.5"
|
||||
bevy-inspector-egui = { version = "0.30", optional = true }
|
||||
bevy_polyline = "0.11.0"
|
||||
bevy-inspector-egui = { version = "0.31", optional = true }
|
||||
bevy-steamworks = "0.13.0"
|
||||
bevy_ballistic = "0.1.0"
|
||||
bevy-ui-gradients = "0.3.0"
|
||||
bevy_debug_log = "0.5.0"
|
||||
bevy_common_assets = { version = "0.12.0", features = ["ron"] }
|
||||
bevy_ballistic = "0.3.0"
|
||||
bevy-ui-gradients = "0.4.0"
|
||||
bevy_debug_log = "0.6.0"
|
||||
bevy_common_assets = { version = "0.13.0", features = ["ron"] }
|
||||
serde = { version = "1.0.219", features = ["derive"] }
|
||||
ron = "0.8"
|
||||
|
||||
@@ -48,4 +47,7 @@ too_many_arguments = "allow"
|
||||
type_complexity = "allow"
|
||||
|
||||
[patch.crates-io]
|
||||
bevy-steamworks = { git = "https://github.com/Laocoon7/bevy_steamworks.git" }
|
||||
bevy-steamworks = { git = "https://github.com/HouraiTeahouse/bevy_steamworks.git", rev = "0bc62fc" }
|
||||
avian3d = { git = "https://github.com/Jondolf/avian.git", rev = "9511076" }
|
||||
bevy_ballistic = { git = "https://github.com/rustunit/bevy_ballistic.git", branch = "bevy-0.16" }
|
||||
bevy_trenchbroom = { git = "https://github.com/extrawurst/bevy_trenchbroom.git", branch = "bevy-0.16.0" }
|
||||
|
||||
@@ -4,11 +4,8 @@ use crate::{
|
||||
hitpoints::Hit, loading_assets::GameAssets, physics_layers::GameLayer, sounds::PlaySound,
|
||||
tb_entities::EnemySpawn, utils::sprite_3d_animation::AnimationTimer,
|
||||
};
|
||||
use avian3d::prelude::{
|
||||
Collider, CollisionLayers, CollisionStarted, LayerMask, PhysicsLayer, Sensor,
|
||||
};
|
||||
use avian3d::prelude::*;
|
||||
use bevy::{pbr::NotShadowCaster, prelude::*};
|
||||
use bevy_polyline::prelude::*;
|
||||
use bevy_sprite3d::{Sprite3dBuilder, Sprite3dParams};
|
||||
use std::f32::consts::PI;
|
||||
|
||||
@@ -83,8 +80,7 @@ fn on_trigger_gun(
|
||||
mut commands: Commands,
|
||||
query_transform: Query<&Transform>,
|
||||
time: Res<Time>,
|
||||
mut polyline_materials: ResMut<Assets<PolylineMaterial>>,
|
||||
mut polylines: ResMut<Assets<Polyline>>,
|
||||
mut gizmo_assets: ResMut<Assets<GizmoAsset>>,
|
||||
) {
|
||||
let state = trigger.0;
|
||||
|
||||
@@ -117,19 +113,20 @@ fn on_trigger_gun(
|
||||
LayerMask(state.target_layer.to_bits() | GameLayer::Level.to_bits()),
|
||||
),
|
||||
Sensor,
|
||||
CollisionEventsEnabled,
|
||||
Visibility::default(),
|
||||
t,
|
||||
))
|
||||
.with_child(PolylineBundle {
|
||||
polyline: PolylineHandle(polylines.add(Polyline {
|
||||
vertices: vec![Vec3::Z * 2., Vec3::Z * -2.],
|
||||
})),
|
||||
material: PolylineMaterialHandle(polyline_materials.add(PolylineMaterial {
|
||||
width: 10.0,
|
||||
color: LinearRgba::rgb(0.9, 0.9, 0.),
|
||||
perspective: false,
|
||||
.with_child(Gizmo {
|
||||
handle: gizmo_assets.add({
|
||||
let mut g = GizmoAsset::default();
|
||||
g.line(Vec3::Z * -2., Vec3::Z * 2., LinearRgba::rgb(0.9, 0.9, 0.));
|
||||
g
|
||||
}),
|
||||
line_config: GizmoLineConfig {
|
||||
width: 8.,
|
||||
..default()
|
||||
})),
|
||||
},
|
||||
..default()
|
||||
});
|
||||
}
|
||||
@@ -146,7 +143,7 @@ fn timeout(mut commands: Commands, query: Query<(Entity, &GunProjectile)>, time:
|
||||
|
||||
for (e, GunProjectile { time, .. }) in query.iter() {
|
||||
if current_time > time + MAX_SHOT_AGES {
|
||||
commands.entity(e).despawn_recursive();
|
||||
commands.entity(e).despawn();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -167,7 +164,7 @@ fn shot_collision(
|
||||
|
||||
let shot_pos = query_shot.get(shot_entity).unwrap().1.translation;
|
||||
|
||||
commands.entity(shot_entity).despawn_recursive();
|
||||
commands.entity(shot_entity).despawn();
|
||||
|
||||
let texture_atlas = TextureAtlas {
|
||||
layout: assets.layout.clone(),
|
||||
|
||||
@@ -7,15 +7,14 @@ use crate::{
|
||||
loading_assets::GameAssets,
|
||||
physics_layers::GameLayer,
|
||||
sounds::PlaySound,
|
||||
utils::{global_observer, sprite_3d_animation::AnimationTimer},
|
||||
utils::{global_observer, sprite_3d_animation::AnimationTimer, trail::Trail},
|
||||
};
|
||||
use avian3d::prelude::*;
|
||||
use bevy::{pbr::NotShadowCaster, prelude::*};
|
||||
use bevy_polyline::prelude::*;
|
||||
use bevy_sprite3d::{Sprite3dBuilder, Sprite3dParams};
|
||||
use std::f32::consts::PI;
|
||||
|
||||
const MAX_SHOT_AGES: f32 = 5.;
|
||||
const MAX_SHOT_AGES: f32 = 15.;
|
||||
|
||||
#[derive(Component)]
|
||||
struct MissileProjectile {
|
||||
@@ -66,7 +65,7 @@ fn on_explosion(
|
||||
};
|
||||
|
||||
for entity in intersections.iter() {
|
||||
if let Some(mut e) = commands.get_entity(*entity) {
|
||||
if let Ok(mut e) = commands.get_entity(*entity) {
|
||||
e.trigger(Hit {
|
||||
damage: explosion.damage,
|
||||
});
|
||||
@@ -89,11 +88,10 @@ fn on_trigger_missile(
|
||||
mut commands: Commands,
|
||||
query_transform: Query<&Transform>,
|
||||
time: Res<Time>,
|
||||
mut polyline_materials: ResMut<Assets<PolylineMaterial>>,
|
||||
mut polylines: ResMut<Assets<Polyline>>,
|
||||
heads_db: Res<HeadsDatabase>,
|
||||
assets: Res<GameAssets>,
|
||||
gltf_assets: Res<Assets<Gltf>>,
|
||||
mut gizmo_assets: ResMut<Assets<GizmoAsset>>,
|
||||
) {
|
||||
let state = trigger.event().0;
|
||||
|
||||
@@ -111,7 +109,7 @@ fn on_trigger_missile(
|
||||
let head = heads_db.head_stats(state.head);
|
||||
|
||||
let mut t = Transform::from_translation(state.pos).with_rotation(rotation);
|
||||
t.translation += t.forward().as_vec3() * 3.0;
|
||||
t.translation += t.forward().as_vec3() * 2.0;
|
||||
|
||||
let mesh = assets.projectiles["missile.glb"].clone();
|
||||
let asset = gltf_assets.get(&mesh).unwrap();
|
||||
@@ -123,12 +121,13 @@ fn on_trigger_missile(
|
||||
time: time.elapsed_secs(),
|
||||
damage: head.damage,
|
||||
},
|
||||
Collider::capsule_endpoints(0.5, Vec3::new(0., 0., 0.), Vec3::new(0., 0., -3.)),
|
||||
Collider::capsule_endpoints(0.4, Vec3::new(0., 0., 2.), Vec3::new(0., 0., -2.)),
|
||||
CollisionLayers::new(
|
||||
LayerMask(GameLayer::Projectile.to_bits()),
|
||||
LayerMask(state.target_layer.to_bits() | GameLayer::Level.to_bits()),
|
||||
),
|
||||
Sensor,
|
||||
CollisionEventsEnabled,
|
||||
Visibility::default(),
|
||||
t,
|
||||
))
|
||||
@@ -137,18 +136,17 @@ fn on_trigger_missile(
|
||||
.with_scale(Vec3::splat(0.04)),
|
||||
SceneRoot(asset.scenes[0].clone()),
|
||||
))
|
||||
.with_child(PolylineBundle {
|
||||
polyline: PolylineHandle(polylines.add(Polyline {
|
||||
vertices: vec![Vec3::Z * 4., Vec3::Z * 0.],
|
||||
})),
|
||||
material: PolylineMaterialHandle(polyline_materials.add(PolylineMaterial {
|
||||
width: 12.0,
|
||||
color: LinearRgba::rgb(0.9, 0.9, 0.),
|
||||
perspective: false,
|
||||
.with_child((
|
||||
Trail::new(t.translation, LinearRgba::rgb(0.9, 0.9, 0.)),
|
||||
Gizmo {
|
||||
handle: gizmo_assets.add(GizmoAsset::default()),
|
||||
line_config: GizmoLineConfig {
|
||||
width: 10.,
|
||||
..default()
|
||||
},
|
||||
..default()
|
||||
})),
|
||||
..default()
|
||||
});
|
||||
},
|
||||
));
|
||||
}
|
||||
|
||||
fn update(mut query: Query<&mut Transform, With<MissileProjectile>>) {
|
||||
@@ -163,7 +161,7 @@ fn timeout(mut commands: Commands, query: Query<(Entity, &MissileProjectile)>, t
|
||||
|
||||
for (e, MissileProjectile { time, .. }) in query.iter() {
|
||||
if current_time > time + MAX_SHOT_AGES {
|
||||
commands.entity(e).despawn_recursive();
|
||||
commands.entity(e).despawn();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -191,7 +189,7 @@ fn shot_collision(
|
||||
|
||||
commands.trigger(PlaySound::MissileExplosion);
|
||||
|
||||
commands.entity(shot_entity).despawn_recursive();
|
||||
commands.entity(shot_entity).despawn();
|
||||
|
||||
commands.trigger(Explosion {
|
||||
damage,
|
||||
|
||||
@@ -88,16 +88,12 @@ fn on_trigger_state(
|
||||
mut commands: Commands,
|
||||
player_rot: Query<&Transform, With<PlayerBodyMesh>>,
|
||||
player_query: Query<(Entity, &AimTarget), With<Player>>,
|
||||
mut active_heads: Query<&mut ActiveHeads, With<Player>>,
|
||||
mut active_heads: Single<&mut ActiveHeads, With<Player>>,
|
||||
heads_db: Res<HeadsDatabase>,
|
||||
time: Res<Time>,
|
||||
character: CharacterHierarchy,
|
||||
) {
|
||||
if matches!(trigger.event(), TriggerState::Active) {
|
||||
let Ok(mut active_heads) = active_heads.get_single_mut() else {
|
||||
return;
|
||||
};
|
||||
|
||||
let Some(state) = active_heads.current() else {
|
||||
return;
|
||||
};
|
||||
|
||||
@@ -67,7 +67,7 @@ fn on_explosion(
|
||||
};
|
||||
|
||||
for entity in intersections.iter() {
|
||||
if let Some(mut e) = commands.get_entity(*entity) {
|
||||
if let Ok(mut e) = commands.get_entity(*entity) {
|
||||
e.trigger(Hit {
|
||||
damage: explosion.damage,
|
||||
});
|
||||
@@ -135,6 +135,7 @@ fn on_trigger_thrown(
|
||||
LayerMask(state.target_layer.to_bits() | GameLayer::Level.to_bits()),
|
||||
),
|
||||
RigidBody::Dynamic,
|
||||
CollisionEventsEnabled,
|
||||
Mass(0.01),
|
||||
LinearVelocity(vel),
|
||||
Visibility::default(),
|
||||
@@ -171,7 +172,7 @@ fn shot_collision(
|
||||
continue;
|
||||
};
|
||||
|
||||
commands.entity(shot_entity).despawn_recursive();
|
||||
commands.entity(shot_entity).despawn();
|
||||
|
||||
commands.trigger(PlaySound::ThrowHit);
|
||||
|
||||
|
||||
@@ -56,7 +56,7 @@ fn wait_for_player(
|
||||
for agent in agents.iter() {
|
||||
if let Some(player) = in_range(50., agent, &players, &transform) {
|
||||
info!("[{agent}] Engage: {player}");
|
||||
if let Some(mut agent) = commands.get_entity(agent) {
|
||||
if let Ok(mut agent) = commands.get_entity(agent) {
|
||||
agent.remove::<WaitForAnyPlayer>().insert(Engage(player));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ fn marker_event(
|
||||
marker: Query<Entity, With<TargetMarker>>,
|
||||
) {
|
||||
for m in marker.iter() {
|
||||
commands.entity(m).despawn_recursive();
|
||||
commands.entity(m).despawn();
|
||||
}
|
||||
|
||||
let MarkerEvent::Spawn(target) = trigger.event() else {
|
||||
|
||||
@@ -110,7 +110,7 @@ fn update_player_aim(
|
||||
}
|
||||
|
||||
if let Some(e) = &aim_target.0 {
|
||||
if commands.get_entity(*e).is_none() {
|
||||
if commands.get_entity(*e).is_err() {
|
||||
aim_target.0 = None;
|
||||
return;
|
||||
}
|
||||
@@ -162,7 +162,7 @@ fn update_npc_aim(
|
||||
}
|
||||
|
||||
if let Some(e) = &aim_target.0 {
|
||||
if commands.get_entity(*e).is_none() {
|
||||
if commands.get_entity(*e).is_err() {
|
||||
aim_target.0 = None;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ fn setup(mut commands: Commands, assets: Res<UIAssets>) {
|
||||
}
|
||||
|
||||
fn spawn_head_ui(
|
||||
parent: &mut ChildBuilder,
|
||||
parent: &mut ChildSpawnerCommands,
|
||||
bg: Handle<Image>,
|
||||
regular: Handle<Image>,
|
||||
damage: Handle<Image>,
|
||||
@@ -129,7 +129,7 @@ fn update(
|
||||
mut head_damage: Query<&mut Node, (With<HeadDamage>, Without<HeadImage>)>,
|
||||
) {
|
||||
if target.is_changed() {
|
||||
if let Ok((mut vis, mut image)) = head_image.get_single_mut() {
|
||||
if let Ok((mut vis, mut image)) = head_image.single_mut() {
|
||||
if let Some(head) = target.head {
|
||||
*vis = Visibility::Visible;
|
||||
image.image = heads_images.heads[head.head].clone();
|
||||
@@ -138,7 +138,7 @@ fn update(
|
||||
}
|
||||
}
|
||||
|
||||
if let Ok(mut node) = head_damage.get_single_mut() {
|
||||
if let Ok(mut node) = head_damage.single_mut() {
|
||||
node.height = Val::Percent(target.head.map(|head| head.damage()).unwrap_or(0.) * 100.);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,6 +86,7 @@ fn setup(mut commands: Commands, assets: Res<UIAssets>) {
|
||||
commands.spawn((
|
||||
Name::new("backpack-head-count-ui"),
|
||||
Text::new("0"),
|
||||
TextShadow::default(),
|
||||
BackpackCountText,
|
||||
TextFont {
|
||||
font: assets.font.clone(),
|
||||
@@ -104,7 +105,7 @@ fn setup(mut commands: Commands, assets: Res<UIAssets>) {
|
||||
}
|
||||
|
||||
fn spawn_head_ui(
|
||||
parent: &mut ChildBuilder,
|
||||
parent: &mut ChildSpawnerCommands,
|
||||
bg: Handle<Image>,
|
||||
regular: Handle<Image>,
|
||||
selector: Handle<Image>,
|
||||
|
||||
@@ -103,7 +103,7 @@ fn update_ui(
|
||||
));
|
||||
} else {
|
||||
for entity in query.iter() {
|
||||
commands.entity(entity).despawn_recursive();
|
||||
commands.entity(entity).despawn();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -114,8 +114,8 @@ fn update(
|
||||
(&MainCamera, &mut Transform, &CameraRotationInput),
|
||||
(Without<CameraTarget>, Without<CameraArmRotation>),
|
||||
>,
|
||||
target: Query<&GlobalTransform, (With<CameraTarget>, Without<CameraArmRotation>)>,
|
||||
arm_rotation: Query<&Transform, With<CameraArmRotation>>,
|
||||
target: Single<&GlobalTransform, (With<CameraTarget>, Without<CameraArmRotation>)>,
|
||||
arm_rotation: Single<&Transform, With<CameraArmRotation>>,
|
||||
spatial_query: SpatialQuery,
|
||||
cam_state: Res<CameraState>,
|
||||
) {
|
||||
@@ -123,15 +123,11 @@ fn update(
|
||||
return;
|
||||
}
|
||||
|
||||
let Ok(target) = target.get_single().map(|t| t.translation()) else {
|
||||
return;
|
||||
};
|
||||
let target = target.translation();
|
||||
|
||||
let Ok(arm_tf) = arm_rotation.get_single() else {
|
||||
return;
|
||||
};
|
||||
let arm_tf = arm_rotation;
|
||||
|
||||
let Ok((camera, mut cam_transform, cam_rotation_input)) = cam.get_single_mut() else {
|
||||
let Ok((camera, mut cam_transform, cam_rotation_input)) = cam.single_mut() else {
|
||||
return;
|
||||
};
|
||||
|
||||
@@ -159,13 +155,9 @@ fn update(
|
||||
|
||||
fn rotate_view(
|
||||
controls: Res<ControlState>,
|
||||
mut cam: Query<&mut CameraRotationInput>,
|
||||
mut cam: Single<&mut CameraRotationInput>,
|
||||
movement: Res<PlayerMovement>,
|
||||
) {
|
||||
let Ok(mut cam) = cam.get_single_mut() else {
|
||||
return;
|
||||
};
|
||||
|
||||
if !controls.view_mode {
|
||||
if movement.any_direction {
|
||||
cam.x = 0.;
|
||||
|
||||
@@ -63,6 +63,7 @@ fn setup(mut commands: Commands, assets: Res<UIAssets>) {
|
||||
commands.spawn((
|
||||
Name::new("cash-ui"),
|
||||
Text::new("0"),
|
||||
TextShadow::default(),
|
||||
CashText,
|
||||
TextFont {
|
||||
font: assets.font.clone(),
|
||||
|
||||
@@ -20,7 +20,7 @@ fn on_heal_trigger(
|
||||
mut cash: ResMut<CashResource>,
|
||||
mut query: Query<&mut Hitpoints, With<Player>>,
|
||||
) {
|
||||
let Ok(mut hp) = query.get_single_mut() else {
|
||||
let Ok(mut hp) = query.single_mut() else {
|
||||
return;
|
||||
};
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use crate::{GameState, heads_database::HeadsDatabase, loading_assets::GameAssets};
|
||||
use bevy::{ecs::system::SystemParam, prelude::*, utils::HashMap};
|
||||
use bevy::{ecs::system::SystemParam, platform::collections::HashMap, prelude::*};
|
||||
use std::time::Duration;
|
||||
|
||||
#[derive(Component, Debug)]
|
||||
@@ -91,14 +91,14 @@ fn setup_projectile_origin(
|
||||
fn setup_once_loaded(
|
||||
mut commands: Commands,
|
||||
mut query: Query<(Entity, &mut AnimationPlayer), Added<AnimationPlayer>>,
|
||||
parent_query: Query<&Parent>,
|
||||
parent: Query<&ChildOf>,
|
||||
animated_character: Query<&AnimatedCharacter>,
|
||||
assets: Res<GameAssets>,
|
||||
gltf_assets: Res<Assets<Gltf>>,
|
||||
mut graphs: ResMut<Assets<AnimationGraph>>,
|
||||
) {
|
||||
for (entity, mut player) in &mut query {
|
||||
let Some(_animated_character) = parent_query
|
||||
let Some(_animated_character) = parent
|
||||
.iter_ancestors(entity)
|
||||
.find_map(|ancestor| animated_character.get(ancestor).ok())
|
||||
else {
|
||||
|
||||
@@ -12,16 +12,11 @@ use super::controller_common::{CharacterController, MaxSlopeAngle};
|
||||
/// and predict collisions using speculative contacts.
|
||||
#[allow(clippy::type_complexity)]
|
||||
pub fn kinematic_controller_collisions(
|
||||
collisions: Res<Collisions>,
|
||||
collisions: Collisions,
|
||||
bodies: Query<&RigidBody>,
|
||||
collider_parents: Query<&ColliderParent, Without<Sensor>>,
|
||||
collider_rbs: Query<&ColliderOf, Without<Sensor>>,
|
||||
mut character_controllers: Query<
|
||||
(
|
||||
&mut Position,
|
||||
&Rotation,
|
||||
&mut LinearVelocity,
|
||||
Option<&MaxSlopeAngle>,
|
||||
),
|
||||
(&mut Position, &mut LinearVelocity, Option<&MaxSlopeAngle>),
|
||||
(With<RigidBody>, With<CharacterController>),
|
||||
>,
|
||||
time: Res<Time>,
|
||||
@@ -29,8 +24,12 @@ pub fn kinematic_controller_collisions(
|
||||
// Iterate through collisions and move the kinematic body to resolve penetration
|
||||
for contacts in collisions.iter() {
|
||||
// Get the rigid body entities of the colliders (colliders could be children)
|
||||
let Ok([collider_parent1, collider_parent2]) =
|
||||
collider_parents.get_many([contacts.entity1, contacts.entity2])
|
||||
let Ok(
|
||||
[
|
||||
&ColliderOf { rigid_body: rb1 },
|
||||
&ColliderOf { rigid_body: rb2 },
|
||||
],
|
||||
) = collider_rbs.get_many([contacts.entity1, contacts.entity2])
|
||||
else {
|
||||
continue;
|
||||
};
|
||||
@@ -42,20 +41,16 @@ pub fn kinematic_controller_collisions(
|
||||
let character_rb: RigidBody;
|
||||
let is_other_dynamic: bool;
|
||||
|
||||
let (mut position, rotation, mut linear_velocity, max_slope_angle) =
|
||||
if let Ok(character) = character_controllers.get_mut(collider_parent1.get()) {
|
||||
let (mut position, mut linear_velocity, max_slope_angle) =
|
||||
if let Ok(character) = character_controllers.get_mut(rb1) {
|
||||
is_first = true;
|
||||
character_rb = *bodies.get(collider_parent1.get()).unwrap();
|
||||
is_other_dynamic = bodies
|
||||
.get(collider_parent2.get())
|
||||
.is_ok_and(|rb| rb.is_dynamic());
|
||||
character_rb = *bodies.get(rb1).unwrap();
|
||||
is_other_dynamic = bodies.get(rb2).is_ok_and(|rb| rb.is_dynamic());
|
||||
character
|
||||
} else if let Ok(character) = character_controllers.get_mut(collider_parent2.get()) {
|
||||
} else if let Ok(character) = character_controllers.get_mut(rb2) {
|
||||
is_first = false;
|
||||
character_rb = *bodies.get(collider_parent2.get()).unwrap();
|
||||
is_other_dynamic = bodies
|
||||
.get(collider_parent1.get())
|
||||
.is_ok_and(|rb| rb.is_dynamic());
|
||||
character_rb = *bodies.get(rb2).unwrap();
|
||||
is_other_dynamic = bodies.get(rb1).is_ok_and(|rb| rb.is_dynamic());
|
||||
character
|
||||
} else {
|
||||
continue;
|
||||
@@ -70,15 +65,15 @@ pub fn kinematic_controller_collisions(
|
||||
// Each contact in a single manifold shares the same contact normal.
|
||||
for manifold in contacts.manifolds.iter() {
|
||||
let normal = if is_first {
|
||||
-manifold.global_normal1(rotation)
|
||||
-manifold.normal
|
||||
} else {
|
||||
-manifold.global_normal2(rotation)
|
||||
manifold.normal
|
||||
};
|
||||
|
||||
let mut deepest_penetration: Scalar = Scalar::MIN;
|
||||
|
||||
// Solve each penetrating contact in the manifold.
|
||||
for contact in manifold.contacts.iter() {
|
||||
for contact in manifold.points.iter() {
|
||||
if contact.penetration > 0.0 {
|
||||
position.0 += normal * contact.penetration;
|
||||
}
|
||||
|
||||
@@ -22,8 +22,10 @@ pub fn plugin(app: &mut App) {
|
||||
//
|
||||
// NOTE: The collision implementation here is very basic and a bit buggy.
|
||||
// A collide-and-slide algorithm would likely work better.
|
||||
PostProcessCollisions,
|
||||
kinematic_controller_collisions.run_if(in_state(GameState::Playing)), // todo check if we can make this less viral,
|
||||
PhysicsSchedule,
|
||||
kinematic_controller_collisions
|
||||
.in_set(NarrowPhaseSet::Last)
|
||||
.run_if(in_state(GameState::Playing)),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -122,6 +124,7 @@ pub struct CharacterControllerBundle {
|
||||
ground_caster: ShapeCaster,
|
||||
gravity: ControllerGravity,
|
||||
movement: MovementBundle,
|
||||
collision_events: CollisionEventsEnabled,
|
||||
}
|
||||
|
||||
/// A bundle that contains components for character movement.
|
||||
@@ -169,6 +172,7 @@ impl CharacterControllerBundle {
|
||||
.with_max_distance(0.2),
|
||||
gravity: ControllerGravity(gravity),
|
||||
movement: MovementBundle::default(),
|
||||
collision_events: CollisionEventsEnabled,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,7 +85,7 @@ fn head_change(
|
||||
};
|
||||
|
||||
if selected_controller.0 != controller {
|
||||
event_controller_switch.send(ControllerSwitchEvent);
|
||||
event_controller_switch.write(ControllerSwitchEvent);
|
||||
|
||||
selected_controller.0 = controller;
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ use crate::{
|
||||
tb_entities::{CameraTarget, CutsceneCamera, CutsceneCameraMovementEnd},
|
||||
};
|
||||
use bevy::prelude::*;
|
||||
use bevy_trenchbroom::class::Target;
|
||||
use bevy_trenchbroom::prelude::*;
|
||||
|
||||
#[derive(Event, Debug)]
|
||||
pub struct StartCutscene(pub String);
|
||||
@@ -96,7 +96,7 @@ fn update(
|
||||
.lerp(camera_end.rotation, timer.fraction()),
|
||||
);
|
||||
|
||||
*cam.single_mut() = t;
|
||||
let _ = cam.single_mut().map(|mut cam| *cam = t);
|
||||
|
||||
if timer.finished() {
|
||||
cam_state.cutscene = false;
|
||||
|
||||
@@ -2,7 +2,7 @@ use crate::{
|
||||
cutscene::StartCutscene, global_observer, keys::KeyCollected, movables::TriggerMovableEvent,
|
||||
sounds::PlaySound,
|
||||
};
|
||||
use bevy::{prelude::*, utils::hashbrown::HashSet};
|
||||
use bevy::{platform::collections::HashSet, prelude::*};
|
||||
|
||||
pub fn plugin(app: &mut App) {
|
||||
global_observer!(app, on_key_collected);
|
||||
|
||||
@@ -40,6 +40,7 @@ fn on_head_drop(trigger: Trigger<HeadDrops>, mut commands: Commands, assets: Res
|
||||
ExternalImpulse::new(spawn_dir * 180.).with_persistence(false),
|
||||
LockedAxes::ROTATION_LOCKED,
|
||||
RigidBody::Dynamic,
|
||||
CollisionEventsEnabled,
|
||||
Restitution::new(0.6),
|
||||
))
|
||||
.with_child((
|
||||
@@ -68,6 +69,6 @@ fn collect_head(
|
||||
|
||||
commands.trigger(PlaySound::HeadCollect);
|
||||
commands.trigger(HeadCollected(key.0));
|
||||
commands.entity(collectable).despawn_recursive();
|
||||
commands.entity(collectable).despawn();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,7 +64,7 @@ fn setup(mut commands: Commands, assets: Res<UIAssets>) {
|
||||
}
|
||||
|
||||
fn spawn_head_ui(
|
||||
parent: &mut ChildBuilder,
|
||||
parent: &mut ChildSpawnerCommands,
|
||||
bg: Handle<Image>,
|
||||
regular: Handle<Image>,
|
||||
selector: Handle<Image>,
|
||||
@@ -219,7 +219,7 @@ fn sync(
|
||||
mut state: ResMut<UiActiveHeads>,
|
||||
time: Res<Time>,
|
||||
) {
|
||||
let Ok(active_heads) = active_heads.get_single() else {
|
||||
let Ok(active_heads) = active_heads.single() else {
|
||||
return;
|
||||
};
|
||||
|
||||
|
||||
@@ -182,7 +182,7 @@ fn on_select_active_head(
|
||||
mut commands: Commands,
|
||||
mut query: Query<(&mut ActiveHeads, &mut Hitpoints), With<Player>>,
|
||||
) {
|
||||
let Ok((mut active_heads, mut hp)) = query.get_single_mut() else {
|
||||
let Ok((mut active_heads, mut hp)) = query.single_mut() else {
|
||||
return;
|
||||
};
|
||||
|
||||
@@ -213,7 +213,7 @@ fn on_swap_backpack(
|
||||
|
||||
let head = backpack.heads.get(backpack_slot).unwrap();
|
||||
|
||||
let Ok((mut active_heads, mut hp)) = query.get_single_mut() else {
|
||||
let Ok((mut active_heads, mut hp)) = query.single_mut() else {
|
||||
return;
|
||||
};
|
||||
|
||||
|
||||
@@ -60,7 +60,7 @@ fn on_hp_added(mut commands: Commands, query: Query<Entity, Added<Hitpoints>>) {
|
||||
fn on_hit(trigger: Trigger<Hit>, mut commands: Commands, mut query: Query<&mut Hitpoints>) {
|
||||
let Hit { damage } = trigger.event();
|
||||
|
||||
let Ok(mut hp) = query.get_mut(trigger.entity()) else {
|
||||
let Ok(mut hp) = query.get_mut(trigger.target()) else {
|
||||
return;
|
||||
};
|
||||
|
||||
@@ -69,6 +69,6 @@ fn on_hit(trigger: Trigger<Hit>, mut commands: Commands, mut query: Query<&mut H
|
||||
hp.current = hp.current.saturating_sub(*damage);
|
||||
|
||||
if hp.current == 0 {
|
||||
commands.trigger_targets(Kill, trigger.entity());
|
||||
commands.trigger_targets(Kill, trigger.target());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,6 +38,7 @@ fn on_spawn_key(trigger: Trigger<KeySpawn>, mut commands: Commands, assets: Res<
|
||||
ExternalImpulse::new(spawn_dir * 180.).with_persistence(false),
|
||||
LockedAxes::ROTATION_LOCKED,
|
||||
RigidBody::Dynamic,
|
||||
CollisionEventsEnabled,
|
||||
Restitution::new(0.6),
|
||||
))
|
||||
.with_child((
|
||||
@@ -66,6 +67,6 @@ fn collect_key(
|
||||
|
||||
commands.trigger(PlaySound::KeyCollect);
|
||||
commands.trigger(KeyCollected(key.0.clone()));
|
||||
commands.entity(collectable).despawn_recursive();
|
||||
commands.entity(collectable).despawn();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ use crate::{
|
||||
GameState,
|
||||
heads_database::{HeadDatabaseAsset, HeadsDatabase},
|
||||
};
|
||||
use bevy::{prelude::*, utils::HashMap};
|
||||
use bevy::{platform::collections::HashMap, prelude::*};
|
||||
use bevy_asset_loader::prelude::*;
|
||||
|
||||
#[derive(AssetCollection, Resource)]
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use crate::{GameState, physics_layers::GameLayer};
|
||||
use avian3d::prelude::*;
|
||||
use bevy::{prelude::*, scene::SceneInstanceReady};
|
||||
use bevy::prelude::*;
|
||||
use bevy_trenchbroom::physics::SceneCollidersReady;
|
||||
|
||||
pub fn plugin(app: &mut App) {
|
||||
app.add_systems(OnEnter(GameState::MapLoading), setup_scene);
|
||||
@@ -13,7 +14,8 @@ fn setup_scene(mut commands: Commands, asset_server: Res<AssetServer>) {
|
||||
SceneRoot(asset_server.load("maps/map1.map#Scene")),
|
||||
))
|
||||
.observe(
|
||||
|_t: Trigger<SceneInstanceReady>, mut next_game_state: ResMut<NextState<GameState>>| {
|
||||
|_t: Trigger<SceneCollidersReady>,
|
||||
mut next_game_state: ResMut<NextState<GameState>>| {
|
||||
info!("map loaded");
|
||||
|
||||
next_game_state.set(GameState::Playing);
|
||||
|
||||
26
src/main.rs
26
src/main.rs
@@ -36,16 +36,15 @@ use bevy::{
|
||||
render::view::ColorGrading,
|
||||
};
|
||||
use bevy_common_assets::ron::RonAssetPlugin;
|
||||
use bevy_polyline::PolylinePlugin;
|
||||
use bevy_sprite3d::Sprite3dPlugin;
|
||||
use bevy_steamworks::{FriendFlags, SteamworksClient, SteamworksPlugin};
|
||||
use bevy_steamworks::{Client, FriendFlags, SteamworksPlugin};
|
||||
use bevy_trenchbroom::prelude::*;
|
||||
use bevy_ui_gradients::UiGradientsPlugin;
|
||||
use camera::MainCamera;
|
||||
use heads_database::HeadDatabaseAsset;
|
||||
use loading_assets::AudioAssets;
|
||||
use std::io::{Read, Write};
|
||||
use utils::{billboards, sprite_3d_animation, squish_animation};
|
||||
use utils::{billboards, sprite_3d_animation, squish_animation, trail};
|
||||
|
||||
#[derive(Resource, Reflect, Debug)]
|
||||
#[reflect(Resource)]
|
||||
@@ -106,7 +105,6 @@ fn main() {
|
||||
|
||||
app.add_plugins(bevy_debug_log::LogViewerPlugin::default());
|
||||
app.add_plugins(PhysicsPlugins::default());
|
||||
app.add_plugins(PolylinePlugin);
|
||||
app.add_plugins(Sprite3dPlugin);
|
||||
app.add_plugins(TrenchBroomPlugin(TrenchBroomConfig::new("hedz")));
|
||||
app.add_plugins(UiGradientsPlugin);
|
||||
@@ -114,8 +112,10 @@ fn main() {
|
||||
|
||||
#[cfg(feature = "dbg")]
|
||||
{
|
||||
use bevy_inspector_egui::quick::WorldInspectorPlugin;
|
||||
app.add_plugins(WorldInspectorPlugin::new());
|
||||
app.add_plugins(bevy_inspector_egui::bevy_egui::EguiPlugin {
|
||||
enable_multipass_for_primary_context: true,
|
||||
});
|
||||
app.add_plugins(bevy_inspector_egui::quick::WorldInspectorPlugin::new());
|
||||
app.add_plugins(PhysicsDebugPlugin::default());
|
||||
|
||||
// app.add_plugins(bevy::pbr::wireframe::WireframePlugin)
|
||||
@@ -153,22 +153,24 @@ fn main() {
|
||||
app.add_plugins(utils::observers::plugin);
|
||||
app.add_plugins(water::plugin);
|
||||
app.add_plugins(head_drop::plugin);
|
||||
app.add_plugins(trail::plugin);
|
||||
|
||||
app.init_state::<GameState>();
|
||||
|
||||
app.insert_resource(AmbientLight {
|
||||
color: Color::WHITE,
|
||||
brightness: 400.,
|
||||
..Default::default()
|
||||
});
|
||||
app.insert_resource(ClearColor(Color::BLACK));
|
||||
//TODO: let use control this
|
||||
app.insert_resource(GlobalVolume::new(0.1));
|
||||
//TODO: let user control this
|
||||
app.insert_resource(GlobalVolume::new(Volume::Linear(0.4)));
|
||||
|
||||
app.add_systems(
|
||||
Startup,
|
||||
(
|
||||
write_trenchbroom_config,
|
||||
steam_system.run_if(resource_exists::<SteamworksClient>),
|
||||
steam_system.run_if(resource_exists::<Client>),
|
||||
),
|
||||
);
|
||||
app.add_systems(OnEnter(GameState::Playing), music);
|
||||
@@ -177,7 +179,7 @@ fn main() {
|
||||
app.run();
|
||||
}
|
||||
|
||||
fn steam_system(steam_client: Res<SteamworksClient>) {
|
||||
fn steam_system(steam_client: Res<Client>) {
|
||||
for friend in steam_client.friends().get_friends(FriendFlags::IMMEDIATE) {
|
||||
info!(
|
||||
"Steam Friend: {:?} - {}({:?})",
|
||||
@@ -215,7 +217,7 @@ fn music(assets: Res<AudioAssets>, mut commands: Commands) {
|
||||
AudioPlayer::new(assets.music.clone()),
|
||||
PlaybackSettings {
|
||||
mode: PlaybackMode::Loop,
|
||||
volume: Volume::new(0.6),
|
||||
volume: Volume::Linear(0.6),
|
||||
..default()
|
||||
},
|
||||
));
|
||||
@@ -225,7 +227,7 @@ fn music(assets: Res<AudioAssets>, mut commands: Commands) {
|
||||
AudioPlayer::new(assets.ambient.clone()),
|
||||
PlaybackSettings {
|
||||
mode: PlaybackMode::Loop,
|
||||
volume: Volume::new(0.8),
|
||||
volume: Volume::Linear(0.8),
|
||||
..default()
|
||||
},
|
||||
));
|
||||
|
||||
@@ -2,8 +2,8 @@ use crate::{
|
||||
GameState, global_observer,
|
||||
tb_entities::{Movable, MoveTarget},
|
||||
};
|
||||
use bevy::{prelude::*, utils::HashSet};
|
||||
use bevy_trenchbroom::class::Target;
|
||||
use bevy::{platform::collections::HashSet, prelude::*};
|
||||
use bevy_trenchbroom::prelude::*;
|
||||
|
||||
#[derive(Component, Reflect, Default, Debug)]
|
||||
#[reflect(Component)]
|
||||
|
||||
@@ -55,13 +55,13 @@ fn on_kill(
|
||||
mut commands: Commands,
|
||||
query: Query<(&Transform, &EnemySpawn, &ActiveHead)>,
|
||||
) {
|
||||
let Ok((transform, enemy, head)) = query.get(trigger.entity()) else {
|
||||
let Ok((transform, enemy, head)) = query.get(trigger.target()) else {
|
||||
return;
|
||||
};
|
||||
|
||||
commands.trigger(HeadDrops(transform.translation, head.0));
|
||||
|
||||
commands.entity(trigger.entity()).despawn_recursive();
|
||||
commands.entity(trigger.target()).despawn();
|
||||
|
||||
if !enemy.key.is_empty() {
|
||||
commands.trigger(KeySpawn(transform.translation, enemy.key.clone()));
|
||||
|
||||
@@ -3,7 +3,7 @@ use crate::{
|
||||
tb_entities::{Platform, PlatformTarget},
|
||||
};
|
||||
use bevy::{math::ops::sin, prelude::*};
|
||||
use bevy_trenchbroom::class::Target;
|
||||
use bevy_trenchbroom::prelude::*;
|
||||
|
||||
#[derive(Component, Reflect, Default, Debug)]
|
||||
#[reflect(Component)]
|
||||
|
||||
@@ -102,9 +102,12 @@ fn spawn(
|
||||
));
|
||||
}
|
||||
|
||||
fn cursor_recenter(mut q_windows: Query<&mut Window, With<PrimaryWindow>>) {
|
||||
let mut primary_window = q_windows.single_mut();
|
||||
let center = Vec2::new(primary_window.width() / 2.0, primary_window.height() / 2.0);
|
||||
fn cursor_recenter(q_windows: Single<&mut Window, With<PrimaryWindow>>) {
|
||||
let mut primary_window = q_windows;
|
||||
let center = Vec2::new(
|
||||
primary_window.resolution.width() / 2.,
|
||||
primary_window.resolution.height() / 2.,
|
||||
);
|
||||
primary_window.set_cursor_position(Some(center));
|
||||
}
|
||||
|
||||
@@ -121,12 +124,8 @@ fn toggle_grab_cursor(window: &mut Window) {
|
||||
}
|
||||
}
|
||||
|
||||
fn toggle_cursor_system(mut primary_window: Query<&mut Window, With<PrimaryWindow>>) {
|
||||
if let Ok(mut window) = primary_window.get_single_mut() {
|
||||
toggle_grab_cursor(&mut window);
|
||||
} else {
|
||||
warn!("Primary window not found for `toggle_cursor_system`!");
|
||||
}
|
||||
fn toggle_cursor_system(mut window: Single<&mut Window, With<PrimaryWindow>>) {
|
||||
toggle_grab_cursor(&mut window);
|
||||
}
|
||||
|
||||
fn collect_cash(
|
||||
@@ -154,11 +153,11 @@ fn collect_cash(
|
||||
fn setup_animations_marker_for_player(
|
||||
mut commands: Commands,
|
||||
animation_handles: Query<Entity, Added<AnimationGraphHandle>>,
|
||||
parent_query: Query<&Parent>,
|
||||
children: Query<&ChildOf>,
|
||||
player: Query<&PlayerBodyMesh>,
|
||||
) {
|
||||
for entity in animation_handles.iter() {
|
||||
for ancestor in parent_query.iter_ancestors(entity) {
|
||||
for ancestor in children.iter_ancestors(entity) {
|
||||
if player.contains(ancestor) {
|
||||
commands.entity(entity).insert(PlayerAnimations);
|
||||
return;
|
||||
@@ -196,18 +195,12 @@ fn toggle_animation(
|
||||
fn on_update_head(
|
||||
trigger: Trigger<HeadChanged>,
|
||||
mut commands: Commands,
|
||||
body_mesh: Query<Entity, With<PlayerBodyMesh>>,
|
||||
mut player_head: Query<&mut ActiveHead, With<Player>>,
|
||||
body_mesh: Single<Entity, With<PlayerBodyMesh>>,
|
||||
mut player: Single<&mut ActiveHead, With<Player>>,
|
||||
head_db: Res<HeadsDatabase>,
|
||||
audio_assets: Res<AudioAssets>,
|
||||
) {
|
||||
let Ok(body_mesh) = body_mesh.get_single() else {
|
||||
return;
|
||||
};
|
||||
|
||||
let Ok(mut player) = player_head.get_single_mut() else {
|
||||
return;
|
||||
};
|
||||
let body_mesh = *body_mesh;
|
||||
|
||||
player.0 = trigger.0;
|
||||
|
||||
@@ -215,7 +208,7 @@ fn on_update_head(
|
||||
|
||||
commands.trigger(PlaySound::Head(head_str.to_string()));
|
||||
|
||||
commands.entity(body_mesh).despawn_descendants();
|
||||
commands.entity(body_mesh).despawn_related::<Children>();
|
||||
|
||||
commands
|
||||
.entity(body_mesh)
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
use avian3d::prelude::*;
|
||||
use bevy::ecs::{component::ComponentId, world::DeferredWorld};
|
||||
use bevy::math::*;
|
||||
use bevy::prelude::*;
|
||||
use bevy_trenchbroom::class::Target;
|
||||
use bevy::{
|
||||
ecs::{component::HookContext, world::DeferredWorld},
|
||||
math::*,
|
||||
prelude::*,
|
||||
};
|
||||
use bevy_trenchbroom::prelude::*;
|
||||
|
||||
use crate::cash::Cash;
|
||||
@@ -17,7 +18,7 @@ use crate::physics_layers::GameLayer;
|
||||
pub struct SpawnPoint {}
|
||||
|
||||
impl SpawnPoint {
|
||||
fn on_add(mut world: DeferredWorld, entity: Entity, _id: ComponentId) {
|
||||
fn on_add(mut world: DeferredWorld, HookContext { entity, .. }: HookContext) {
|
||||
let Some(assets) = world.get_resource::<GameAssets>() else {
|
||||
return;
|
||||
};
|
||||
@@ -117,7 +118,7 @@ pub struct EnemySpawn {
|
||||
}
|
||||
|
||||
impl EnemySpawn {
|
||||
fn on_add(mut world: DeferredWorld, entity: Entity, _id: ComponentId) {
|
||||
fn on_add(mut world: DeferredWorld, HookContext { entity, .. }: HookContext) {
|
||||
//TODO: figure out why this crashes if removed
|
||||
let Some(_assets) = world.get_resource::<GameAssets>() else {
|
||||
return;
|
||||
@@ -155,7 +156,7 @@ impl EnemySpawn {
|
||||
pub struct CashSpawn {}
|
||||
|
||||
impl CashSpawn {
|
||||
fn on_add(mut world: DeferredWorld, entity: Entity, _id: ComponentId) {
|
||||
fn on_add(mut world: DeferredWorld, HookContext { entity, .. }: HookContext) {
|
||||
let Some(assets) = world.get_resource::<GameAssets>() else {
|
||||
return;
|
||||
};
|
||||
@@ -167,6 +168,7 @@ impl CashSpawn {
|
||||
SceneRoot(mesh),
|
||||
Cash,
|
||||
Collider::cuboid(2., 3.0, 2.),
|
||||
CollisionEventsEnabled,
|
||||
Sensor,
|
||||
));
|
||||
}
|
||||
|
||||
@@ -18,12 +18,12 @@ pub fn plugin(app: &mut App) {
|
||||
fn face_camera(
|
||||
cam_query: Query<&GlobalTransform, With<MainCamera>>,
|
||||
mut query: Query<
|
||||
(&mut Transform, &Parent, &InheritedVisibility),
|
||||
(&mut Transform, &ChildOf, &InheritedVisibility),
|
||||
(With<Billboard>, Without<MainCamera>),
|
||||
>,
|
||||
parent_transform: Query<&GlobalTransform>,
|
||||
) {
|
||||
let Ok(cam_transform) = cam_query.get_single() else {
|
||||
let Ok(cam_transform) = cam_query.single() else {
|
||||
return;
|
||||
};
|
||||
|
||||
@@ -32,7 +32,7 @@ fn face_camera(
|
||||
continue;
|
||||
}
|
||||
|
||||
let Ok(parent_global) = parent_transform.get(parent.get()) else {
|
||||
let Ok(parent_global) = parent_transform.get(parent.parent()) else {
|
||||
continue;
|
||||
};
|
||||
|
||||
@@ -43,9 +43,9 @@ fn face_camera(
|
||||
|
||||
fn face_camera_no_parent(
|
||||
cam_query: Query<&GlobalTransform, With<MainCamera>>,
|
||||
mut query: Query<&mut Transform, (With<Billboard>, Without<MainCamera>, Without<Parent>)>,
|
||||
mut query: Query<&mut Transform, (With<Billboard>, Without<MainCamera>, Without<ChildOf>)>,
|
||||
) {
|
||||
let Ok(cam_transform) = cam_query.get_single() else {
|
||||
let Ok(cam_transform) = cam_query.single() else {
|
||||
return;
|
||||
};
|
||||
for mut transform in query.iter_mut() {
|
||||
|
||||
@@ -2,5 +2,6 @@ pub mod billboards;
|
||||
pub mod observers;
|
||||
pub mod sprite_3d_animation;
|
||||
pub mod squish_animation;
|
||||
pub mod trail;
|
||||
|
||||
pub(crate) use observers::global_observer;
|
||||
|
||||
@@ -17,7 +17,7 @@ pub fn plugin(app: &mut App) {
|
||||
|
||||
fn global_observers(
|
||||
mut cmds: Commands,
|
||||
query: Query<Entity, (With<Observer>, Without<Parent>, Added<Observer>)>,
|
||||
query: Query<Entity, (With<Observer>, Without<Children>, Added<Observer>)>,
|
||||
mut root: Local<Option<Entity>>,
|
||||
) {
|
||||
if root.is_none() {
|
||||
|
||||
@@ -29,7 +29,7 @@ fn animate_sprite(
|
||||
if atlas.index < length - 1 {
|
||||
atlas.index = atlas.index.saturating_add(1) % length;
|
||||
} else {
|
||||
commands.entity(e).despawn_recursive();
|
||||
commands.entity(e).despawn();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
63
src/utils/trail.rs
Normal file
63
src/utils/trail.rs
Normal file
@@ -0,0 +1,63 @@
|
||||
use bevy::prelude::*;
|
||||
|
||||
use crate::GameState;
|
||||
|
||||
#[derive(Component)]
|
||||
pub struct Trail {
|
||||
points: Vec<Vec3>,
|
||||
col: LinearRgba,
|
||||
}
|
||||
|
||||
impl Trail {
|
||||
pub fn new(pos: Vec3, col: LinearRgba) -> Self {
|
||||
let mut v = Vec::with_capacity(12);
|
||||
v.push(pos);
|
||||
Self { points: v, col }
|
||||
}
|
||||
|
||||
pub fn add(&mut self, pos: Vec3) {
|
||||
if self.points.len() >= self.points.capacity() {
|
||||
self.points.pop();
|
||||
}
|
||||
self.points.insert(0, pos);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn plugin(app: &mut App) {
|
||||
app.add_systems(
|
||||
FixedUpdate,
|
||||
update_trail.run_if(in_state(GameState::Playing)),
|
||||
);
|
||||
}
|
||||
|
||||
fn update_trail(
|
||||
mut query: Query<(&mut Trail, &Gizmo, &GlobalTransform, &ChildOf)>,
|
||||
global_transform: Query<&GlobalTransform>,
|
||||
mut gizmo_assets: ResMut<Assets<GizmoAsset>>,
|
||||
) -> Result<(), BevyError> {
|
||||
for (mut trail, gizmo, pos, parent) in query.iter_mut() {
|
||||
trail.add(pos.translation());
|
||||
|
||||
let parent_transform = global_transform.get(parent.parent())?;
|
||||
|
||||
let Some(gizmo) = gizmo_assets.get_mut(gizmo.handle.id()) else {
|
||||
continue;
|
||||
};
|
||||
|
||||
for window in trail.points.windows(2) {
|
||||
let [a, b] = window else {
|
||||
continue;
|
||||
};
|
||||
let a = GlobalTransform::from_translation(*a)
|
||||
.reparented_to(parent_transform)
|
||||
.translation;
|
||||
let b = GlobalTransform::from_translation(*b)
|
||||
.reparented_to(parent_transform)
|
||||
.translation;
|
||||
|
||||
gizmo.line(a, b, trail.col);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -31,10 +31,11 @@ fn setup(mut commands: Commands, query: Query<(Entity, &Children), With<Water>>)
|
||||
|
||||
commands.entity(e).insert((
|
||||
Sensor,
|
||||
ColliderConstructorHierarchy::new(ColliderConstructor::TrimeshFromMesh),
|
||||
CollisionEventsEnabled,
|
||||
ColliderConstructorHierarchy::new(ColliderConstructor::ConvexHullFromMesh),
|
||||
));
|
||||
|
||||
commands.entity(*child).insert(WaterSensor);
|
||||
commands.entity(child).insert(WaterSensor);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -131,6 +131,16 @@
|
||||
scale(vector) : "Scale" : "1 1 1" : ""
|
||||
]
|
||||
|
||||
@BaseClass = visibility
|
||||
[
|
||||
visibility(choices) : "Visibility" : "Inherited" : "" =
|
||||
[
|
||||
"Inherited" : "Uses the visibility of its parents. If its a root-level entity, it will be visible."
|
||||
"Hidden" : "Always not rendered, regardless of its parent's visibility."
|
||||
"Visible" : "Always rendered, regardless of its parent's visibility."
|
||||
]
|
||||
]
|
||||
|
||||
@SolidClass = water
|
||||
[
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user