Skip to content

Commit

Permalink
Better tower outlines (#2)
Browse files Browse the repository at this point in the history
  • Loading branch information
rparrett authored May 20, 2024
1 parent 1768d00 commit dc33026
Show file tree
Hide file tree
Showing 8 changed files with 114 additions and 111 deletions.
10 changes: 10 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ rand = "0.8.5"
serde = "*"
ron = "0.8.0"
bevy_mod_outline = "0.7.0"
bevy-scene-hook = "10.0.0"

[target.'cfg(target_arch = "wasm32")'.dependencies]
web-sys = { version = "*", features = ["console", "Window", "Storage"] }
Binary file added assets/models/tower.glb
Binary file not shown.
2 changes: 1 addition & 1 deletion src/loading.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ pub struct Models {
pub tile4: Handle<Scene>,
#[asset(path = "models/itemspawner.glb#Scene0")]
pub item_spawner: Handle<Scene>,
#[asset(path = "models/towerbase.glb#Scene0")]
#[asset(path = "models/tower.glb#Scene0")]
pub tower_base: Handle<Scene>,
#[asset(path = "models/towerheadsm.glb#Scene0")]
pub tower_head: Handle<Scene>,
Expand Down
17 changes: 5 additions & 12 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use bevy_dolly::prelude::*;
#[cfg(feature = "inspector")]
use bevy_inspector_egui::quick::WorldInspectorPlugin;
use bevy_rapier3d::prelude::*;
use bevy_scene_hook::HookPlugin;
use bevy_tnua::prelude::*;
use bevy_tnua_rapier3d::{TnuaRapier3dIOBundle, TnuaRapier3dPlugin, TnuaRapier3dSensorShape};
use bevy_two_entities::tuple::{TupleQueryExt, TupleQueryMutExt};
Expand All @@ -25,7 +26,7 @@ use map::{
map_to_world, Floor, Item, ItemSpawner, Lava, MapPlugin, MovingFloor, PlacedTower, TilePos,
START_TILE,
};
use outline::{InnerMeshOutline, OutlinePlugin};
use outline::OutlinePlugin;
use save::SavePlugin;
use settings::{MusicSetting, SfxSetting};
use starfield::StarfieldPlugin;
Expand Down Expand Up @@ -193,7 +194,8 @@ fn main() {
keyboard_navigation: true,
..default()
})
.add_plugins(DefaultNavigationPlugins);
.add_plugins(DefaultNavigationPlugins)
.add_plugins(HookPlugin);

app.init_resource::<Lives>()
.register_type::<Lives>()
Expand All @@ -214,7 +216,6 @@ fn main() {
{
app.add_plugins(WorldInspectorPlugin::new());
app.add_plugins(RapierDebugRenderPlugin::default());
app.register_type::<SelectedTilePos>();
app.register_type::<SelectedTile>();
app.register_type::<SelectedItem>();
}
Expand Down Expand Up @@ -383,7 +384,6 @@ fn cursor(
}

fn item_probe(
mut commands: Commands,
mut collision_events: EventReader<CollisionEvent>,
probe_query: Query<&Parent, With<ItemProbe>>,
item_query: Query<Entity, With<Item>>,
Expand All @@ -400,23 +400,16 @@ fn item_probe(
if let Ok(mut selected_item) = selected_item_query.get_mut(probe_entity.get()) {
selected_item.0 = Some(item_entity);
}

commands.entity(item_entity).insert(InnerMeshOutline {
width: 3.,
color: Color::hsla(160., 0.9, 0.5, 1.0),
});
}
CollisionEvent::Stopped(e1, e2, _) => {
let queries = (&probe_query, &item_query);
let Some((_, item_entity)) = queries.get_both(*e1, *e2) else {
if !queries.both(*e1, *e2) {
continue;
};

for mut selected_item in selected_item_query.iter_mut() {
selected_item.0 = None;
}

commands.entity(item_entity).remove::<InnerMeshOutline>();
}
}
}
Expand Down
19 changes: 19 additions & 0 deletions src/map.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use std::{f32::consts::FRAC_PI_2, fmt::Display, time::Duration};

use bevy::{prelude::*, utils::HashSet};
use bevy_mod_outline::{AsyncSceneInheritOutline, OutlineBundle, OutlineVolume};
use bevy_rapier3d::prelude::*;
use bevy_tnua::TnuaPipelineStages;
use rand::{seq::SliceRandom, thread_rng, Rng};
Expand Down Expand Up @@ -214,6 +215,15 @@ fn spawn_map(
TilePos(pos),
Collider::cuboid(TILE_SIZE.x / 2., TILE_SIZE.y / 2., TILE_SIZE.x / 2.),
ActiveEvents::COLLISION_EVENTS,
OutlineBundle {
outline: OutlineVolume {
width: 3.0,
colour: Color::hsla(160., 0.9, 0.5, 1.0),
visible: true,
},
..default()
},
AsyncSceneInheritOutline,
));

if *col_val == 3 {
Expand Down Expand Up @@ -361,6 +371,15 @@ fn item_spawner(
},
Collider::ball(0.6),
Sensor,
OutlineBundle {
outline: OutlineVolume {
width: 3.0,
colour: Color::hsla(160., 0.9, 0.5, 1.0),
visible: true,
},
..default()
},
AsyncSceneInheritOutline,
))
.id();

