EventCollision system that takes into account more than one type of collider AND Auto Low ready when gun collider hits object OR when player starts sprinting

This commit is contained in:
Franklin 2023-11-25 14:39:02 -04:00
parent 1752e21637
commit f78d4c7f40
14 changed files with 202 additions and 65 deletions

View File

@ -31,14 +31,18 @@ Multiplayer
- [x] High Ready & Low Ready system with state
- [x] High ready animation (procedural)
- [x] Low ready animation (procedural)
- [ ] TODO: Auto Low ready when gun collider hits object OR when player starts sprinting
- [x] EventCollision system that takes into account more than one type of collider.
- [x] Auto Low ready when gun collider hits object OR when player starts sprinting
- [ ] (Not a priority) Collision with wall and attempting to Aim can look very twitchy, maybe add a cooldown of 0.1-0.4s on each change from low ready to high ready
- [ ] Optics
- [ ] All optics implementing a fn/trait that gives a specific gun offset to use the optic correctly
- [ ] Find some way to implement a shader for the optics
- [ ] Bobbing
- [ ] Gun Bob on run
- [ ] Gun Bob on walk
- [ ] Reload animation (procedural)
- [ ] Real world magazines
- [ ] Rewriting bullet physics to use raycasts & kinematic rigidbodies (logic controlled)
- [ ] Create a Controls struct that holds mappings to all the game keys and replace them in all the game's code

View File

@ -0,0 +1,11 @@
use bevy::{ecs::{event::Event, entity::Entity}, transform::components::Transform};
use super::collision_event_type::CollisionEventType;
#[derive(Event)]
pub struct BulletCollisionEvent {
pub bullet: Entity,
pub other: Entity,
pub at: Transform,
pub collision_type: CollisionEventType,
}

View File

@ -0,0 +1,6 @@
#[derive(PartialEq, Copy, Clone)]
pub enum CollisionEventType {
Start,
End,
}

View File

@ -0,0 +1,10 @@
use bevy::ecs::{event::Event, entity::Entity};
use super::collision_event_type::CollisionEventType;
#[derive(Event)]
pub struct EquippedGunCollisionEvent {
pub gun: Entity,
pub other: Entity,
pub collision_type: CollisionEventType,
}

View File

@ -1,3 +1,6 @@
//pub mod loot_container;
pub mod inventory_changed;
pub mod pickup_item;
pub mod bullet_collision;
pub mod equipped_gun_collision;
pub mod collision_event_type;

View File

