Bevy 0.16 upgrade (#24)

This commit is contained in:
extrawurst
2025-04-29 00:14:25 +02:00
committed by GitHub
parent b4bfa53df4
commit 11568d57ed
40 changed files with 932 additions and 699 deletions

1212
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -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" }

View File

@@ -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(),

View File

@@ -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()
});
},
));
}
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,

View File

@@ -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;
};

View File

@@ -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);

View File

@@ -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));
}
}

View File

@@ -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 {

View File

@@ -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;
}

View File

@@ -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.);
}
}

View File

@@ -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>,

View File

@@ -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.;

View File

@@ -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(),

View File

@@ -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;
};

View File

@@ -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 {

View File

@@ -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;
}

View File

@@ -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,
}
}
}

View File

@@ -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;
}

View File

@@ -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;

View File

@@ -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);

View File

@@ -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();
}
}

View File

@@ -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;
};

View File

@@ -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;
};

View File

@@ -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());
}
}

View File

@@ -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();
}
}

View File

@@ -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)]

View File

@@ -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);

View File

@@ -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()
},
));

View File

@@ -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)]

View File

@@ -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()));

View File

@@ -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)]

View File

@@ -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() {
fn toggle_cursor_system(mut window: Single<&mut Window, With<PrimaryWindow>>) {
toggle_grab_cursor(&mut window);
} else {
warn!("Primary window not found for `toggle_cursor_system`!");
}
}
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)

View File

@@ -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,
));
}

View File

@@ -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() {

View File

@@ -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;

View File

@@ -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() {

View File

@@ -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
View 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(())
}

View File

@@ -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);
}
}

View File

@@ -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
[
]