Expand Down
102 changes: 31 additions & 71 deletions src/outline.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use bevy::prelude::*;
use bevy_mod_outline::{
AutoGenerateOutlineNormalsPlugin, OutlineBundle, OutlinePlugin as ActualOutlinePlugin,
OutlineVolume,
AsyncSceneInheritOutlinePlugin, AutoGenerateOutlineNormalsPlugin,
OutlinePlugin as ActualOutlinePlugin, OutlineVolume,
};

use crate::{
Expand All @@ -15,77 +15,38 @@ impl Plugin for OutlinePlugin {
fn build(&self, app: &mut App) {
app.add_plugins(ActualOutlinePlugin)
.add_plugins(AutoGenerateOutlineNormalsPlugin)
.add_systems(Update, update_inner)
.add_systems(Update, remove_inner)
.add_plugins(AsyncSceneInheritOutlinePlugin)
.add_systems(Update, update);
}
}

#[derive(Component)]
pub struct InnerMeshOutline {
pub color: Color,
pub width: f32,
}

fn update_inner(
mut commands: Commands,
query: Query<(Entity, &InnerMeshOutline), Changed<InnerMeshOutline>>,
children_query: Query<&Children>,
mesh_query: Query<Entity, With<Handle<Mesh>>>,
) {
for (entity, inner_mesh_outline) in &query {
for descendant in children_query.iter_descendants(entity) {
if let Ok(mesh_entity) = mesh_query.get(descendant) {
// TODO we may be able to fix the "separate outline for tower head and body"
// problem by only adding the OutlineBundle to the toplevel mesh and then adding
// InheritOutlineBundle to every descendant of that mesh.
commands.entity(mesh_entity).insert(OutlineBundle {
outline: OutlineVolume {
width: inner_mesh_outline.width,
colour: inner_mesh_outline.color,
visible: true,
},
..default()
});
}
}
}
}

fn remove_inner(
mut commands: Commands,
mut removed: RemovedComponents<InnerMeshOutline>,
children_query: Query<&Children>,
mesh_query: Query<Entity, With<Handle<Mesh>>>,
) {
for entity in removed.read() {
for descendant in children_query.iter_descendants(entity) {
if let Ok(mesh_entity) = mesh_query.get(descendant) {
commands.entity(mesh_entity).remove::<OutlineBundle>();
}
}
}
}

fn update(
mut commands: Commands,
player_query: Query<
selection_changed_query: Query<
(&SelectedTile, &SelectedItem),
Or<(Changed<SelectedTile>, Changed<SelectedTile>)>,
Or<(Changed<SelectedTile>, Changed<SelectedItem>)>,
>,
outlines_query: Query<Entity, With<InnerMeshOutline>>,
grabbed_item_changed_query: Query<(), Changed<GrabbedItem>>,
grabbed_item_removed: RemovedComponents<GrabbedItem>,
grabbed_item_query: Query<&Item, With<GrabbedItem>>,
invalid_tile_query: Query<(), Or<(With<MovingFloor>, With<PlacedTower>, With<ItemSpawner>)>>,
placed_tower_query: Query<&PlacedTower>,
mut outline_query: Query<&mut OutlineVolume>,
) {
let Ok((tile, item)) = player_query.get_single() else {
if selection_changed_query.is_empty()
&& grabbed_item_changed_query.is_empty()
&& grabbed_item_removed.is_empty()
{
return;
};
}

for entity in &outlines_query {
commands.entity(entity).remove::<InnerMeshOutline>();
for mut outline in &mut outline_query {
outline.visible = false;
}

let Ok((tile, item)) = selection_changed_query.get_single() else {
return;
};

let grabbed_item = grabbed_item_query.get_single();

match grabbed_item {
Expand All @@ -94,10 +55,9 @@ fn update(

if let Some(entity) = tile.0 {
if let Ok(placed_tower) = placed_tower_query.get(entity) {
commands.entity(placed_tower.0).insert(InnerMeshOutline {
width: 3.,
color: Color::hsla(160., 0.9, 0.5, 1.0),
});
if let Ok(mut outline) = outline_query.get_mut(placed_tower.0) {
outline.visible = true;
}
}
}
}
Expand All @@ -106,21 +66,21 @@ fn update(

if let Some(entity) = tile.0 {
if invalid_tile_query.get(entity).is_err() {
commands.entity(entity).insert(InnerMeshOutline {
width: 3.,
color: Color::hsla(160., 0.9, 0.5, 1.0),
});
if let Ok(mut outline) = outline_query.get_mut(entity) {
outline.visible = true;
}
}
}
}
_ => {
// Outline items that can be grabbed
// No item is grabbed.
// Check selected item to see if it is
// grabbable, and if so outline it.

if let Some(entity) = item.0 {
commands.entity(entity).insert(InnerMeshOutline {
width: 3.,
color: Color::hsla(160., 0.9, 0.5, 1.0),
});
if let Ok(mut outline) = outline_query.get_mut(entity) {
outline.visible = true;
}
}
}
}
Expand Down
Loading

0 comments on commit dc33026

Please sign in to comment.