@ -1,5 +1,5 @@
use bevy::prelude::*;
use bevy_rapier3d::geometry::Sensor;
use bevy_rapier3d::geometry::{Sensor, ActiveEvents};
use crate::{comps::core::markers::holdable::InPlayerHands, utils::hierarchy::find_child_in_parent_children};
@ -9,14 +9,6 @@ use crate::{comps::core::markers::holdable::InPlayerHands, utils::hierarchy::fin
#[reflect(Component)]
pub struct GunFirearmCollider;
/*for (gun_firearm_collider_entity, _) in gun_firearm_collider_query.iter() {
println!("a");
if find_child_in_parent_children(commands, firearm_asset_entity, gun_firearm_collider_entity, &children) {
println!("Sensor");
commands.entity(gun_firearm_collider_entity).insert(Sensor);
}
} */
pub fn update_gun_collider(
mut commands: Commands,
gun_firearm_collider_query: Query<Entity, Added<GunFirearmCollider>>,
@ -27,11 +19,11 @@ pub fn update_gun_collider(
let mut found = false;
for in_player_hands_entity in in_player_hands_query.iter() {
if find_child_in_parent_children(&mut commands, in_player_hands_entity, gun_firearm_collider_entity, &children) {
commands.entity(gun_firearm_collider_entity).insert(Sensor);
commands.entity(gun_firearm_collider_entity).insert(Sensor).insert(ActiveEvents::COLLISION_EVENTS);
found = true;
}
}
if found { continue; }
commands.entity(gun_firearm_collider_entity).remove::<Sensor>();
commands.entity(gun_firearm_collider_entity).remove::<Sensor>().remove::<ActiveEvents>();
}
}

View File

@ -0,0 +1,87 @@
//!
//! The goal of this module is to provide collision hydration to all other systems in the game.
//! The reason is, well, rapier does not give a way to read collisions in multiple systems.
//! If you read a collision event you automatically starve the systems relying on it.
//!
//! Have Different collision events for different systems, don't call the system directly, just use events to pass in collisions.
//!
use bevy::{prelude::*, ecs::system::SystemParam};
use bevy_rapier3d::{pipeline::CollisionEvent, rapier::geometry::CollisionEventFlags, geometry::Sensor};
use crate::comps::core::{markers::{bullet::BulletMarker, proxy::weapons::gun_colliders::GunFirearmCollider}, events::{bullet_collision::BulletCollisionEvent, equipped_gun_collision::EquippedGunCollisionEvent, collision_event_type::CollisionEventType}};
#[derive(SystemParam)]
pub struct CollisionHandlerQueryParams<'w, 's> {
bullets: Query<'w, 's, (Entity, &'static Transform), With<BulletMarker>>,
firearms: Query<'w, 's, Entity, (With<Sensor>, With<GunFirearmCollider>, Without<BulletMarker>)>,
}
#[derive(SystemParam)]
pub struct CollisionHandlerEventParams<'w> {
bullet_collision_events: EventWriter<'w, BulletCollisionEvent>,
equipped_gun_collision_events: EventWriter<'w, EquippedGunCollisionEvent>,
}
pub fn collision_handler(
queries: CollisionHandlerQueryParams,
mut events: CollisionHandlerEventParams,
mut collisions: EventReader<CollisionEvent>,
//time: Res<Time>,
) {
for collision in collisions.read() {
let (entity_a, entity_b, flags, collision_type) = match collision {
CollisionEvent::Started(entity_a, entity_b, flags) => {
(entity_a, entity_b, flags, CollisionEventType::Start)
}
CollisionEvent::Stopped(entity_a, entity_b, flags) => {
(entity_a, entity_b, flags, CollisionEventType::End)
}
};
if entity_a == entity_b { continue; } // Avoid inner collisions
if flags.contains(CollisionEventFlags::SENSOR) { // Sensor collision event handling
println!("Sensor collision");
// ######
// Equipped Firearm Collisions
// ######
for gun_collider in queries.firearms.iter() {
println!("Gun collider entity detected");
if &gun_collider == entity_a {
events.equipped_gun_collision_events.send(EquippedGunCollisionEvent {
gun: gun_collider,
other: *entity_b,
collision_type,
});
} else if &gun_collider == entity_b {
events.equipped_gun_collision_events.send(EquippedGunCollisionEvent {
gun: gun_collider,
other: *entity_a,
collision_type,
});
}
}
continue; // Continue, as we already handled sensor collisions
}
// ######
// Bullet collisions
// ######
for (bullet, bullet_transform) in queries.bullets.iter() {
if entity_a == &bullet {
events.bullet_collision_events.send(BulletCollisionEvent {
bullet,
other: *entity_b,
at: *bullet_transform,
collision_type,
});
} else if entity_b == &bullet {
events.bullet_collision_events.send(BulletCollisionEvent {
bullet,
other: *entity_a,
at: *bullet_transform,
collision_type,
});
}
}
}
}

View File

@ -1,6 +1,6 @@
use crate::comps::core::markers::{bullet::BulletMarker, muzzle_flash::MuzzleFlashMarker};
use crate::comps::core::{markers::{bullet::BulletMarker, muzzle_flash::MuzzleFlashMarker}, events::{bullet_collision::BulletCollisionEvent, collision_event_type::CollisionEventType}};
use bevy::prelude::*;
use bevy_rapier3d::{prelude::*, rapier::geometry::CollisionEventFlags};
use bevy_rapier3d::prelude::*;
pub fn despawn_muzzle_flashes(
mut commands: Commands,
@ -21,59 +21,45 @@ pub fn despawn_stray_bullets(
mut materials: ResMut<Assets<StandardMaterial>>,
mut query: Query<(&mut BulletMarker, Entity, &Transform)>,
mut collisions: EventReader<CollisionEvent>,
//res: Res<IntegrationParameters>,
mut bullet_collisions: EventReader<BulletCollisionEvent>,
time: Res<Time>,
) {
let collisions_read: Vec<&CollisionEvent> = collisions.read().collect();
for (mut bullet, bullet_entity, transform) in query.iter_mut() {
bullet.timer.tick(time.delta());
for event in collisions_read.iter() {
match event {
CollisionEvent::Started(entity_a, entity_b, flags) => {
if flags.contains(CollisionEventFlags::SENSOR) {
continue;
}
if entity_a == entity_b {
// Avoid inner collisions
continue;
}
if entity_a == &bullet_entity || entity_b == &bullet_entity {
commands.entity(bullet_entity).remove::<Collider>();
//commands.entity(bullet_entity).insert(Sensor);
spawn_bullet_hit_marker(
&mut commands,
transform.translation,
&mut meshes,
&mut materials,
);
}
}
CollisionEvent::Stopped(entity_a, entity_b, flags) => {
if flags.contains(CollisionEventFlags::SENSOR) {
continue;
}
if entity_a == entity_b {
// Avoid inner collisions
continue;
}
if entity_a == &bullet_entity || entity_b == &bullet_entity {
let collisions_read: Vec<&BulletCollisionEvent> = bullet_collisions.read().collect();
for event in collisions_read.into_iter() {
match event.collision_type {
CollisionEventType::Start => {
commands.entity(event.bullet).remove::<Collider>();
//commands.entity(bullet_entity).insert(Sensor);
spawn_bullet_hit_marker(
&mut commands,
event.at.translation,
&mut meshes,
&mut materials,
);
},
CollisionEventType::End => {
for (bullet, bullet_entity, _) in query.iter() {
if bullet_entity != event.bullet { continue; }
commands
.entity(bullet_entity)
.entity(event.bullet)
.insert(Collider::ball(bullet.caliber.size()));
//commands.entity(bullet_entity).remove::<Sensor>();
//commands.entity(*entity_b).despawn();
spawn_bullet_exit_marker(
&mut commands,
transform.translation,
&mut meshes,
&mut materials,
);
continue;
}
//commands.entity(bullet_entity).remove::<Sensor>();
//commands.entity(*entity_b).despawn();
spawn_bullet_exit_marker(
&mut commands,
event.at.translation,
&mut meshes,
&mut materials,
);
continue;
} //_ => {}
}
}
for (mut bullet, bullet_entity, transform) in query.iter_mut() {
bullet.timer.tick(time.delta());
if bullet.timer.finished() {
commands.entity(bullet_entity).despawn();
}

View File

@ -0,0 +1,27 @@
use bevy::prelude::*;
use crate::comps::core::events::{equipped_gun_collision::EquippedGunCollisionEvent, collision_event_type::CollisionEventType};
use super::player_firing::{PlayerFiringInfo, GunReadyPose};
pub fn update_gun_position_on_collision(
mut query: Query<&mut PlayerFiringInfo>,
mut events: EventReader<EquippedGunCollisionEvent>,
) {
for event in events.read() {
println!("Collision");
for mut player_firing_info in query.iter_mut() {
match event.collision_type {
CollisionEventType::Start => {
player_firing_info.gun_ready_pose = GunReadyPose::LowReady;
player_firing_info.gun_colliding_with_object = true;
},
CollisionEventType::End => {
player_firing_info.gun_colliding_with_object = false;
//player_firing_info.gun_ready_pose = GunReadyPose::HighReady;
},
}
}
}
}

View File

@ -1,4 +1,5 @@
pub mod despawn_shots;
pub mod player_firing;
pub mod shoot;
pub mod inspect;
pub mod inspect;
pub mod equipped_gun_collisions;

View File

@ -24,6 +24,9 @@ pub struct PlayerFiringInfo {
pub is_reloading: bool,
pub gun_ready_pose: GunReadyPose,
pub is_inspecting: bool,
/// This is specifically for situations where the player is holding a gun that is stuck against a wall, and should not be false
/// until the gun stops colliding with the wall.
pub gun_colliding_with_object: bool,
}
impl Default for PlayerFiringInfo {
@ -36,6 +39,7 @@ impl Default for PlayerFiringInfo {
is_reloading: false,
gun_ready_pose: GunReadyPose::LowReady,
is_inspecting: false,
gun_colliding_with_object: false,
}
}
}

View File

@ -1,2 +1,3 @@
pub mod guns;
pub mod player;
pub mod collisions;

View File

@ -144,6 +144,7 @@ pub fn capture_hand_usage(
} else if !mouse_wheel_events.is_empty() {
if player.0.equipment.is_firearm() {
for mouse_wheel_event in mouse_wheel_events.read() {
if player_firing_info.gun_colliding_with_object { continue; }
if mouse_wheel_event.y >= 0.0 {
player_firing_info.gun_ready_pose = GunReadyPose::HighReady;
} else {
@ -196,7 +197,7 @@ pub fn capture_hand_usage(
}
};
if resources.mouse_buttons.pressed(MouseButton::Right)
&& !resources.game_ui_state.any_window() && !player_linear_xz_state.is_sprinting()
&& !resources.game_ui_state.any_window() && !player_linear_xz_state.is_sprinting() && !player_firing_info.gun_colliding_with_object
{
player_firing_info.gun_ready_pose = GunReadyPose::HighReady;
let rotation_lerp_quat = in_hand_transform.rotation.lerp(

View File

@ -4,10 +4,10 @@ use crate::{
comps::core::{
controller::capture_input,
inventory::plugin::InventoryPlugin,
spawners::{player::player_spawner, spawn::SpawnerPlugin},
spawners::{player::player_spawner, spawn::SpawnerPlugin}, events::{bullet_collision::BulletCollisionEvent, equipped_gun_collision::EquippedGunCollisionEvent},
},
logic::core::{
guns::{despawn_shots::{despawn_muzzle_flashes, despawn_stray_bullets}, inspect::inspect_firearm},
guns::{despawn_shots::{despawn_muzzle_flashes, despawn_stray_bullets}, inspect::inspect_firearm, equipped_gun_collisions::update_gun_position_on_collision},
player::{
camera_player_sync::{
follow_cursor_with_camera, update_camera_vertical_position, MouseMovementSettings,
@ -15,7 +15,7 @@ use crate::{
hands::{capture_hand_usage, interact_action},
player_values_state::PlayerValuesState,
player_vertical_sync::sync_player_y_state, camera_switching::switch_camera,
},
}, collisions::collision_handler,
},
setup::{
//animations::{load_animations, AllAnimations},
@ -60,9 +60,13 @@ pub fn load_scene(application: &mut App) {
application.add_systems(Update, change_equipment.before(player_spawner));
application.add_systems(Update, (despawn_muzzle_flashes, despawn_stray_bullets));
application.add_systems(Update, inspect_firearm);
application.add_systems(Update, collision_handler);
application.add_systems(Update, update_gun_position_on_collision);
//application.add_systems(Update, animate_player);
//application.add_systems(Update, register_bullet_hits);
application.add_event::<EquipmentChangeEvent>();
application.add_event::<BulletCollisionEvent>();
application.add_event::<EquippedGunCollisionEvent>();
}