From a16d5dde71dfc64ad13ecab650044fc2fdcbbd5d Mon Sep 17 00:00:00 2001 From: Lubba64 Date: Fri, 14 Feb 2025 17:58:34 -0500 Subject: [PATCH] feat: Outlines --- sbepis/Cargo.toml | 2 + sbepis/src/main.rs | 22 ++++--- sbepis/src/npcs/consort.rs | 65 +++++++++++-------- .../src/player_controller/camera_controls.rs | 64 ++++++++++++++++-- sbepis/src/player_controller/mod.rs | 14 ++-- 5 files changed, 117 insertions(+), 50 deletions(-) diff --git a/sbepis/Cargo.toml b/sbepis/Cargo.toml index f3be3c3d..b441ba19 100644 --- a/sbepis/Cargo.toml +++ b/sbepis/Cargo.toml @@ -38,6 +38,8 @@ blenvy = { git = "https://github.com/jwright159/Blenvy.git" } bevy-butler = "0.5.4-alpha.3" bevy_hanabi = "0.14.0" typetag = "0.2.19" +bevy_edge_detection = "0.15.4" +bevy_mod_outline = {version="0.9.0", git="https://github.com/komadori/bevy_mod_outline.git"} [build-dependencies] winres = "0.1" diff --git a/sbepis/src/main.rs b/sbepis/src/main.rs index c49d4f7e..83bf684a 100644 --- a/sbepis/src/main.rs +++ b/sbepis/src/main.rs @@ -7,6 +7,8 @@ use bevy::input::common_conditions::input_just_pressed; use bevy::log::LogPlugin; use bevy::prelude::*; use bevy::winit::WinitWindows; +use bevy_edge_detection::EdgeDetectionPlugin; +use bevy_mod_outline::OutlinePlugin; use bevy_rapier3d::prelude::*; use winit::window::Icon; @@ -58,20 +60,22 @@ fn main() { }, }) .set(LogPlugin { - filter: "info,sbepis=debug,avian3d=debug,wgpu=error,naga=warn,calloop=error,symphonia_core=warn,symphonia_bundle_mp3=warn,blenvy=error".into(), + filter: "info,sbepis=debug,avian3d=debug,wgpu=error,naga=warn,calloop=error,symphonia_core=warn,symphonia_bundle_mp3=warn,blenvy=error,bevy_mod_outline=error".into(), ..default() }), - RapierPhysicsPlugin::::default(), - #[cfg(feature = "rapier_debug")] - RapierDebugRenderPlugin::default(), - #[cfg(feature = "inspector")] - bevy_inspector_egui::quick::WorldInspectorPlugin::new(), - #[cfg(feature = "overview_camera")] - overview_camera::OverviewCameraPlugin, - bevy_hanabi::HanabiPlugin, + OutlinePlugin, )); app.add_plugins(( + #[cfg(feature = "rapier_debug")] + RapierDebugRenderPlugin::default(), + #[cfg(feature = "inspector")] + bevy_inspector_egui::quick::WorldInspectorPlugin::new(), + #[cfg(feature = "overview_camera")] + overview_camera::OverviewCameraPlugin, + EdgeDetectionPlugin::default(), + RapierPhysicsPlugin::::default(), + bevy_hanabi::HanabiPlugin, player_commands::PlayerCommandsPlugin, camera::PlayerCameraPlugin, skybox::SkyboxPlugin, diff --git a/sbepis/src/npcs/consort.rs b/sbepis/src/npcs/consort.rs index 9ba5d296..aa558e88 100644 --- a/sbepis/src/npcs/consort.rs +++ b/sbepis/src/npcs/consort.rs @@ -10,6 +10,7 @@ use crate::entity::{Healing, RandomInput, RotateTowardMovement, SpawnHealthBar}; use crate::gridbox_material; use crate::main_bundles::Mob; use crate::npcs::NpcPlugin; +use crate::player_controller::camera_controls::InteractOutlineComponentTarget; use crate::questing::{QuestGiver, SpawnQuestMarker}; use super::name_tags::SpawnNameTag; @@ -39,36 +40,44 @@ fn spawn_consort( continue; } + commands.entity(ev.entity).insert(( + Name::new("Consort"), + Transform::from_translation(ev.position), + Mob, + SpawnHealthBar, + RandomInput::default(), + Healing(0.2), + RotateTowardMovement, + Consort, + QuestGiver::default(), + SpawnQuestMarker, + SpawnNameTag, + )); + let mut id = None; + commands.entity(ev.entity).with_children(|parent| { + id = Some( + parent + .spawn(( + Transform::from_translation(Vec3::Y * 0.5), + Mesh3d( + meshes.add( + Capsule3d::new(0.25, 0.5) + .mesh() + .rings(1) + .latitudes(8) + .longitudes(16) + .uv_profile(CapsuleUvProfile::Fixed), + ), + ), + MeshMaterial3d(gridbox_material("magenta", &mut materials, &asset_server)), + Collider::capsule_y(0.25, 0.25), + )) + .id(), + ); + }); commands .entity(ev.entity) - .insert(( - Name::new("Consort"), - Transform::from_translation(ev.position), - Mob, - SpawnHealthBar, - RandomInput::default(), - Healing(0.2), - RotateTowardMovement, - Consort, - QuestGiver::default(), - SpawnQuestMarker, - SpawnNameTag, - )) - .with_child(( - Transform::from_translation(Vec3::Y * 0.5), - Mesh3d( - meshes.add( - Capsule3d::new(0.25, 0.5) - .mesh() - .rings(1) - .latitudes(8) - .longitudes(16) - .uv_profile(CapsuleUvProfile::Fixed), - ), - ), - MeshMaterial3d(gridbox_material("magenta", &mut materials, &asset_server)), - Collider::capsule_y(0.25, 0.25), - )); + .insert(InteractOutlineComponentTarget(id.unwrap())); ev_spawned.send(EntitySpawned(ev.entity)); } } diff --git a/sbepis/src/player_controller/camera_controls.rs b/sbepis/src/player_controller/camera_controls.rs index 42e56977..971b4373 100644 --- a/sbepis/src/player_controller/camera_controls.rs +++ b/sbepis/src/player_controller/camera_controls.rs @@ -3,6 +3,7 @@ use std::marker::PhantomData; use bevy::prelude::*; use bevy_butler::*; +use bevy_mod_outline::OutlineVolume; use bevy_rapier3d::prelude::*; use leafwing_input_manager::prelude::*; @@ -64,21 +65,24 @@ fn rotate_camera_and_body( } } +#[resource(plugin = PlayerControllerPlugin, init = LastHitEntity(None))] +#[derive(Resource)] +pub struct LastHitEntity(pub Option); + +#[derive(Component)] +pub struct InteractOutlineComponentTarget(pub Entity); + pub fn interact_with( rapier_context: Query<&RapierContext>, player_camera: Query<&GlobalTransform, With>, entities: Query>, parents: Query<&Parent>, input: Query<&ActionState>, + outline_targets: Query<&InteractOutlineComponentTarget>, + mut commands: Commands, + mut last_hit: ResMut, mut ev_interact: EventWriter>, ) { - if !match input.iter().find(|input| !input.disabled()) { - Some(input) => input.just_pressed(&PlayerAction::Interact), - None => false, - } { - return; - } - let player_camera = player_camera.get_single().expect("Player camera missing"); let mut hit_entity: Option<(Option, f32)> = None; rapier_context.single().intersections_with_ray( @@ -102,6 +106,52 @@ pub fn interact_with( }, ); + if let Some((Some(entity), _)) = hit_entity { + if last_hit.0.is_none() { + for target in outline_targets.get(entity).iter() { + commands.entity(target.0).insert(OutlineVolume { + colour: Color::Srgba(Srgba { + red: 1.0, + green: 1.0, + blue: 1.0, + alpha: 1.0, + }), + visible: true, + width: 1.0, + }); + } + } + match last_hit.0 { + None => {} + Some(last_hit_entity) => { + if last_hit_entity != entity { + for target in outline_targets.get(last_hit_entity).iter() { + commands.entity(target.0).remove::(); + } + last_hit.0 = None; + } + } + } + last_hit.0 = Some(entity); + } else { + match last_hit.0 { + None => {} + Some(last_hit_entity) => { + for target in outline_targets.get(last_hit_entity).iter() { + commands.entity(target.0).remove::(); + } + last_hit.0 = None; + } + } + } + + if !match input.iter().find(|input| !input.disabled()) { + Some(input) => input.just_pressed(&PlayerAction::Interact), + None => false, + } { + return; + } + if let Some((Some(entity), _)) = hit_entity { ev_interact.send(InteractedWith::new(entity)); } diff --git a/sbepis/src/player_controller/mod.rs b/sbepis/src/player_controller/mod.rs index cee991c5..06112004 100644 --- a/sbepis/src/player_controller/mod.rs +++ b/sbepis/src/player_controller/mod.rs @@ -1,11 +1,5 @@ use std::f32::consts::PI; -use bevy::prelude::*; -use bevy::render::mesh::CapsuleUvProfile; -use bevy_butler::*; -use bevy_rapier3d::prelude::*; -use leafwing_input_manager::prelude::*; - use crate::camera::PlayerCamera; use crate::gridbox_material; use crate::input::*; @@ -14,6 +8,12 @@ use crate::main_bundles::Mob; use crate::menus::{ InputManagerMenuPlugin, Menu, MenuStack, MenuWithInputManager, MenuWithoutMouse, }; +use bevy::prelude::*; +use bevy::render::mesh::CapsuleUvProfile; +use bevy_butler::*; +use bevy_edge_detection::EdgeDetection; +use bevy_rapier3d::prelude::*; +use leafwing_input_manager::prelude::*; use self::camera_controls::*; use self::weapons::hammer::*; @@ -98,6 +98,8 @@ fn setup( ..default() }), PlayerCamera, + Msaa::Off, + EdgeDetection::default(), Pitch(0.0), SpatialListener::new(-0.25), ))