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

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()
})),
..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() {
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)

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