more clear splitting of visual stuff in client mod
This commit is contained in:
@@ -1,8 +1,11 @@
|
||||
use super::TriggerArrow;
|
||||
use crate::{
|
||||
GameState, billboards::Billboard, global_observer, heads_database::HeadsDatabase,
|
||||
hitpoints::Hit, loading_assets::GameAssets, physics_layers::GameLayer,
|
||||
utils::sprite_3d_animation::AnimationTimer,
|
||||
GameState, global_observer,
|
||||
heads_database::HeadsDatabase,
|
||||
hitpoints::Hit,
|
||||
loading_assets::GameAssets,
|
||||
physics_layers::GameLayer,
|
||||
utils::{Billboard, sprite_3d_animation::AnimationTimer},
|
||||
};
|
||||
use avian3d::prelude::*;
|
||||
use bevy::{light::NotShadowCaster, prelude::*};
|
||||
|
||||
@@ -1,8 +1,14 @@
|
||||
use super::TriggerGun;
|
||||
use crate::{
|
||||
GameState, abilities::ProjectileId, billboards::Billboard, global_observer,
|
||||
heads_database::HeadsDatabase, hitpoints::Hit, loading_assets::GameAssets,
|
||||
physics_layers::GameLayer, tb_entities::EnemySpawn, utils::sprite_3d_animation::AnimationTimer,
|
||||
GameState,
|
||||
abilities::ProjectileId,
|
||||
global_observer,
|
||||
heads_database::HeadsDatabase,
|
||||
hitpoints::Hit,
|
||||
loading_assets::GameAssets,
|
||||
physics_layers::GameLayer,
|
||||
tb_entities::EnemySpawn,
|
||||
utils::{Billboard, sprite_3d_animation::AnimationTimer},
|
||||
};
|
||||
use avian3d::prelude::*;
|
||||
use bevy::{light::NotShadowCaster, prelude::*};
|
||||
|
||||
@@ -17,7 +17,7 @@ use crate::{
|
||||
physics_layers::GameLayer,
|
||||
player::Player,
|
||||
protocol::PlaySound,
|
||||
utils::{billboards::Billboard, explosions::Explosion, sprite_3d_animation::AnimationTimer},
|
||||
utils::{Billboard, explosions::Explosion, sprite_3d_animation::AnimationTimer},
|
||||
};
|
||||
use bevy::{light::NotShadowCaster, prelude::*};
|
||||
use bevy_replicon::prelude::{SendMode, ServerTriggerExt, Signature, ToClients};
|
||||
|
||||
@@ -1,19 +1,3 @@
|
||||
use crate::GameState;
|
||||
#[cfg(feature = "client")]
|
||||
use crate::control::Inputs;
|
||||
use crate::control::ViewMode;
|
||||
#[cfg(feature = "client")]
|
||||
use crate::physics_layers::GameLayer;
|
||||
#[cfg(feature = "client")]
|
||||
use crate::player::LocalPlayer;
|
||||
#[cfg(feature = "client")]
|
||||
use crate::{control::LookDirMovement, loading_assets::UIAssets};
|
||||
#[cfg(feature = "client")]
|
||||
use avian3d::prelude::SpatialQuery;
|
||||
#[cfg(feature = "client")]
|
||||
use avian3d::prelude::{
|
||||
Collider, LayerMask, PhysicsLayer as _, ShapeCastConfig, SpatialQueryFilter,
|
||||
};
|
||||
use bevy::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
@@ -22,183 +6,3 @@ pub struct CameraTarget;
|
||||
|
||||
#[derive(Component, Reflect, Debug, Serialize, Deserialize, PartialEq)]
|
||||
pub struct CameraArmRotation;
|
||||
|
||||
/// Requested camera rotation based on various input sources (keyboard, gamepad)
|
||||
#[derive(Component, Reflect, Debug, Default, Deref, DerefMut)]
|
||||
#[reflect(Component)]
|
||||
pub struct CameraRotationInput(pub Vec2);
|
||||
|
||||
#[derive(Resource, Reflect, Debug, Default)]
|
||||
#[reflect(Resource)]
|
||||
pub struct CameraState {
|
||||
pub cutscene: bool,
|
||||
pub view_mode: ViewMode,
|
||||
}
|
||||
|
||||
#[derive(Component, Reflect, Debug, Default)]
|
||||
struct CameraUi;
|
||||
|
||||
#[derive(Component, Reflect, Debug)]
|
||||
#[reflect(Component)]
|
||||
pub struct MainCamera {
|
||||
pub enabled: bool,
|
||||
dir: Dir3,
|
||||
distance: f32,
|
||||
target_offset: Vec3,
|
||||
}
|
||||
|
||||
impl MainCamera {
|
||||
fn new(arm: Vec3) -> Self {
|
||||
let (dir, distance) = Dir3::new_and_length(arm).expect("invalid arm length");
|
||||
Self {
|
||||
enabled: true,
|
||||
dir,
|
||||
distance,
|
||||
target_offset: Vec3::new(0., 2., 0.),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn plugin(app: &mut App) {
|
||||
app.register_type::<CameraRotationInput>();
|
||||
app.register_type::<CameraState>();
|
||||
app.register_type::<MainCamera>();
|
||||
|
||||
app.init_resource::<CameraState>();
|
||||
app.add_systems(OnEnter(GameState::Playing), startup);
|
||||
#[cfg(feature = "client")]
|
||||
app.add_systems(
|
||||
PostUpdate,
|
||||
(update, update_ui, update_look_around, rotate_view).run_if(in_state(GameState::Playing)),
|
||||
);
|
||||
}
|
||||
|
||||
fn startup(mut commands: Commands) {
|
||||
commands.spawn((
|
||||
Camera3d::default(),
|
||||
MainCamera::new(Vec3::new(0., 1.8, 15.)),
|
||||
CameraRotationInput::default(),
|
||||
));
|
||||
}
|
||||
|
||||
#[cfg(feature = "client")]
|
||||
fn update_look_around(
|
||||
inputs: Single<&Inputs, With<LocalPlayer>>,
|
||||
mut cam_state: ResMut<CameraState>,
|
||||
) {
|
||||
let view_mode = inputs.view_mode;
|
||||
|
||||
if view_mode != cam_state.view_mode {
|
||||
cam_state.view_mode = view_mode;
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "client")]
|
||||
fn update_ui(
|
||||
mut commands: Commands,
|
||||
cam_state: Res<CameraState>,
|
||||
assets: Res<UIAssets>,
|
||||
query: Query<Entity, With<CameraUi>>,
|
||||
) {
|
||||
if cam_state.is_changed() {
|
||||
let show_free_cam_ui = cam_state.view_mode.is_free() || cam_state.cutscene;
|
||||
|
||||
if show_free_cam_ui {
|
||||
commands.spawn((
|
||||
CameraUi,
|
||||
Node {
|
||||
margin: UiRect::top(Val::Px(20.))
|
||||
.with_left(Val::Auto)
|
||||
.with_right(Val::Auto),
|
||||
justify_content: JustifyContent::Center,
|
||||
..default()
|
||||
},
|
||||
children![(
|
||||
Node {
|
||||
display: Display::Block,
|
||||
position_type: PositionType::Absolute,
|
||||
..default()
|
||||
},
|
||||
ImageNode::new(assets.camera.clone()),
|
||||
)],
|
||||
));
|
||||
} else {
|
||||
for entity in query.iter() {
|
||||
commands.entity(entity).despawn();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "client")]
|
||||
fn update(
|
||||
cam: Single<
|
||||
(&MainCamera, &mut Transform, &CameraRotationInput),
|
||||
(Without<CameraTarget>, Without<CameraArmRotation>),
|
||||
>,
|
||||
target_q: Single<
|
||||
(&Transform, &Children),
|
||||
(
|
||||
With<CameraTarget>,
|
||||
With<LocalPlayer>,
|
||||
Without<CameraArmRotation>,
|
||||
),
|
||||
>,
|
||||
arm_rotation: Query<&Transform, With<CameraArmRotation>>,
|
||||
spatial_query: SpatialQuery,
|
||||
cam_state: Res<CameraState>,
|
||||
) {
|
||||
if cam_state.cutscene {
|
||||
return;
|
||||
}
|
||||
|
||||
let (camera, mut cam_transform, cam_rotation_input) = cam.into_inner();
|
||||
|
||||
let (target_q, children) = target_q.into_inner();
|
||||
|
||||
let arm_tf = children
|
||||
.iter()
|
||||
.find_map(|child| arm_rotation.get(child).ok())
|
||||
.unwrap();
|
||||
|
||||
if !camera.enabled {
|
||||
return;
|
||||
}
|
||||
|
||||
let target = target_q.translation + camera.target_offset;
|
||||
|
||||
let direction = arm_tf.rotation * Quat::from_rotation_y(cam_rotation_input.x) * camera.dir;
|
||||
|
||||
let max_distance = camera.distance;
|
||||
|
||||
let filter = SpatialQueryFilter::from_mask(LayerMask(GameLayer::Level.to_bits()));
|
||||
let cam_pos = if let Some(first_hit) = spatial_query.cast_shape(
|
||||
&Collider::sphere(0.5),
|
||||
target,
|
||||
Quat::IDENTITY,
|
||||
direction,
|
||||
&ShapeCastConfig::from_max_distance(max_distance),
|
||||
&filter,
|
||||
) {
|
||||
let distance = first_hit.distance;
|
||||
target + (direction * distance)
|
||||
} else {
|
||||
target + (direction * camera.distance)
|
||||
};
|
||||
|
||||
*cam_transform = Transform::from_translation(cam_pos).looking_at(target, Vec3::Y);
|
||||
}
|
||||
|
||||
#[cfg(feature = "client")]
|
||||
fn rotate_view(
|
||||
inputs: Single<&Inputs, With<LocalPlayer>>,
|
||||
look_dir: Res<LookDirMovement>,
|
||||
mut cam: Single<&mut CameraRotationInput>,
|
||||
) {
|
||||
if !inputs.view_mode.is_free() {
|
||||
cam.x = 0.0;
|
||||
return;
|
||||
}
|
||||
|
||||
cam.0 += look_dir.0 * -0.001;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
use crate::{
|
||||
GameState, aim::MarkerEvent, global_observer, loading_assets::UIAssets,
|
||||
utils::billboards::Billboard,
|
||||
GameState, aim::MarkerEvent, global_observer, loading_assets::UIAssets, utils::Billboard,
|
||||
};
|
||||
use bevy::prelude::*;
|
||||
use bevy_sprite3d::Sprite3d;
|
||||
|
||||
193
crates/hedz_reloaded/src/client/camera.rs
Normal file
193
crates/hedz_reloaded/src/client/camera.rs
Normal file
@@ -0,0 +1,193 @@
|
||||
use crate::{
|
||||
GameState,
|
||||
camera::{CameraArmRotation, CameraTarget},
|
||||
control::{Inputs, LookDirMovement, ViewMode},
|
||||
loading_assets::UIAssets,
|
||||
physics_layers::GameLayer,
|
||||
player::LocalPlayer,
|
||||
};
|
||||
use avian3d::prelude::SpatialQuery;
|
||||
use avian3d::prelude::{
|
||||
Collider, LayerMask, PhysicsLayer as _, ShapeCastConfig, SpatialQueryFilter,
|
||||
};
|
||||
use bevy::prelude::*;
|
||||
|
||||
/// Requested camera rotation based on various input sources (keyboard, gamepad)
|
||||
#[derive(Component, Reflect, Debug, Default, Deref, DerefMut)]
|
||||
#[reflect(Component)]
|
||||
pub struct CameraRotationInput(pub Vec2);
|
||||
|
||||
#[derive(Resource, Reflect, Debug, Default)]
|
||||
#[reflect(Resource)]
|
||||
pub struct CameraState {
|
||||
pub cutscene: bool,
|
||||
pub view_mode: ViewMode,
|
||||
}
|
||||
|
||||
#[derive(Component, Reflect, Debug, Default)]
|
||||
struct CameraUi;
|
||||
|
||||
#[derive(Component, Reflect, Debug)]
|
||||
#[reflect(Component)]
|
||||
pub struct MainCamera {
|
||||
pub enabled: bool,
|
||||
dir: Dir3,
|
||||
distance: f32,
|
||||
target_offset: Vec3,
|
||||
}
|
||||
|
||||
impl MainCamera {
|
||||
fn new(arm: Vec3) -> Self {
|
||||
let (dir, distance) = Dir3::new_and_length(arm).expect("invalid arm length");
|
||||
Self {
|
||||
enabled: true,
|
||||
dir,
|
||||
distance,
|
||||
target_offset: Vec3::new(0., 2., 0.),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn plugin(app: &mut App) {
|
||||
app.register_type::<CameraRotationInput>();
|
||||
app.register_type::<CameraState>();
|
||||
app.register_type::<MainCamera>();
|
||||
|
||||
app.init_resource::<CameraState>();
|
||||
|
||||
app.add_systems(OnEnter(GameState::Playing), startup);
|
||||
|
||||
app.add_systems(
|
||||
PostUpdate,
|
||||
(update, update_ui, update_look_around, rotate_view).run_if(in_state(GameState::Playing)),
|
||||
);
|
||||
}
|
||||
|
||||
fn startup(mut commands: Commands) {
|
||||
commands.spawn((
|
||||
Camera3d::default(),
|
||||
MainCamera::new(Vec3::new(0., 1.8, 15.)),
|
||||
CameraRotationInput::default(),
|
||||
));
|
||||
}
|
||||
|
||||
#[cfg(feature = "client")]
|
||||
fn update_look_around(
|
||||
inputs: Single<&Inputs, With<LocalPlayer>>,
|
||||
mut cam_state: ResMut<CameraState>,
|
||||
) {
|
||||
let view_mode = inputs.view_mode;
|
||||
|
||||
if view_mode != cam_state.view_mode {
|
||||
cam_state.view_mode = view_mode;
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "client")]
|
||||
fn update_ui(
|
||||
mut commands: Commands,
|
||||
cam_state: Res<CameraState>,
|
||||
assets: Res<UIAssets>,
|
||||
query: Query<Entity, With<CameraUi>>,
|
||||
) {
|
||||
if cam_state.is_changed() {
|
||||
let show_free_cam_ui = cam_state.view_mode.is_free() || cam_state.cutscene;
|
||||
|
||||
if show_free_cam_ui {
|
||||
commands.spawn((
|
||||
CameraUi,
|
||||
Node {
|
||||
margin: UiRect::top(Val::Px(20.))
|
||||
.with_left(Val::Auto)
|
||||
.with_right(Val::Auto),
|
||||
justify_content: JustifyContent::Center,
|
||||
..default()
|
||||
},
|
||||
children![(
|
||||
Node {
|
||||
display: Display::Block,
|
||||
position_type: PositionType::Absolute,
|
||||
..default()
|
||||
},
|
||||
ImageNode::new(assets.camera.clone()),
|
||||
)],
|
||||
));
|
||||
} else {
|
||||
for entity in query.iter() {
|
||||
commands.entity(entity).despawn();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn update(
|
||||
cam: Single<
|
||||
(&MainCamera, &mut Transform, &CameraRotationInput),
|
||||
(Without<CameraTarget>, Without<CameraArmRotation>),
|
||||
>,
|
||||
target_q: Single<
|
||||
(&Transform, &Children),
|
||||
(
|
||||
With<CameraTarget>,
|
||||
With<LocalPlayer>,
|
||||
Without<CameraArmRotation>,
|
||||
),
|
||||
>,
|
||||
arm_rotation: Query<&Transform, With<CameraArmRotation>>,
|
||||
spatial_query: SpatialQuery,
|
||||
cam_state: Res<CameraState>,
|
||||
) {
|
||||
if cam_state.cutscene {
|
||||
return;
|
||||
}
|
||||
|
||||
let (camera, mut cam_transform, cam_rotation_input) = cam.into_inner();
|
||||
|
||||
let (target_q, children) = target_q.into_inner();
|
||||
|
||||
let arm_tf = children
|
||||
.iter()
|
||||
.find_map(|child| arm_rotation.get(child).ok())
|
||||
.unwrap();
|
||||
|
||||
if !camera.enabled {
|
||||
return;
|
||||
}
|
||||
|
||||
let target = target_q.translation + camera.target_offset;
|
||||
|
||||
let direction = arm_tf.rotation * Quat::from_rotation_y(cam_rotation_input.x) * camera.dir;
|
||||
|
||||
let max_distance = camera.distance;
|
||||
|
||||
let filter = SpatialQueryFilter::from_mask(LayerMask(GameLayer::Level.to_bits()));
|
||||
let cam_pos = if let Some(first_hit) = spatial_query.cast_shape(
|
||||
&Collider::sphere(0.5),
|
||||
target,
|
||||
Quat::IDENTITY,
|
||||
direction,
|
||||
&ShapeCastConfig::from_max_distance(max_distance),
|
||||
&filter,
|
||||
) {
|
||||
let distance = first_hit.distance;
|
||||
target + (direction * distance)
|
||||
} else {
|
||||
target + (direction * camera.distance)
|
||||
};
|
||||
|
||||
*cam_transform = Transform::from_translation(cam_pos).looking_at(target, Vec3::Y);
|
||||
}
|
||||
|
||||
#[cfg(feature = "client")]
|
||||
fn rotate_view(
|
||||
inputs: Single<&Inputs, With<LocalPlayer>>,
|
||||
look_dir: Res<LookDirMovement>,
|
||||
mut cam: Single<&mut CameraRotationInput>,
|
||||
) {
|
||||
if !inputs.view_mode.is_free() {
|
||||
cam.x = 0.0;
|
||||
return;
|
||||
}
|
||||
|
||||
cam.0 += look_dir.0 * -0.001;
|
||||
}
|
||||
@@ -26,13 +26,13 @@ fn rotate_rig(
|
||||
return;
|
||||
}
|
||||
|
||||
let (local_player_childer, selected_controller) = *local_player;
|
||||
let (local_player_children, selected_controller) = *local_player;
|
||||
|
||||
if !matches!(selected_controller, SelectedController::Flying) {
|
||||
return;
|
||||
}
|
||||
|
||||
local_player_childer.iter().find(|&child| {
|
||||
local_player_children.iter().find(|&child| {
|
||||
if let Ok(mut rig_transform) = player_mesh.get_mut(child) {
|
||||
let look_dir = look_dir.0;
|
||||
|
||||
|
||||
104
crates/hedz_reloaded/src/client/cutscene.rs
Normal file
104
crates/hedz_reloaded/src/client/cutscene.rs
Normal file
@@ -0,0 +1,104 @@
|
||||
use crate::{
|
||||
GameState,
|
||||
client::camera::{CameraState, MainCamera},
|
||||
cutscene::StartCutscene,
|
||||
global_observer,
|
||||
tb_entities::{CameraTarget, CutsceneCamera, CutsceneCameraMovementEnd},
|
||||
};
|
||||
use bevy::prelude::*;
|
||||
use bevy_trenchbroom::prelude::*;
|
||||
|
||||
#[derive(Resource, Debug, Default)]
|
||||
enum CutsceneState {
|
||||
#[default]
|
||||
None,
|
||||
Playing {
|
||||
timer: Timer,
|
||||
camera_start: Transform,
|
||||
camera_end: Transform,
|
||||
},
|
||||
}
|
||||
|
||||
pub fn plugin(app: &mut App) {
|
||||
app.init_resource::<CutsceneState>();
|
||||
app.add_systems(Update, update.run_if(in_state(GameState::Playing)));
|
||||
|
||||
global_observer!(app, on_start_cutscene);
|
||||
}
|
||||
|
||||
fn on_start_cutscene(
|
||||
trigger: On<StartCutscene>,
|
||||
mut cam_state: ResMut<CameraState>,
|
||||
mut cutscene_state: ResMut<CutsceneState>,
|
||||
cutscenes: Query<(&Transform, &CutsceneCamera, &Target), Without<MainCamera>>,
|
||||
cutscene_movement: Query<
|
||||
(&Transform, &CutsceneCameraMovementEnd, &Target),
|
||||
Without<MainCamera>,
|
||||
>,
|
||||
cam_target: Query<(&Transform, &CameraTarget), Without<MainCamera>>,
|
||||
) {
|
||||
let cutscene = trigger.event().0.clone();
|
||||
|
||||
cam_state.cutscene = true;
|
||||
|
||||
// asumes `name` and `targetname` are equal
|
||||
let Some((t, _, target)) = cutscenes
|
||||
.iter()
|
||||
.find(|(_, cutscene_camera, _)| cutscene == cutscene_camera.name)
|
||||
else {
|
||||
return;
|
||||
};
|
||||
|
||||
let move_end = cutscene_movement
|
||||
.iter()
|
||||
.find(|(_, _, target)| cutscene == target.target.clone().unwrap_or_default())
|
||||
.map(|(t, _, _)| *t)
|
||||
.unwrap_or_else(|| *t);
|
||||
|
||||
let Some((target, _)) = cam_target.iter().find(|(_, camera_target)| {
|
||||
camera_target.targetname == target.target.clone().unwrap_or_default()
|
||||
}) else {
|
||||
return;
|
||||
};
|
||||
|
||||
*cutscene_state = CutsceneState::Playing {
|
||||
timer: Timer::from_seconds(2.0, TimerMode::Once),
|
||||
camera_start: t.looking_at(target.translation, Vec3::Y),
|
||||
camera_end: move_end.looking_at(target.translation, Vec3::Y),
|
||||
};
|
||||
}
|
||||
|
||||
fn update(
|
||||
mut cam_state: ResMut<CameraState>,
|
||||
mut cutscene_state: ResMut<CutsceneState>,
|
||||
mut cam: Query<&mut Transform, With<MainCamera>>,
|
||||
time: Res<Time>,
|
||||
) {
|
||||
if let CutsceneState::Playing {
|
||||
timer,
|
||||
camera_start,
|
||||
camera_end,
|
||||
} = &mut *cutscene_state
|
||||
{
|
||||
cam_state.cutscene = true;
|
||||
timer.tick(time.delta());
|
||||
|
||||
let t = Transform::from_translation(
|
||||
camera_start
|
||||
.translation
|
||||
.lerp(camera_end.translation, timer.fraction()),
|
||||
)
|
||||
.with_rotation(
|
||||
camera_start
|
||||
.rotation
|
||||
.lerp(camera_end.rotation, timer.fraction()),
|
||||
);
|
||||
|
||||
let _ = cam.single_mut().map(|mut cam| *cam = t);
|
||||
|
||||
if timer.is_finished() {
|
||||
cam_state.cutscene = false;
|
||||
*cutscene_state = CutsceneState::None;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@ use crate::{
|
||||
GameState,
|
||||
abilities::Healing,
|
||||
loading_assets::{AudioAssets, GameAssets},
|
||||
utils::{billboards::Billboard, observers::global_observer},
|
||||
utils::{Billboard, observers::global_observer},
|
||||
};
|
||||
use bevy::prelude::*;
|
||||
use rand::{Rng, thread_rng};
|
||||
|
||||
@@ -25,7 +25,9 @@ use bevy_trenchbroom::geometry::Brushes;
|
||||
pub mod aim;
|
||||
pub mod audio;
|
||||
mod backpack;
|
||||
pub mod camera;
|
||||
pub mod control;
|
||||
pub mod cutscene;
|
||||
pub mod debug;
|
||||
pub mod enemy;
|
||||
pub mod heal_effect;
|
||||
@@ -34,6 +36,7 @@ mod settings;
|
||||
pub mod setup;
|
||||
pub mod steam;
|
||||
pub mod ui;
|
||||
mod utils;
|
||||
|
||||
pub fn plugin(app: &mut App) {
|
||||
app.add_plugins((
|
||||
@@ -49,6 +52,9 @@ pub fn plugin(app: &mut App) {
|
||||
ui::plugin,
|
||||
settings::plugin,
|
||||
backpack::plugin,
|
||||
camera::plugin,
|
||||
utils::billboards::plugin,
|
||||
cutscene::plugin,
|
||||
));
|
||||
|
||||
app.add_systems(
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::{DebugVisuals, camera::MainCamera};
|
||||
use crate::{DebugVisuals, client::camera::MainCamera};
|
||||
use bevy::{core_pipeline::tonemapping::Tonemapping, prelude::*, render::view::ColorGrading};
|
||||
use bevy_trenchbroom::TrenchBroomServer;
|
||||
|
||||
|
||||
@@ -1,22 +1,12 @@
|
||||
use crate::camera::MainCamera;
|
||||
use crate::{client::camera::MainCamera, utils::Billboard};
|
||||
use bevy::prelude::*;
|
||||
use bevy_sprite3d::Sprite3dPlugin;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Component, Reflect, Default, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[reflect(Component)]
|
||||
pub enum Billboard {
|
||||
#[default]
|
||||
All,
|
||||
XZ,
|
||||
}
|
||||
|
||||
pub fn plugin(app: &mut App) {
|
||||
if !app.is_plugin_added::<Sprite3dPlugin>() {
|
||||
app.add_plugins(Sprite3dPlugin);
|
||||
}
|
||||
|
||||
app.register_type::<Billboard>();
|
||||
app.add_systems(Update, (face_camera, face_camera_no_parent));
|
||||
}
|
||||
|
||||
1
crates/hedz_reloaded/src/client/utils/mod.rs
Normal file
1
crates/hedz_reloaded/src/client/utils/mod.rs
Normal file
@@ -0,0 +1 @@
|
||||
pub mod billboards;
|
||||
@@ -1,107 +1,5 @@
|
||||
use crate::{
|
||||
GameState,
|
||||
camera::{CameraState, MainCamera},
|
||||
global_observer,
|
||||
tb_entities::{CameraTarget, CutsceneCamera, CutsceneCameraMovementEnd},
|
||||
};
|
||||
use bevy::prelude::*;
|
||||
use bevy_trenchbroom::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Clone, Debug, Event, Serialize, Deserialize)]
|
||||
pub struct StartCutscene(pub String);
|
||||
|
||||
#[derive(Resource, Debug, Default)]
|
||||
enum CutsceneState {
|
||||
#[default]
|
||||
None,
|
||||
Playing {
|
||||
timer: Timer,
|
||||
camera_start: Transform,
|
||||
camera_end: Transform,
|
||||
},
|
||||
}
|
||||
|
||||
pub fn plugin(app: &mut App) {
|
||||
app.init_resource::<CutsceneState>();
|
||||
app.add_systems(Update, update.run_if(in_state(GameState::Playing)));
|
||||
|
||||
global_observer!(app, on_start_cutscene);
|
||||
}
|
||||
|
||||
fn on_start_cutscene(
|
||||
trigger: On<StartCutscene>,
|
||||
mut cam_state: ResMut<CameraState>,
|
||||
mut cutscene_state: ResMut<CutsceneState>,
|
||||
cutscenes: Query<(&Transform, &CutsceneCamera, &Target), Without<MainCamera>>,
|
||||
cutscene_movement: Query<
|
||||
(&Transform, &CutsceneCameraMovementEnd, &Target),
|
||||
Without<MainCamera>,
|
||||
>,
|
||||
cam_target: Query<(&Transform, &CameraTarget), Without<MainCamera>>,
|
||||
) {
|
||||
let cutscene = trigger.event().0.clone();
|
||||
|
||||
cam_state.cutscene = true;
|
||||
|
||||
// asumes `name` and `targetname` are equal
|
||||
let Some((t, _, target)) = cutscenes
|
||||
.iter()
|
||||
.find(|(_, cutscene_camera, _)| cutscene == cutscene_camera.name)
|
||||
else {
|
||||
return;
|
||||
};
|
||||
|
||||
let move_end = cutscene_movement
|
||||
.iter()
|
||||
.find(|(_, _, target)| cutscene == target.target.clone().unwrap_or_default())
|
||||
.map(|(t, _, _)| *t)
|
||||
.unwrap_or_else(|| *t);
|
||||
|
||||
let Some((target, _)) = cam_target.iter().find(|(_, camera_target)| {
|
||||
camera_target.targetname == target.target.clone().unwrap_or_default()
|
||||
}) else {
|
||||
return;
|
||||
};
|
||||
|
||||
*cutscene_state = CutsceneState::Playing {
|
||||
timer: Timer::from_seconds(2.0, TimerMode::Once),
|
||||
camera_start: t.looking_at(target.translation, Vec3::Y),
|
||||
camera_end: move_end.looking_at(target.translation, Vec3::Y),
|
||||
};
|
||||
}
|
||||
|
||||
fn update(
|
||||
mut cam_state: ResMut<CameraState>,
|
||||
mut cutscene_state: ResMut<CutsceneState>,
|
||||
mut cam: Query<&mut Transform, With<MainCamera>>,
|
||||
time: Res<Time>,
|
||||
) {
|
||||
if let CutsceneState::Playing {
|
||||
timer,
|
||||
camera_start,
|
||||
camera_end,
|
||||
} = &mut *cutscene_state
|
||||
{
|
||||
cam_state.cutscene = true;
|
||||
timer.tick(time.delta());
|
||||
|
||||
let t = Transform::from_translation(
|
||||
camera_start
|
||||
.translation
|
||||
.lerp(camera_end.translation, timer.fraction()),
|
||||
)
|
||||
.with_rotation(
|
||||
camera_start
|
||||
.rotation
|
||||
.lerp(camera_end.rotation, timer.fraction()),
|
||||
);
|
||||
|
||||
let _ = cam.single_mut().map(|mut cam| *cam = t);
|
||||
|
||||
if timer.is_finished() {
|
||||
cam_state.cutscene = false;
|
||||
*cutscene_state = CutsceneState::None;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,9 +6,7 @@ use crate::{
|
||||
protocol::{GltfSceneRoot, NetworkEnv, PlaySound},
|
||||
server_observer,
|
||||
tb_entities::SecretHead,
|
||||
utils::{
|
||||
billboards::Billboard, one_shot_force::OneShotImpulse, squish_animation::SquishAnimation,
|
||||
},
|
||||
utils::{Billboard, one_shot_force::OneShotImpulse, squish_animation::SquishAnimation},
|
||||
};
|
||||
use avian3d::prelude::*;
|
||||
use bevy::{ecs::relationship::RelatedSpawner, prelude::*};
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
use crate::{
|
||||
billboards::Billboard,
|
||||
global_observer,
|
||||
physics_layers::GameLayer,
|
||||
player::Player,
|
||||
protocol::{GltfSceneRoot, PlaySound},
|
||||
squish_animation::SquishAnimation,
|
||||
utils::one_shot_force::OneShotImpulse,
|
||||
utils::{Billboard, one_shot_force::OneShotImpulse},
|
||||
};
|
||||
use avian3d::prelude::*;
|
||||
use bevy::prelude::*;
|
||||
|
||||
@@ -52,7 +52,7 @@ use bevy_trenchbroom::{
|
||||
TrenchBroomPlugins, config::TrenchBroomConfig, prelude::TrenchBroomPhysicsPlugin,
|
||||
};
|
||||
use bevy_trenchbroom_avian::AvianPhysicsBackend;
|
||||
use utils::{billboards, squish_animation};
|
||||
use utils::squish_animation;
|
||||
|
||||
pub const HEDZ_GREEN: Srgba = Srgba::rgb(0.0, 1.0, 0.0);
|
||||
pub const HEDZ_PURPLE: Srgba = Srgba::rgb(91. / 256., 4. / 256., 138. / 256.);
|
||||
@@ -119,16 +119,13 @@ pub fn plugin(app: &mut App) {
|
||||
app.add_plugins(gates::plugin);
|
||||
app.add_plugins(platforms::plugin);
|
||||
app.add_plugins(movables::plugin);
|
||||
app.add_plugins(utils::billboards::plugin);
|
||||
app.add_plugins(aim::plugin);
|
||||
app.add_plugins(npc::plugin);
|
||||
app.add_plugins(keys::plugin);
|
||||
app.add_plugins(utils::squish_animation::plugin);
|
||||
app.add_plugins(camera::plugin);
|
||||
#[cfg(feature = "client")]
|
||||
app.add_plugins(client::plugin);
|
||||
app.add_plugins(control::plugin);
|
||||
app.add_plugins(cutscene::plugin);
|
||||
app.add_plugins(backpack::plugin);
|
||||
app.add_plugins(loading_assets::LoadingPlugin);
|
||||
app.add_plugins(loading_map::plugin);
|
||||
|
||||
@@ -12,7 +12,7 @@ use crate::{
|
||||
loading_assets::GameAssets,
|
||||
protocol::{PlaySound, is_server},
|
||||
tb_entities::EnemySpawn,
|
||||
utils::billboards::Billboard,
|
||||
utils::Billboard,
|
||||
};
|
||||
use bevy::{light::NotShadowCaster, prelude::*};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
@@ -23,8 +23,7 @@ use crate::{
|
||||
player::{Player, PlayerBodyMesh},
|
||||
tick::GameTick,
|
||||
utils::{
|
||||
auto_rotate::AutoRotation, billboards::Billboard, squish_animation::SquishAnimation,
|
||||
trail::SpawnTrail,
|
||||
Billboard, auto_rotate::AutoRotation, squish_animation::SquishAnimation, trail::SpawnTrail,
|
||||
},
|
||||
};
|
||||
use avian3d::prelude::{
|
||||
@@ -105,7 +104,7 @@ pub fn plugin(app: &mut App) {
|
||||
.replicate_once::<PlayerBodyMesh>()
|
||||
.replicate_once::<Npc>()
|
||||
.replicate::<SquishAnimation>()
|
||||
.replicate_once::<Transform>()
|
||||
.replicate::<Transform>()
|
||||
.replicate_once::<SpawnTrail>()
|
||||
.replicate_as::<Visibility, SerVisibility>();
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
pub mod auto_rotate;
|
||||
pub mod billboards;
|
||||
pub mod cooldown;
|
||||
pub mod debounce;
|
||||
pub mod explosions;
|
||||
@@ -14,7 +13,18 @@ use bevy::prelude::*;
|
||||
pub use cooldown::Cooldown;
|
||||
pub use debounce::Debounce;
|
||||
pub(crate) use observers::global_observer;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Component, Reflect, Default, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[reflect(Component)]
|
||||
pub enum Billboard {
|
||||
#[default]
|
||||
All,
|
||||
XZ,
|
||||
}
|
||||
|
||||
pub fn plugin(app: &mut App) {
|
||||
app.register_type::<Billboard>();
|
||||
|
||||
app.add_plugins(one_shot_force::plugin);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user