Added shooting, recoil system like csgo with patterns, and set gun aim time to rebound_time
This commit is contained in:
parent
794d8bf4aa
commit
56fc163b5c
|
@ -1,6 +1,6 @@
|
|||
use bevy::prelude::{Component, Vec3};
|
||||
use bevy::prelude::{Component, Quat, Vec3};
|
||||
|
||||
use crate::logic::core::guns::caliber::Caliber;
|
||||
use crate::logic::core::guns::{caliber::Caliber, spray_pattern::FirearmSprayPattern};
|
||||
|
||||
#[derive(Component)]
|
||||
pub struct FirearmData<'a> {
|
||||
|
@ -10,8 +10,21 @@ pub struct FirearmData<'a> {
|
|||
/// Placeholder until mags get implemented
|
||||
pub max_capacity: u32,
|
||||
/// Rounds per minute
|
||||
pub fire_rate: u32,
|
||||
/// Amount of ms it takes for gun to come down from shooting
|
||||
pub rebound_time_millis: i64,
|
||||
pub fire_rate: f32,
|
||||
/// Amount of seconds it takes for gun to come down from shooting
|
||||
pub rebound_time_seconds: f32,
|
||||
pub asset_path: &'a str,
|
||||
|
||||
pub vertical_recoil_modifier: f32,
|
||||
pub horizontal_recoil_modifier: f32,
|
||||
pub recoil_pattern: FirearmSprayPattern,
|
||||
|
||||
/// Final rotation of hands when aimed in
|
||||
pub final_aimed_rotation: Quat,
|
||||
/// Final rotation of hands when not aimed in
|
||||
pub final_rotation: Quat,
|
||||
/// Final position of hands when aimed in
|
||||
pub final_aimed_position: Vec3,
|
||||
/// Final position of hands when not aimed in
|
||||
pub final_position: Vec3,
|
||||
}
|
||||
|
|
|
@ -8,3 +8,6 @@ pub struct HoldableObjectData {
|
|||
/// Initial Rotation in degrees
|
||||
pub y_rot: f32,
|
||||
}
|
||||
|
||||
#[derive(Component)]
|
||||
pub struct InPlayerHands;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use crate::comps::core::markers::{firearm::FirearmData, holdable::HoldableObjectData};
|
||||
use bevy::prelude::*;
|
||||
|
||||
use super::caliber::Caliber;
|
||||
use super::{caliber::Caliber, spray_pattern::FirearmSprayPattern};
|
||||
|
||||
pub enum Firearm {
|
||||
M4A1,
|
||||
|
@ -13,9 +13,33 @@ impl Firearm {
|
|||
firing_point: Vec3::ZERO,
|
||||
caliber: Caliber::NATO556,
|
||||
max_capacity: 30,
|
||||
fire_rate: 800,
|
||||
rebound_time_millis: 10,
|
||||
fire_rate: 800.0,
|
||||
rebound_time_seconds: 0.1,
|
||||
vertical_recoil_modifier: 2.0,
|
||||
horizontal_recoil_modifier: 0.5,
|
||||
recoil_pattern: FirearmSprayPattern {
|
||||
vertical: Vec::from([
|
||||
1.0, 1.2, 1.3, 1.6, 1.5, 1.7, 1.5, 1.5, 1.5, 2.0
|
||||
]),
|
||||
horizontal: Vec::from([
|
||||
1.0, 1.2, 1.3, -1.6, 1.5, -1.7, -1.5, 1.5, -1.5, 2.0
|
||||
]),
|
||||
},
|
||||
asset_path: "weapons/m4a1_rifle.glb#Scene0",
|
||||
|
||||
final_aimed_rotation: Quat::from_rotation_x(0.026),
|
||||
final_rotation: Quat::default(),
|
||||
final_aimed_position: Vec3 {
|
||||
x: -0.003,
|
||||
y: -0.35,
|
||||
z: -1.6,
|
||||
},
|
||||
final_position: Vec3 {
|
||||
x: 0.6,
|
||||
y: -0.45,
|
||||
z: -2.7,
|
||||
},
|
||||
|
||||
}
|
||||
}
|
||||
pub fn holdable_object_data(&self) -> HoldableObjectData {
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
pub mod caliber;
|
||||
pub mod firearm;
|
||||
pub mod player_firing;
|
||||
pub mod spawn_firearm;
|
||||
pub mod spray_pattern;
|
|
@ -0,0 +1,28 @@
|
|||
use std::time::Duration;
|
||||
|
||||
use bevy::prelude::*;
|
||||
|
||||
#[derive(Resource, Reflect)]
|
||||
#[reflect(Resource)]
|
||||
pub struct PlayerFiringInfo {
|
||||
/// Timestamp since last round fired from the gun. (seconds)
|
||||
pub last_shot_timestamp: f32,
|
||||
/// How many rounds were fired in the last sray
|
||||
pub rounds_in_last_spray: i32,
|
||||
/// What round the player is currently at.
|
||||
/// This is pretty much a multiplier for the recoil, since the higher the round index the higher the recoil.
|
||||
/// Decreases with time, each gun has its own rebound time
|
||||
pub current_round_index: usize,
|
||||
pub full_auto_timer: Timer,
|
||||
}
|
||||
|
||||
impl Default for PlayerFiringInfo {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
last_shot_timestamp: Default::default(),
|
||||
rounds_in_last_spray: Default::default(),
|
||||
current_round_index: Default::default(),
|
||||
full_auto_timer: Timer::new(Duration::from_secs_f32(0.0), TimerMode::Repeating),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,14 +1,20 @@
|
|||
use std::time::Duration;
|
||||
|
||||
use bevy::prelude::*;
|
||||
|
||||
use crate::{
|
||||
comps::core::markers::player::PlayerHand, constants::player_values::DEFAULT_PLAYER_FIREARM,
|
||||
comps::core::markers::{holdable::InPlayerHands, player::PlayerHand},
|
||||
constants::player_values::DEFAULT_PLAYER_FIREARM,
|
||||
utils,
|
||||
};
|
||||
|
||||
use super::player_firing::PlayerFiringInfo;
|
||||
|
||||
pub fn spawn_firearm_on_player_hands(
|
||||
mut commands: Commands,
|
||||
query: Query<Entity, With<PlayerHand>>,
|
||||
asset_server: Res<AssetServer>,
|
||||
mut player_firing_info: ResMut<PlayerFiringInfo>,
|
||||
) {
|
||||
for entity in query.iter() {
|
||||
let mut firearm_transform = Transform::from_xyz(0.0, 0.0, 0.0);
|
||||
|
@ -25,9 +31,16 @@ pub fn spawn_firearm_on_player_hands(
|
|||
},
|
||||
DEFAULT_PLAYER_FIREARM.firearm_data(),
|
||||
DEFAULT_PLAYER_FIREARM.holdable_object_data(),
|
||||
InPlayerHands,
|
||||
))
|
||||
.id();
|
||||
|
||||
commands.entity(entity).push_children(&[firearm]);
|
||||
let time_in_secs_between_each_round =
|
||||
1.0 / DEFAULT_PLAYER_FIREARM.firearm_data().fire_rate * 60.0;
|
||||
player_firing_info.full_auto_timer = Timer::new(
|
||||
Duration::from_secs_f32(time_in_secs_between_each_round),
|
||||
TimerMode::Once,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
|
||||
|
||||
pub struct FirearmSprayPattern {
|
||||
pub vertical: Vec<f32>,
|
||||
pub horizontal: Vec<f32>,
|
||||
}
|
|
@ -3,7 +3,8 @@ use bevy::{input::mouse::MouseMotion, prelude::*, window::CursorGrabMode};
|
|||
|
||||
use crate::{
|
||||
comps::core::markers::{camera::MainCamera, player::Player},
|
||||
constants::player_values::{PLAYER_CAMERA_HEIGHT, PLAYER_CROUCH_HEIGHT, PLAYER_CROUCH_TIME_S}, utils::rad_deg::radians_from_degrees,
|
||||
constants::player_values::{PLAYER_CAMERA_HEIGHT, PLAYER_CROUCH_HEIGHT, PLAYER_CROUCH_TIME_S},
|
||||
utils::rad_deg::radians_from_degrees,
|
||||
};
|
||||
|
||||
use super::player_movement::PlayerLinearXZState;
|
||||
|
@ -42,12 +43,25 @@ pub fn update_camera_vertical_position(
|
|||
if let PlayerLinearXZState::Crouched(since) = player_linear_xz_state {
|
||||
// Lerp/Smooth out this movement
|
||||
let delta = time.elapsed().as_secs_f32() - since;
|
||||
camera_transform.translation = camera_transform.translation.lerp(Vec3 { x: camera_transform.translation.x, y: PLAYER_CROUCH_HEIGHT, z: camera_transform.translation.z }, (delta / PLAYER_CROUCH_TIME_S).clamp(0.0, 1.0));
|
||||
camera_transform.translation = camera_transform.translation.lerp(
|
||||
Vec3 {
|
||||
x: camera_transform.translation.x,
|
||||
y: PLAYER_CROUCH_HEIGHT,
|
||||
z: camera_transform.translation.z,
|
||||
},
|
||||
(delta / PLAYER_CROUCH_TIME_S).clamp(0.0, 1.0),
|
||||
);
|
||||
} else {
|
||||
// TODO: Add elapsed time to standup so that crouch time and standup time is the same.
|
||||
camera_transform.translation = camera_transform.translation.lerp(Vec3 { x: camera_transform.translation.x, y: PLAYER_CAMERA_HEIGHT, z: camera_transform.translation.z }, time.delta_seconds().clamp(0.0, 1.0));
|
||||
camera_transform.translation = camera_transform.translation.lerp(
|
||||
Vec3 {
|
||||
x: camera_transform.translation.x,
|
||||
y: PLAYER_CAMERA_HEIGHT,
|
||||
z: camera_transform.translation.z,
|
||||
},
|
||||
time.delta_seconds().clamp(0.0, 1.0),
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// Handles looking around if cursor is locked
|
||||
|
@ -86,13 +100,15 @@ pub fn follow_cursor_with_camera(
|
|||
for motion in motions.iter() {
|
||||
let window_scale = window.height().min(window.width());
|
||||
if btn.pressed(MouseButton::Right) {
|
||||
pitch -= (settings.aimed_sensitivity * motion.delta.y * window_scale).to_radians();
|
||||
yaw -= (settings.aimed_sensitivity * motion.delta.x * window_scale).to_radians();
|
||||
pitch -= (settings.aimed_sensitivity * motion.delta.y * window_scale)
|
||||
.to_radians();
|
||||
yaw -= (settings.aimed_sensitivity * motion.delta.x * window_scale)
|
||||
.to_radians();
|
||||
} else {
|
||||
pitch -= (settings.sensitivity * motion.delta.y * window_scale).to_radians();
|
||||
pitch -=
|
||||
(settings.sensitivity * motion.delta.y * window_scale).to_radians();
|
||||
yaw -= (settings.sensitivity * motion.delta.x * window_scale).to_radians();
|
||||
}
|
||||
|
||||
}
|
||||
pitch = pitch.clamp(-1.54, 1.54);
|
||||
|
||||
|
@ -103,18 +119,24 @@ pub fn follow_cursor_with_camera(
|
|||
player_transform.rotation = desired_rotation_quat;
|
||||
if keyboard_input.pressed(KeyCode::Q) {
|
||||
let final_quat = Quat::from_axis_angle(Vec3::Z, radians_from_degrees(30.0));
|
||||
camera_transform.rotation = camera_transform.rotation.lerp(final_quat, time.delta_seconds() / 0.2);
|
||||
camera_transform.rotation = camera_transform
|
||||
.rotation
|
||||
.lerp(final_quat, time.delta_seconds() / 0.2);
|
||||
} else if keyboard_input.pressed(KeyCode::E) {
|
||||
let final_quat = Quat::from_axis_angle(Vec3::Z, radians_from_degrees(-30.0));
|
||||
camera_transform.rotation = camera_transform.rotation.lerp(final_quat, time.delta_seconds() / 0.2);
|
||||
let final_quat =
|
||||
Quat::from_axis_angle(Vec3::Z, radians_from_degrees(-30.0));
|
||||
camera_transform.rotation = camera_transform
|
||||
.rotation
|
||||
.lerp(final_quat, time.delta_seconds() / 0.2);
|
||||
} else {
|
||||
camera_transform.rotation = camera_transform.rotation.lerp(Quat::default(), time.delta_seconds() / 0.2);
|
||||
camera_transform.rotation = camera_transform
|
||||
.rotation
|
||||
.lerp(Quat::default(), time.delta_seconds() / 0.2);
|
||||
}
|
||||
// headbob_camera(&mut camera_transform, time.delta_seconds_f64());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
warn!("Primary window not found for `player_look`!");
|
||||
}
|
||||
|
|
|
@ -1,32 +1,90 @@
|
|||
use bevy::prelude::*;
|
||||
|
||||
use crate::comps::core::markers::player::PlayerHand;
|
||||
use crate::{
|
||||
comps::core::markers::{firearm::FirearmData, holdable::InPlayerHands, player::PlayerHand},
|
||||
logic::core::guns::player_firing::PlayerFiringInfo, utils::rad_deg::radians_from_degrees,
|
||||
};
|
||||
|
||||
pub fn capture_hand_usage(
|
||||
mouse_buttons: Res<Input<MouseButton>>,
|
||||
mut query: Query<&mut Transform, With<PlayerHand>>,
|
||||
mut hand_query: Query<&mut Transform, With<PlayerHand>>,
|
||||
time: Res<Time>,
|
||||
|
||||
mut player_firing_info: ResMut<PlayerFiringInfo>,
|
||||
mut firearm_query: Query<
|
||||
(&mut Transform, &'static FirearmData),
|
||||
(With<InPlayerHands>, Without<PlayerHand>),
|
||||
>,
|
||||
) {
|
||||
for mut transform in query.iter_mut() {
|
||||
if mouse_buttons.pressed(MouseButton::Left) {
|
||||
if mouse_buttons.just_pressed(MouseButton::Left) {
|
||||
// First shot
|
||||
} else {
|
||||
// All other shots
|
||||
}
|
||||
}
|
||||
// For now just set the transforms
|
||||
player_firing_info.full_auto_timer.tick(time.delta());
|
||||
|
||||
for (mut _firearm_transform, firearm_data) in firearm_query.iter_mut() {
|
||||
for mut hand_transform in hand_query.iter_mut() {
|
||||
// AIMING IN/OUT
|
||||
if mouse_buttons.pressed(MouseButton::Right) {
|
||||
let aimed_in = Vec3 { x: -0.003, y: -0.35, z: -1.6};
|
||||
let mut aimed_in_rot = Quat::default();
|
||||
aimed_in_rot.x = 0.013;
|
||||
transform.rotation = transform.rotation.lerp(aimed_in_rot, time.delta_seconds() / 0.1);
|
||||
transform.translation = transform.translation.lerp(aimed_in, (time.delta_seconds() / 0.1).clamp(0.0, 1.0));
|
||||
|
||||
|
||||
//firearm_transform.rotation = firearm_transform.rotation.lerp(Quat::default(), (time.delta_seconds() / //firearm_data.rebound_time_seconds).clamp(0.0, 1.0));
|
||||
|
||||
|
||||
|
||||
let rotation_lerp_quat = hand_transform.rotation.lerp(
|
||||
firearm_data.final_aimed_rotation,
|
||||
(time.delta_seconds() / firearm_data.rebound_time_seconds).clamp(0.0, 1.0),
|
||||
);
|
||||
let position_lerp_vec3 = hand_transform.translation.lerp(
|
||||
firearm_data.final_aimed_position,
|
||||
(time.delta_seconds() / firearm_data.rebound_time_seconds).clamp(0.0, 1.0),
|
||||
);
|
||||
hand_transform.rotation = rotation_lerp_quat;
|
||||
hand_transform.translation = position_lerp_vec3;
|
||||
|
||||
|
||||
|
||||
|
||||
} else {
|
||||
let aimed_in = Vec3 { x: 0.6, y: -0.45, z: -2.7};
|
||||
let aimed_in_rot = Quat::default();
|
||||
transform.rotation = transform.rotation.lerp(aimed_in_rot, time.delta_seconds() / 0.1);
|
||||
transform.translation = transform.translation.lerp(aimed_in, (time.delta_seconds() / 0.1).clamp(0.0, 1.0));
|
||||
hand_transform.rotation = hand_transform
|
||||
.rotation
|
||||
.lerp(firearm_data.final_rotation, (time.delta_seconds() / 0.5).clamp(0.0, 1.0));
|
||||
hand_transform.translation = hand_transform.translation.lerp(
|
||||
firearm_data.final_position,
|
||||
(time.delta_seconds() / 0.5).clamp(0.0, 1.0),
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// Shooting & Recoil
|
||||
if mouse_buttons.pressed(MouseButton::Left) {
|
||||
if player_firing_info.full_auto_timer.finished() {
|
||||
|
||||
let vertical_recoil_number: f32 = match firearm_data.recoil_pattern.vertical.get(player_firing_info.current_round_index) {
|
||||
Some(vert_recoil_number) => *vert_recoil_number,
|
||||
None => {
|
||||
*firearm_data.recoil_pattern.vertical.last().expect("FOUND A FIREARM_DATA WITHOUT ANY FIREARM_RECOIL_PATTERN.")
|
||||
},
|
||||
};
|
||||
let horizontal_recoil_number: f32 = match firearm_data.recoil_pattern.horizontal.get(player_firing_info.current_round_index) {
|
||||
Some(horizontal_recoil_number) => *horizontal_recoil_number,
|
||||
None => {
|
||||
*firearm_data.recoil_pattern.horizontal.last().expect("FOUND A FIREARM_DATA WITHOUT ANY FIREARM_RECOIL_PATTERN.")
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
|
||||
player_firing_info.current_round_index += 1;
|
||||
player_firing_info.last_shot_timestamp = time.elapsed_seconds();
|
||||
player_firing_info.full_auto_timer.reset();
|
||||
|
||||
|
||||
hand_transform
|
||||
.rotate_x(radians_from_degrees(firearm_data.vertical_recoil_modifier * vertical_recoil_number));
|
||||
hand_transform.rotate_y(radians_from_degrees(firearm_data.horizontal_recoil_modifier * horizontal_recoil_number));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use bevy::prelude::*;
|
||||
use bevy_inspector_egui::quick::WorldInspectorPlugin;
|
||||
use bevy_rapier3d::prelude::*;
|
||||
use logic::core::guns::player_firing::PlayerFiringInfo;
|
||||
use scenes::scene1;
|
||||
|
||||
mod comps;
|
||||
|
@ -24,8 +25,8 @@ fn setup_plugins(application: &mut App) {
|
|||
application
|
||||
.add_plugins(DefaultPlugins)
|
||||
.add_plugins(RapierPhysicsPlugin::<NoUserData>::default())
|
||||
.add_plugins(WorldInspectorPlugin::new()); // Rapier Physics
|
||||
//.add_plugins(RapierDebugRenderPlugin::default()); // Uncomment this to see physics objects as wireframes
|
||||
.add_plugins(WorldInspectorPlugin::new())
|
||||
.register_type::<PlayerFiringInfo>();
|
||||
}
|
||||
|
||||
fn load(application: &mut App) {
|
||||
|
|
|
@ -3,13 +3,14 @@ use bevy::prelude::*;
|
|||
use crate::{
|
||||
comps::core::controller::capture_input,
|
||||
logic::core::{
|
||||
guns::spawn_firearm::spawn_firearm_on_player_hands,
|
||||
guns::{player_firing::PlayerFiringInfo, spawn_firearm::spawn_firearm_on_player_hands},
|
||||
player::{
|
||||
camera_player_sync::{
|
||||
follow_cursor_with_camera, update_camera_vertical_position, MouseMovementSettings,
|
||||
},
|
||||
hands::capture_hand_usage,
|
||||
player_vertical_sync::sync_player_y_state,
|
||||
spawn_player::spawn_player, hands::capture_hand_usage,
|
||||
spawn_player::spawn_player,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
@ -21,6 +22,7 @@ use super::{
|
|||
|
||||
pub fn load_scene(application: &mut App) {
|
||||
application.insert_resource(MouseMovementSettings::default());
|
||||
application.insert_resource(PlayerFiringInfo::default());
|
||||
// Startup
|
||||
application.add_systems(Startup, spawn_ground);
|
||||
application.add_systems(Startup, spawn_obstacles);
|
||||
|
|
Loading…
Reference in New Issue