Bevy 0.17 Migration Final PR (#76)

* Get bevy 0.17 compiling and running (#72)

* get bevy 0.17 compiling and running

* try to fix CI breaking from const assertion for client/server features

* fix `bin` -> `lib` for `shared` in CI

* typo

* fix some collider issues (#73)

* Physics/controller improvements (#74)

* trying to fix physics prediction

* fixed prediction desync

* substantial controller improvements

* Finish off main bevy 0.17 migration (#75)

* fix lookdir issues
- airplane moving backwards
- player model facing backwards
- camera was technically backwards the whole time, and player models were facing the right way; camera is now facing forwards
- firing without a target now respects lookdir

* fix aim targeting

* migrate to bevy_trenchbroom 0.10 crates release

* fixed colliders not being adjusted out of worldspace

* predict platforms to stop constant rollbacks while riding them

* fix key/head drop visuals not working

* Fix key/head drop random initial force

* fixed static head drops duplicating

* fix platform velocity inheritance

* fix thrown projectiles not autorotating

* fix inconsistent explosion animations

* update avian3d to 0.4.1

* fix controller snapping to fixed angle upon switching heads

* clean up commented code

* fix broken physics positions

* Clean comments, fix warnings (#77)

* clean comments, fix warnings

* fix missing import

* steamworks 162 libs

* fix mouselook

---------

Co-authored-by: extrawurst <mail@rusticorn.com>
This commit is contained in:
PROMETHIA-27
2025-11-15 09:16:38 -05:00
committed by GitHub
parent ad1b7446e1
commit b83e506a4d
75 changed files with 2514 additions and 1831 deletions

View File

@@ -5,9 +5,8 @@ use crate::{
utils::sprite_3d_animation::AnimationTimer,
};
use avian3d::prelude::*;
use bevy::{pbr::NotShadowCaster, prelude::*};
use bevy_sprite3d::{Sprite3dBuilder, Sprite3dParams};
use std::f32::consts::PI;
use bevy::{light::NotShadowCaster, prelude::*};
use bevy_sprite3d::Sprite3d;
#[derive(Component)]
struct ArrowProjectile {
@@ -27,18 +26,18 @@ pub fn plugin(app: &mut App) {
global_observer!(app, on_trigger_arrow);
}
fn setup(mut commands: Commands, assets: Res<GameAssets>, mut sprite_params: Sprite3dParams) {
fn setup(mut commands: Commands, assets: Res<GameAssets>, asset_server: Res<AssetServer>) {
let layout = TextureAtlasLayout::from_grid(UVec2::splat(256), 7, 6, None, None);
let texture_atlas_layout = sprite_params.atlas_layouts.add(layout);
let layout = asset_server.add(layout);
commands.insert_resource(ShotAssets {
image: assets.impact_atlas.clone(),
layout: texture_atlas_layout,
layout,
});
}
fn on_trigger_arrow(
trigger: Trigger<TriggerArrow>,
trigger: On<TriggerArrow>,
mut commands: Commands,
query_transform: Query<&Transform>,
heads_db: Res<HeadsDatabase>,
@@ -55,7 +54,7 @@ fn on_trigger_arrow(
.looking_at(t.translation, Vec3::Y)
.rotation
} else {
state.rot.mul_quat(Quat::from_rotation_y(PI))
state.rot()
};
let mut transform = Transform::from_translation(state.pos).with_rotation(rotation);
@@ -74,7 +73,6 @@ fn update(
query: Query<(Entity, &Transform, &ArrowProjectile)>,
spatial_query: SpatialQuery,
assets: Res<ShotAssets>,
mut sprite_params: Sprite3dParams,
) {
for (e, t, arrow) in query.iter() {
let filter = SpatialQueryFilter::from_mask(LayerMask(
@@ -89,26 +87,27 @@ fn update(
&ShapeCastConfig::from_max_distance(150.),
&filter,
) {
cmds.entity(first_hit.entity).trigger(Hit {
cmds.trigger(Hit {
damage: arrow.damage,
entity: first_hit.entity,
});
cmds.spawn(
Sprite3dBuilder {
image: assets.image.clone(),
cmds.spawn((
Sprite3d {
pixels_per_metre: 128.,
alpha_mode: AlphaMode::Blend,
unlit: true,
..default()
}
.bundle_with_atlas(
&mut sprite_params,
TextureAtlas {
},
Sprite {
image: assets.image.clone(),
texture_atlas: Some(TextureAtlas {
layout: assets.layout.clone(),
index: 0,
},
),
)
}),
..default()
},
))
.insert((
Billboard::All,
Transform::from_translation(first_hit.point1),

View File

@@ -36,7 +36,7 @@ pub fn plugin(app: &mut App) {
}
fn on_trigger_missile(
trigger: Trigger<TriggerCurver>,
trigger: On<TriggerCurver>,
mut commands: Commands,
query_transform: Query<&Transform>,
time: Res<Time>,
@@ -52,7 +52,7 @@ fn on_trigger_missile(
.looking_at(t.translation, Vec3::Y)
.rotation
} else {
state.rot.mul_quat(Quat::from_rotation_y(PI))
state.rot()
};
let head = heads_db.head_stats(state.head);
@@ -88,11 +88,16 @@ fn on_trigger_missile(
fn enemy_hit(
mut commands: Commands,
mut collision_event_reader: EventReader<CollisionStarted>,
mut collision_message_reader: MessageReader<CollisionStart>,
query_shot: Query<&CurverProjectile>,
query_npc: Query<&EnemySpawn>,
) {
for CollisionStarted(e1, e2) in collision_event_reader.read() {
for CollisionStart {
collider1: e1,
collider2: e2,
..
} in collision_message_reader.read()
{
if !query_shot.contains(*e1) && !query_shot.contains(*e2) {
continue;
}
@@ -108,7 +113,9 @@ fn enemy_hit(
if let Ok(projectile) = projectile {
let damage = projectile.damage;
commands.entity(enemy_entity).trigger(Hit { damage });
commands
.entity(enemy_entity)
.trigger(|entity| Hit { entity, damage });
}
}
}
@@ -132,11 +139,16 @@ fn timeout(mut commands: Commands, query: Query<(Entity, &CurverProjectile)>, ti
fn shot_collision(
mut commands: Commands,
mut collision_event_reader: EventReader<CollisionStarted>,
mut collision_message_reader: MessageReader<CollisionStart>,
query_shot: Query<&Transform, With<CurverProjectile>>,
sensors: Query<(), With<Sensor>>,
) {
for CollisionStarted(e1, e2) in collision_event_reader.read() {
for CollisionStart {
collider1: e1,
collider2: e2,
..
} in collision_message_reader.read()
{
if !query_shot.contains(*e1) && !query_shot.contains(*e2) {
continue;
}

View File

@@ -5,9 +5,8 @@ use crate::{
tb_entities::EnemySpawn, utils::sprite_3d_animation::AnimationTimer,
};
use avian3d::prelude::*;
use bevy::{pbr::NotShadowCaster, prelude::*};
use bevy_sprite3d::{Sprite3dBuilder, Sprite3dParams};
use std::f32::consts::PI;
use bevy::{light::NotShadowCaster, prelude::*};
use bevy_sprite3d::Sprite3d;
#[derive(Component)]
struct GunProjectile {
@@ -37,9 +36,9 @@ pub fn plugin(app: &mut App) {
global_observer!(app, on_trigger_gun);
}
fn setup(mut commands: Commands, assets: Res<GameAssets>, mut sprite_params: Sprite3dParams) {
fn setup(mut commands: Commands, assets: Res<GameAssets>, asset_server: Res<AssetServer>) {
let layout = TextureAtlasLayout::from_grid(UVec2::splat(256), 7, 6, None, None);
let texture_atlas_layout = sprite_params.atlas_layouts.add(layout);
let texture_atlas_layout = asset_server.add(layout);
commands.insert_resource(ShotAssets {
image: assets.impact_atlas.clone(),
@@ -49,12 +48,17 @@ fn setup(mut commands: Commands, assets: Res<GameAssets>, mut sprite_params: Spr
fn enemy_hit(
mut commands: Commands,
mut collision_event_reader: EventReader<CollisionStarted>,
mut collision_message_reader: MessageReader<CollisionStart>,
query_shot: Query<&GunProjectile>,
query_npc: Query<&EnemySpawn>,
heads_db: Res<HeadsDatabase>,
) {
for CollisionStarted(e1, e2) in collision_event_reader.read() {
for CollisionStart {
collider1: e1,
collider2: e2,
..
} in collision_message_reader.read()
{
if !query_shot.contains(*e1) && !query_shot.contains(*e2) {
continue;
}
@@ -70,13 +74,16 @@ fn enemy_hit(
if let Ok(head) = projectile.map(|p| p.owner_head) {
let damage = heads_db.head_stats(head).damage;
commands.entity(enemy_entity).trigger(Hit { damage });
commands.trigger(Hit {
damage,
entity: enemy_entity,
});
}
}
}
fn on_trigger_gun(
trigger: Trigger<TriggerGun>,
trigger: On<TriggerGun>,
mut commands: Commands,
query_transform: Query<&Transform>,
time: Res<Time>,
@@ -94,7 +101,7 @@ fn on_trigger_gun(
.looking_at(t.translation, Vec3::Y)
.rotation
} else {
state.rot.mul_quat(Quat::from_rotation_y(PI))
state.rot()
};
let mut transform = Transform::from_translation(state.pos).with_rotation(rotation);
@@ -149,13 +156,17 @@ fn timeout(mut commands: Commands, query: Query<(Entity, &GunProjectile)>, time:
fn shot_collision(
mut commands: Commands,
mut collision_event_reader: EventReader<CollisionStarted>,
mut collision_message_reader: MessageReader<CollisionStart>,
query_shot: Query<(&GunProjectile, &Transform)>,
sensors: Query<(), With<Sensor>>,
assets: Res<ShotAssets>,
mut sprite_params: Sprite3dParams,
) {
for CollisionStarted(e1, e2) in collision_event_reader.read() {
for CollisionStart {
collider1: e1,
collider2: e2,
..
} in collision_message_reader.read()
{
if !query_shot.contains(*e1) && !query_shot.contains(*e2) {
continue;
}
@@ -180,16 +191,19 @@ fn shot_collision(
};
commands
.spawn(
Sprite3dBuilder {
image: assets.image.clone(),
.spawn((
Sprite3d {
pixels_per_metre: 128.,
alpha_mode: AlphaMode::Blend,
unlit: true,
..default()
}
.bundle_with_atlas(&mut sprite_params, texture_atlas),
)
},
Sprite {
image: assets.image.clone(),
texture_atlas: Some(texture_atlas),
..default()
},
))
.insert((
Billboard::All,
Transform::from_translation(shot_pos),

View File

@@ -8,8 +8,14 @@ use serde::{Deserialize, Serialize};
#[derive(Component, Serialize, Deserialize, PartialEq)]
pub struct Healing;
#[derive(Clone, Event, Debug, Serialize, Deserialize)]
pub enum HealingStateChanged {
#[derive(Clone, EntityEvent, Debug, Serialize, Deserialize)]
pub struct HealingStateChanged {
pub entity: Entity,
pub state: HealingState,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub enum HealingState {
Started,
Stopped,
}
@@ -21,24 +27,24 @@ pub fn plugin(app: &mut App) {
}
fn on_heal_start_stop(
trigger: Trigger<HealingStateChanged>,
trigger: On<HealingStateChanged>,
mut cmds: Commands,
query: Query<&Healing>,
) {
if matches!(trigger.event(), HealingStateChanged::Started) {
if query.contains(trigger.target()) {
if matches!(trigger.event().state, HealingState::Started) {
if query.contains(trigger.event().entity) {
// already healing, just ignore
return;
}
cmds.entity(trigger.target()).insert(Healing);
cmds.entity(trigger.event().entity).insert(Healing);
} else {
if !query.contains(trigger.target()) {
if !query.contains(trigger.event().entity) {
// Not healing, just ignore
return;
}
cmds.entity(trigger.target()).remove::<Healing>();
cmds.entity(trigger.event().entity).remove::<Healing>();
}
}

View File

@@ -36,7 +36,7 @@ pub fn plugin(app: &mut App) {
}
fn on_trigger_missile(
trigger: Trigger<TriggerMissile>,
trigger: On<TriggerMissile>,
mut commands: Commands,
query_transform: Query<&Transform>,
time: Res<Time>,
@@ -53,7 +53,7 @@ fn on_trigger_missile(
.looking_at(t.translation, Vec3::Y)
.rotation
} else {
state.rot.mul_quat(Quat::from_rotation_y(PI))
state.rot()
};
let head = heads_db.head_stats(state.head);
@@ -122,11 +122,16 @@ fn timeout(mut commands: Commands, query: Query<(Entity, &MissileProjectile)>, t
fn shot_collision(
mut commands: Commands,
mut collision_event_reader: EventReader<CollisionStarted>,
mut collision_message_reader: MessageReader<CollisionStart>,
query_shot: Query<(&MissileProjectile, &Transform)>,
sensors: Query<(), With<Sensor>>,
) {
for CollisionStarted(e1, e2) in collision_event_reader.read() {
for CollisionStart {
collider1: e1,
collider2: e2,
..
} in collision_message_reader.read()
{
if !query_shot.contains(*e1) && !query_shot.contains(*e2) {
continue;
}

View File

@@ -14,17 +14,12 @@ use crate::{
};
#[cfg(feature = "server")]
use crate::{
aim::AimTarget,
character::CharacterHierarchy,
control::ControlState,
head::ActiveHead,
heads::ActiveHeads,
heads_database::HeadsDatabase,
player::{Player, PlayerBodyMesh},
aim::AimTarget, character::CharacterHierarchy, control::ControlState, head::ActiveHead,
heads::ActiveHeads, heads_database::HeadsDatabase, player::Player,
utils::explosions::Explosion,
};
use bevy::{pbr::NotShadowCaster, prelude::*};
use bevy_sprite3d::{Sprite3dBuilder, Sprite3dParams};
use bevy::{light::NotShadowCaster, prelude::*};
use bevy_sprite3d::Sprite3d;
pub use healing::Healing;
#[cfg(feature = "server")]
use healing::HealingStateChanged;
@@ -51,7 +46,6 @@ pub enum HeadAbility {
pub struct TriggerData {
target: Option<Entity>,
dir: Dir3,
rot: Quat,
pos: Vec3,
target_layer: GameLayer,
head: usize,
@@ -61,7 +55,6 @@ impl TriggerData {
pub fn new(
target: Option<Entity>,
dir: Dir3,
rot: Quat,
pos: Vec3,
target_layer: GameLayer,
head: usize,
@@ -69,12 +62,17 @@ impl TriggerData {
Self {
target,
dir,
rot,
pos,
target_layer,
head,
}
}
pub fn rot(&self) -> Quat {
// as it turns out, `glam` comes with some `looking_to` functions for left and right handed coordinate systems, but it seems like they're wrong?
// at the very least they give some very odd results and the cross multiplications inside all look backwards compared to what bevy transforms do.
Transform::default().looking_to(self.dir, Vec3::Y).rotation
}
}
#[derive(Event, Reflect)]
@@ -205,8 +203,7 @@ fn on_trigger_state(
fn update(
mut res: ResMut<TriggerStateRes>,
mut commands: Commands,
player_rot: Query<&Transform, With<PlayerBodyMesh>>,
player_query: Query<(Entity, &AimTarget), With<Player>>,
player_query: Query<(Entity, &AimTarget, &ActionState<ControlState>), With<Player>>,
mut active_heads: Single<&mut ActiveHeads, With<Player>>,
heads_db: Res<HeadsDatabase>,
time: Res<Time>,
@@ -222,7 +219,7 @@ fn update(
return;
}
let Some((player, target)) = player_query.iter().next() else {
let Some((player, target, actions)) = player_query.iter().next() else {
return;
};
@@ -233,10 +230,6 @@ fn update(
return;
};
let Some((rot, dir)) = player_rot.iter().next().map(|t| (t.rotation, t.forward())) else {
return;
};
let head = heads_db.head_stats(state.head);
if matches!(head.ability, HeadAbility::None | HeadAbility::Medic) {
@@ -248,8 +241,7 @@ fn update(
res.next_trigger_timestamp = time.elapsed_secs() + (1. / head.aps);
let trigger_state = TriggerData {
dir,
rot,
dir: actions.look_dir,
pos: projectile_origin,
target: target.0,
target_layer: GameLayer::Npc,
@@ -287,10 +279,17 @@ fn update_heal_ability(
return;
}
use crate::abilities::healing::HealingState;
if res.active {
commands.trigger_targets(HealingStateChanged::Started, player);
commands.trigger(HealingStateChanged {
state: HealingState::Started,
entity: player,
});
} else {
commands.trigger_targets(HealingStateChanged::Stopped, player);
commands.trigger(HealingStateChanged {
state: HealingState::Stopped,
entity: player,
});
}
}
}
@@ -301,9 +300,9 @@ struct ShotAssets {
layout: Handle<TextureAtlasLayout>,
}
fn setup(mut commands: Commands, assets: Res<GameAssets>, mut sprite_params: Sprite3dParams) {
fn setup(mut commands: Commands, assets: Res<GameAssets>, asset_server: Res<AssetServer>) {
let layout = TextureAtlasLayout::from_grid(UVec2::splat(256), 7, 6, None, None);
let texture_atlas_layout = sprite_params.atlas_layouts.add(layout);
let texture_atlas_layout = asset_server.add(layout);
commands.insert_resource(ShotAssets {
image: assets.impact_atlas.clone(),
@@ -319,27 +318,26 @@ pub struct BuildExplosionSprite {
}
fn build_explosion_sprite(
trigger: Trigger<BuildExplosionSprite>,
trigger: On<BuildExplosionSprite>,
mut commands: Commands,
assets: Res<ShotAssets>,
mut sprite_params: Sprite3dParams,
) {
commands.spawn((
Transform::from_translation(trigger.event().pos),
Sprite3dBuilder {
image: assets.image.clone(),
Sprite3d {
pixels_per_metre: trigger.event().pixels_per_meter,
alpha_mode: AlphaMode::Blend,
unlit: true,
..default()
}
.bundle_with_atlas(
&mut sprite_params,
TextureAtlas {
},
Sprite {
image: assets.image.clone(),
texture_atlas: Some(TextureAtlas {
layout: assets.layout.clone(),
index: 0,
},
),
}),
..default()
},
Billboard::All,
NotShadowCaster,
AnimationTimer::new(Timer::from_seconds(

View File

@@ -12,7 +12,6 @@ use bevy_ballistic::launch_velocity;
#[cfg(feature = "server")]
use lightyear::prelude::{NetworkTarget, Replicate};
use serde::{Deserialize, Serialize};
use std::f32::consts::PI;
#[derive(Component, Serialize, Deserialize, PartialEq)]
pub struct ThrownProjectile {
@@ -30,7 +29,7 @@ pub fn plugin(app: &mut App) {
}
fn on_trigger_thrown(
trigger: Trigger<TriggerThrow>,
trigger: On<TriggerThrow>,
mut commands: Commands,
query_transform: Query<&Transform>,
heads_db: Res<HeadsDatabase>,
@@ -50,8 +49,7 @@ fn on_trigger_thrown(
.map(|(low, _)| low)
.unwrap()
} else {
state.rot.mul_quat(Quat::from_rotation_y(-PI / 2.))
* (Vec3::new(2., 1., 0.).normalize() * SPEED)
((state.dir.as_vec3() * 2.0) + Vec3::Y).normalize() * SPEED
};
let head = heads_db.head_stats(state.head);
@@ -89,11 +87,16 @@ fn on_trigger_thrown(
fn shot_collision(
mut commands: Commands,
mut collision_event_reader: EventReader<CollisionStarted>,
mut collision_message_reader: MessageReader<CollisionStart>,
query_shot: Query<(&ThrownProjectile, &Transform)>,
sensors: Query<(), With<Sensor>>,
) {
for CollisionStarted(e1, e2) in collision_event_reader.read() {
for CollisionStart {
collider1: e1,
collider2: e2,
..
} in collision_message_reader.read()
{
if !query_shot.contains(*e1) && !query_shot.contains(*e2) {
continue;
}