Added shooting, recoil system like csgo with patterns, and set gun aim time to rebound_time

This commit is contained in:
Franklin Blanco 2023-09-16 13:19:33 -07:00
parent 794d8bf4aa
commit 56fc163b5c
12 changed files with 225 additions and 53 deletions

View File

@ -50,4 +50,4 @@ pub fn capture_input(
};
move_player(player_movement_input, player_query, time);
}
}
}

View File

@ -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,
}

View File

@ -8,3 +8,6 @@ pub struct HoldableObjectData {
/// Initial Rotation in degrees
pub y_rot: f32,
}
#[derive(Component)]
pub struct InPlayerHands;

View File

@ -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 {

View File

@ -1,3 +1,5 @@
pub mod caliber;
pub mod firearm;
pub mod player_firing;
pub mod spawn_firearm;
pub mod spray_pattern;

View File

@ -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),
}
}
}

View File

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

View File

@ -0,0 +1,6 @@
pub struct FirearmSprayPattern {
pub vertical: Vec<f32>,
pub horizontal: Vec<f32>,
}

View File

@ -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
@ -66,14 +80,14 @@ pub fn follow_cursor_with_camera(
// if you want to use the cursor, but not let it leave the window,
// use `Confined` mode:
// window.cursor.grab_mode = CursorGrabMode::Confined;
// for a game that doesn't use the cursor (like a shooter):
// use `Locked` mode to keep the cursor in one place
window.cursor.grab_mode = CursorGrabMode::Locked;
// also hide the cursor
window.cursor.visible = false;
}
if keyboard_input.just_pressed(KeyCode::Escape) {
window.cursor.grab_mode = CursorGrabMode::None;
// also hide the cursor
@ -86,35 +100,43 @@ 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);
let desired_rotation_quat =
Quat::from_axis_angle(Vec3::Y, yaw) * Quat::from_axis_angle(Vec3::X, pitch);
for mut camera_transform in camera_query.iter_mut() {
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`!");
}

View File

@ -1,33 +1,91 @@
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
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) {
//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 {
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));
}
}
}
// For now just set the transforms
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));
} 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));
}
}
}

View File

@ -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) {

View File

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