more clear splitting of visual stuff in client mod
This commit is contained in:
@@ -1,8 +1,11 @@
|
|||||||
use super::TriggerArrow;
|
use super::TriggerArrow;
|
||||||
use crate::{
|
use crate::{
|
||||||
GameState, billboards::Billboard, global_observer, heads_database::HeadsDatabase,
|
GameState, global_observer,
|
||||||
hitpoints::Hit, loading_assets::GameAssets, physics_layers::GameLayer,
|
heads_database::HeadsDatabase,
|
||||||
utils::sprite_3d_animation::AnimationTimer,
|
hitpoints::Hit,
|
||||||
|
loading_assets::GameAssets,
|
||||||
|
physics_layers::GameLayer,
|
||||||
|
utils::{Billboard, sprite_3d_animation::AnimationTimer},
|
||||||
};
|
};
|
||||||
use avian3d::prelude::*;
|
use avian3d::prelude::*;
|
||||||
use bevy::{light::NotShadowCaster, prelude::*};
|
use bevy::{light::NotShadowCaster, prelude::*};
|
||||||
|
|||||||
@@ -1,8 +1,14 @@
|
|||||||
use super::TriggerGun;
|
use super::TriggerGun;
|
||||||
use crate::{
|
use crate::{
|
||||||
GameState, abilities::ProjectileId, billboards::Billboard, global_observer,
|
GameState,
|
||||||
heads_database::HeadsDatabase, hitpoints::Hit, loading_assets::GameAssets,
|
abilities::ProjectileId,
|
||||||
physics_layers::GameLayer, tb_entities::EnemySpawn, utils::sprite_3d_animation::AnimationTimer,
|
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 avian3d::prelude::*;
|
||||||
use bevy::{light::NotShadowCaster, prelude::*};
|
use bevy::{light::NotShadowCaster, prelude::*};
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ use crate::{
|
|||||||
physics_layers::GameLayer,
|
physics_layers::GameLayer,
|
||||||
player::Player,
|
player::Player,
|
||||||
protocol::PlaySound,
|
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::{light::NotShadowCaster, prelude::*};
|
||||||
use bevy_replicon::prelude::{SendMode, ServerTriggerExt, Signature, ToClients};
|
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 bevy::prelude::*;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
@@ -22,183 +6,3 @@ pub struct CameraTarget;
|
|||||||
|
|
||||||
#[derive(Component, Reflect, Debug, Serialize, Deserialize, PartialEq)]
|
#[derive(Component, Reflect, Debug, Serialize, Deserialize, PartialEq)]
|
||||||
pub struct CameraArmRotation;
|
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::{
|
use crate::{
|
||||||
GameState, aim::MarkerEvent, global_observer, loading_assets::UIAssets,
|
GameState, aim::MarkerEvent, global_observer, loading_assets::UIAssets, utils::Billboard,
|
||||||
utils::billboards::Billboard,
|
|
||||||
};
|
};
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
use bevy_sprite3d::Sprite3d;
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let (local_player_childer, selected_controller) = *local_player;
|
let (local_player_children, selected_controller) = *local_player;
|
||||||
|
|
||||||
if !matches!(selected_controller, SelectedController::Flying) {
|
if !matches!(selected_controller, SelectedController::Flying) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
local_player_childer.iter().find(|&child| {
|
local_player_children.iter().find(|&child| {
|
||||||
if let Ok(mut rig_transform) = player_mesh.get_mut(child) {
|
if let Ok(mut rig_transform) = player_mesh.get_mut(child) {
|
||||||
let look_dir = look_dir.0;
|
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,
|
GameState,
|
||||||
abilities::Healing,
|
abilities::Healing,
|
||||||
loading_assets::{AudioAssets, GameAssets},
|
loading_assets::{AudioAssets, GameAssets},
|
||||||
utils::{billboards::Billboard, observers::global_observer},
|
utils::{Billboard, observers::global_observer},
|
||||||
};
|
};
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
use rand::{Rng, thread_rng};
|
use rand::{Rng, thread_rng};
|
||||||
|
|||||||
@@ -25,7 +25,9 @@ use bevy_trenchbroom::geometry::Brushes;
|
|||||||
pub mod aim;
|
pub mod aim;
|
||||||
pub mod audio;
|
pub mod audio;
|
||||||
mod backpack;
|
mod backpack;
|
||||||
|
pub mod camera;
|
||||||
pub mod control;
|
pub mod control;
|
||||||
|
pub mod cutscene;
|
||||||
pub mod debug;
|
pub mod debug;
|
||||||
pub mod enemy;
|
pub mod enemy;
|
||||||
pub mod heal_effect;
|
pub mod heal_effect;
|
||||||
@@ -34,6 +36,7 @@ mod settings;
|
|||||||
pub mod setup;
|
pub mod setup;
|
||||||
pub mod steam;
|
pub mod steam;
|
||||||
pub mod ui;
|
pub mod ui;
|
||||||
|
mod utils;
|
||||||
|
|
||||||
pub fn plugin(app: &mut App) {
|
pub fn plugin(app: &mut App) {
|
||||||
app.add_plugins((
|
app.add_plugins((
|
||||||
@@ -49,6 +52,9 @@ pub fn plugin(app: &mut App) {
|
|||||||
ui::plugin,
|
ui::plugin,
|
||||||
settings::plugin,
|
settings::plugin,
|
||||||
backpack::plugin,
|
backpack::plugin,
|
||||||
|
camera::plugin,
|
||||||
|
utils::billboards::plugin,
|
||||||
|
cutscene::plugin,
|
||||||
));
|
));
|
||||||
|
|
||||||
app.add_systems(
|
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::{core_pipeline::tonemapping::Tonemapping, prelude::*, render::view::ColorGrading};
|
||||||
use bevy_trenchbroom::TrenchBroomServer;
|
use bevy_trenchbroom::TrenchBroomServer;
|
||||||
|
|
||||||
|
|||||||
@@ -1,22 +1,12 @@
|
|||||||
use crate::camera::MainCamera;
|
use crate::{client::camera::MainCamera, utils::Billboard};
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
use bevy_sprite3d::Sprite3dPlugin;
|
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) {
|
pub fn plugin(app: &mut App) {
|
||||||
if !app.is_plugin_added::<Sprite3dPlugin>() {
|
if !app.is_plugin_added::<Sprite3dPlugin>() {
|
||||||
app.add_plugins(Sprite3dPlugin);
|
app.add_plugins(Sprite3dPlugin);
|
||||||
}
|
}
|
||||||
|
|
||||||
app.register_type::<Billboard>();
|
|
||||||
app.add_systems(Update, (face_camera, face_camera_no_parent));
|
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::prelude::*;
|
||||||
use bevy_trenchbroom::prelude::*;
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(Clone, Debug, Event, Serialize, Deserialize)]
|
#[derive(Clone, Debug, Event, Serialize, Deserialize)]
|
||||||
pub struct StartCutscene(pub String);
|
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},
|
protocol::{GltfSceneRoot, NetworkEnv, PlaySound},
|
||||||
server_observer,
|
server_observer,
|
||||||
tb_entities::SecretHead,
|
tb_entities::SecretHead,
|
||||||
utils::{
|
utils::{Billboard, one_shot_force::OneShotImpulse, squish_animation::SquishAnimation},
|
||||||
billboards::Billboard, one_shot_force::OneShotImpulse, squish_animation::SquishAnimation,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
use avian3d::prelude::*;
|
use avian3d::prelude::*;
|
||||||
use bevy::{ecs::relationship::RelatedSpawner, prelude::*};
|
use bevy::{ecs::relationship::RelatedSpawner, prelude::*};
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
billboards::Billboard,
|
|
||||||
global_observer,
|
global_observer,
|
||||||
physics_layers::GameLayer,
|
physics_layers::GameLayer,
|
||||||
player::Player,
|
player::Player,
|
||||||
protocol::{GltfSceneRoot, PlaySound},
|
protocol::{GltfSceneRoot, PlaySound},
|
||||||
squish_animation::SquishAnimation,
|
squish_animation::SquishAnimation,
|
||||||
utils::one_shot_force::OneShotImpulse,
|
utils::{Billboard, one_shot_force::OneShotImpulse},
|
||||||
};
|
};
|
||||||
use avian3d::prelude::*;
|
use avian3d::prelude::*;
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ use bevy_trenchbroom::{
|
|||||||
TrenchBroomPlugins, config::TrenchBroomConfig, prelude::TrenchBroomPhysicsPlugin,
|
TrenchBroomPlugins, config::TrenchBroomConfig, prelude::TrenchBroomPhysicsPlugin,
|
||||||
};
|
};
|
||||||
use bevy_trenchbroom_avian::AvianPhysicsBackend;
|
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_GREEN: Srgba = Srgba::rgb(0.0, 1.0, 0.0);
|
||||||
pub const HEDZ_PURPLE: Srgba = Srgba::rgb(91. / 256., 4. / 256., 138. / 256.);
|
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(gates::plugin);
|
||||||
app.add_plugins(platforms::plugin);
|
app.add_plugins(platforms::plugin);
|
||||||
app.add_plugins(movables::plugin);
|
app.add_plugins(movables::plugin);
|
||||||
app.add_plugins(utils::billboards::plugin);
|
|
||||||
app.add_plugins(aim::plugin);
|
app.add_plugins(aim::plugin);
|
||||||
app.add_plugins(npc::plugin);
|
app.add_plugins(npc::plugin);
|
||||||
app.add_plugins(keys::plugin);
|
app.add_plugins(keys::plugin);
|
||||||
app.add_plugins(utils::squish_animation::plugin);
|
app.add_plugins(utils::squish_animation::plugin);
|
||||||
app.add_plugins(camera::plugin);
|
|
||||||
#[cfg(feature = "client")]
|
#[cfg(feature = "client")]
|
||||||
app.add_plugins(client::plugin);
|
app.add_plugins(client::plugin);
|
||||||
app.add_plugins(control::plugin);
|
app.add_plugins(control::plugin);
|
||||||
app.add_plugins(cutscene::plugin);
|
|
||||||
app.add_plugins(backpack::plugin);
|
app.add_plugins(backpack::plugin);
|
||||||
app.add_plugins(loading_assets::LoadingPlugin);
|
app.add_plugins(loading_assets::LoadingPlugin);
|
||||||
app.add_plugins(loading_map::plugin);
|
app.add_plugins(loading_map::plugin);
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ use crate::{
|
|||||||
loading_assets::GameAssets,
|
loading_assets::GameAssets,
|
||||||
protocol::{PlaySound, is_server},
|
protocol::{PlaySound, is_server},
|
||||||
tb_entities::EnemySpawn,
|
tb_entities::EnemySpawn,
|
||||||
utils::billboards::Billboard,
|
utils::Billboard,
|
||||||
};
|
};
|
||||||
use bevy::{light::NotShadowCaster, prelude::*};
|
use bevy::{light::NotShadowCaster, prelude::*};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|||||||
@@ -23,8 +23,7 @@ use crate::{
|
|||||||
player::{Player, PlayerBodyMesh},
|
player::{Player, PlayerBodyMesh},
|
||||||
tick::GameTick,
|
tick::GameTick,
|
||||||
utils::{
|
utils::{
|
||||||
auto_rotate::AutoRotation, billboards::Billboard, squish_animation::SquishAnimation,
|
Billboard, auto_rotate::AutoRotation, squish_animation::SquishAnimation, trail::SpawnTrail,
|
||||||
trail::SpawnTrail,
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use avian3d::prelude::{
|
use avian3d::prelude::{
|
||||||
@@ -105,7 +104,7 @@ pub fn plugin(app: &mut App) {
|
|||||||
.replicate_once::<PlayerBodyMesh>()
|
.replicate_once::<PlayerBodyMesh>()
|
||||||
.replicate_once::<Npc>()
|
.replicate_once::<Npc>()
|
||||||
.replicate::<SquishAnimation>()
|
.replicate::<SquishAnimation>()
|
||||||
.replicate_once::<Transform>()
|
.replicate::<Transform>()
|
||||||
.replicate_once::<SpawnTrail>()
|
.replicate_once::<SpawnTrail>()
|
||||||
.replicate_as::<Visibility, SerVisibility>();
|
.replicate_as::<Visibility, SerVisibility>();
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
pub mod auto_rotate;
|
pub mod auto_rotate;
|
||||||
pub mod billboards;
|
|
||||||
pub mod cooldown;
|
pub mod cooldown;
|
||||||
pub mod debounce;
|
pub mod debounce;
|
||||||
pub mod explosions;
|
pub mod explosions;
|
||||||
@@ -14,7 +13,18 @@ use bevy::prelude::*;
|
|||||||
pub use cooldown::Cooldown;
|
pub use cooldown::Cooldown;
|
||||||
pub use debounce::Debounce;
|
pub use debounce::Debounce;
|
||||||
pub(crate) use observers::global_observer;
|
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) {
|
pub fn plugin(app: &mut App) {
|
||||||
|
app.register_type::<Billboard>();
|
||||||
|
|
||||||
app.add_plugins(one_shot_force::plugin);
|
app.add_plugins(one_shot_force::plugin);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user