Rip out 2d, merge editor and debug mode

main
Elijah C. Voigt 2 years ago
parent c9b2a22e68
commit f0a64541e1

@ -14,13 +14,6 @@ main = "/Music/Main Track2"
select = "/SFX/MenuSelect" select = "/SFX/MenuSelect"
hover = "/SFX/MenuHover" hover = "/SFX/MenuHover"
###
# 2D Mode SFX
###
[audio.display2d]
pick_up = "/SFX/2D/2DPickUpPiece"
put_down = "/SFX/2D/2DPutDownPiece"
### ###
# 3D Mode SFX # 3D Mode SFX
### ###
@ -147,26 +140,3 @@ exposure = 1.0
gamma = 1.0 gamma = 1.0
pre_saturation = 1.0 pre_saturation = 1.0
post_saturation = 1.0 post_saturation = 1.0
####################
# Display2d settings
####################
[display2d.sprites]
# Sprite file path inside the `assets` folder
file = "images/sprites.png"
# Size of each tile [x,y] (they have to be the same size)
tile_size_x = 16
tile_size_y = 16
columns = 8
rows = 1
# The order of each sprite left-to-right, top-to-bottom
sprite_order = ["LightTile"
,"DarkTile"
,"RedQueen"
,"RedDrone"
,"RedPawn"
,"BlueQueen"
,"BlueDrone"
,"BluePawn"
]

@ -74,22 +74,10 @@ fn audio_trigger(
} }
AudioEvent::MenuHover => tweak.get::<String>("audio_menu_hover").unwrap(), AudioEvent::MenuHover => tweak.get::<String>("audio_menu_hover").unwrap(),
AudioEvent::MenuSelect => tweak.get::<String>("audio_menu_select").unwrap(), AudioEvent::MenuSelect => tweak.get::<String>("audio_menu_select").unwrap(),
AudioEvent::PickUp => match state { AudioEvent::PickUp => tweak.get::<String>("audio_display3d_pick_up").unwrap(),
DisplayState::Display2d => tweak.get::<String>("audio_display2d_pick_up").unwrap(), AudioEvent::PutDown => tweak.get::<String>("audio_display3d_put_down").unwrap(),
DisplayState::Display3d => tweak.get::<String>("audio_display3d_pick_up").unwrap(), AudioEvent::Idle | AudioEvent::StopIdle => tweak.get::<String>("audio_display3d_idle").unwrap(),
}, AudioEvent::Invalid => tweak.get::<String>("audio_display3d_invalid").unwrap(),
AudioEvent::PutDown => match state {
DisplayState::Display2d => tweak.get::<String>("audio_display2d_put_down").unwrap(),
DisplayState::Display3d => tweak.get::<String>("audio_display3d_put_down").unwrap(),
},
AudioEvent::Idle | AudioEvent::StopIdle => match state {
DisplayState::Display2d => tweak.get::<String>("audio_display2d_idle").unwrap(),
DisplayState::Display3d => tweak.get::<String>("audio_display3d_idle").unwrap(),
},
AudioEvent::Invalid => match state {
DisplayState::Display2d => tweak.get::<String>("audio_display2d_invalid").unwrap(),
DisplayState::Display3d => tweak.get::<String>("audio_display3d_invalid").unwrap(),
},
}; };
// There is an event for this // There is an event for this
if !aud.is_empty() { if !aud.is_empty() {

@ -19,6 +19,20 @@ impl Plugin for DebugPlugin {
SystemInformationDiagnosticsPlugin::default(), SystemInformationDiagnosticsPlugin::default(),
)) ))
.init_resource::<DebugInfo>() .init_resource::<DebugInfo>()
.insert_resource(
GizmoConfig {
depth_bias: -0.1,
..default()
},
)
.add_systems(Update, (
aabb_gizmo,
))
// Systems that run in the editor mode
.add_systems(Update, (
selected_gizmo.run_if(any_with_component::<game::Selected>()),
selected_position.run_if(any_with_component::<game::Selected>()),
).run_if(resource_exists::<DebugEnabled>()))
.add_systems(Startup, init_debug_ui) .add_systems(Startup, init_debug_ui)
.add_systems( .add_systems(
Update, Update,
@ -141,10 +155,48 @@ fn camera_info(mut debug_infos: ResMut<DebugInfo>, cameras: Query<(&Camera, &Nam
debug_infos.set("Cameras".into(), camera_names); debug_infos.set("Cameras".into(), camera_names);
} }
fn gltf_info( fn aabb_gizmo(
gltfs: Res<Assets<Gltf>>, added: Query<Entity, Added<game::Selected>>,
mut removed: RemovedComponents<game::Selected>,
selected: Query<Entity, With<game::Selected>>,
active: Option<Res<DebugEnabled>>,
mut commands: Commands,
) { ) {
gltfs.iter().for_each(|(_, gltf)| { added.iter().for_each(|e| {
info!("Named animations: {:#?}", gltf.named_animations); commands.entity(e).insert(AabbGizmo { color: Some(Color::RED) });
}); });
removed.read().for_each(|e| {
commands.entity(e).remove::<AabbGizmo>();
});
match active {
Some(_) => selected.iter().for_each(|e| {
commands.entity(e).insert(AabbGizmo { color: Some(Color::RED) });
}),
None => selected.iter().for_each(|e| {
commands.entity(e).remove::<AabbGizmo>();
}),
}
}
/// Draw a gizmo showing cardinal directions for a selected object
fn selected_gizmo(
selected: Query<&GlobalTransform, With<game::Selected>>,
mut gizmos: Gizmos,
) {
selected.iter().for_each(|g| {
let s = g.translation();
gizmos.ray(s, Vec3::X, Color::RED);
gizmos.ray(s, Vec3::Y, Color::GREEN);
gizmos.ray(s, Vec3::Z, Color::BLUE);
});
}
fn selected_position(
selected: Query<(Entity, &GlobalTransform), With<game::Selected>>,
mut debug_info: ResMut<debug::DebugInfo>,
) {
let val = selected.iter().map(|(e, gt)| {
format!("\n{:?} {:?}", e, gt.translation())
}).collect::<Vec<String>>().join("");
debug_info.set("Position".into(), val);
} }

@ -1,404 +0,0 @@
/// TODO: Pick up and move pieces!
/// TODO: Custom Asset: SpriteSheetAtlas Mapper
/// TODO: Handle Cursor!
use bevy::window::{PrimaryWindow, WindowResized};
use crate::{
game::{Board, BoardIndex, Piece, Side, Tile},
prelude::*,
tweak::Tweaks,
};
use serde::Deserialize;
const SCALE: f32 = 4.0;
pub(crate) struct Display2dPlugin;
impl Plugin for Display2dPlugin {
fn build(&self, app: &mut App) {
app.add_systems(Startup, (initialize_camera, set_background))
.add_systems(OnExit(GameState::Loading), initialize_board)
.add_systems(
Update,
(
move_piece
.run_if(in_state(GameState::Play))
.run_if(in_state(DisplayState::Display2d))
.run_if(any_with_component::<game::Selected>()),
select
.run_if(in_state(GameState::Play))
.run_if(in_state(DisplayState::Display2d))
.run_if(|buttons: Res<Input<MouseButton>>| -> bool {
buttons.just_pressed(MouseButton::Left)
}),
update_background.run_if(on_event::<WindowResized>()),
set_transform
.after(game::update_board)
.run_if(any_component_changed::<BoardIndex>),
sync_sprite.run_if(any_component_changed::<Side>),
load_spritesheet.run_if(on_event::<AssetEvent<Tweaks>>()),
// Set Sprite for Pieces
set_sprite.run_if(any_component_changed::<GameSprite>),
// When tweakfile is updated
set_sprite.run_if(resource_exists_and_changed::<SpriteSheet>()),
capture_piece.run_if(any_component_added::<game::Captured>),
),
)
.add_systems(OnEnter(DisplayState::Display2d), activate::<Display2d>)
.add_systems(OnExit(DisplayState::Display2d), deactivate::<Display2d>)
.add_systems(
OnEnter(GameState::Play),
activate::<Display2d>.run_if(in_state(DisplayState::Display2d)),
);
}
}
/// Sprite sheet Resource for later reference
#[derive(Debug, Resource)]
struct SpriteSheet {
handle: Handle<TextureAtlas>,
}
/// Marker component for the 2d entitys
#[derive(Debug, Component)]
pub(crate) struct Display2d;
#[derive(Debug, Component)]
struct BackgroundImage;
/// All possible sprites
/// Necessary because individual components of Piece, Side, and Light/Dark are not homogeneous
#[derive(Debug, Deserialize, Component, Clone, PartialEq)]
pub(crate) enum GameSprite {
RedQueen,
RedDrone,
RedPawn,
BlueQueen,
BlueDrone,
BluePawn,
LightTile,
DarkTile,
}
impl From<(game::Piece, game::Side)> for GameSprite {
fn from((piece, side): (game::Piece, game::Side)) -> GameSprite {
match (piece, side) {
(Piece::Queen, Side::A) => GameSprite::RedQueen,
(Piece::Drone, Side::A) => GameSprite::RedDrone,
(Piece::Pawn, Side::A) => GameSprite::RedPawn,
(Piece::Queen, Side::B) => GameSprite::BlueQueen,
(Piece::Drone, Side::B) => GameSprite::BlueDrone,
(Piece::Pawn, Side::B) => GameSprite::BluePawn,
}
}
}
impl From<Tile> for GameSprite {
fn from(tile: game::Tile) -> GameSprite {
match tile {
Tile::Light => GameSprite::LightTile,
Tile::Dark => GameSprite::DarkTile,
}
}
}
/// STARTUP: Initialize 2d gameplay Camera
fn initialize_camera(mut commands: Commands) {
commands.spawn((
Display2d,
DisplayState::Display2d,
Camera2dBundle {
camera: Camera {
is_active: false,
..default()
},
..default()
},
UiCameraConfig { show_ui: true },
Name::new("2D Camera"),
));
}
/// STARTUP: Load sprite sheet and insert texture atlas
fn load_spritesheet(
mut texture_atlases: ResMut<Assets<TextureAtlas>>,
tweaks: Res<Assets<tweak::Tweaks>>,
mut commands: Commands,
tweaks_file: Res<tweak::GameTweaks>,
) {
info!("Loading spritesheet");
let tweak = tweaks
.get(&tweaks_file.handle.clone())
.expect("Load tweakfiles");
let atlas = TextureAtlas::from_grid(
tweak
.get_handle_unchecked::<Image>("display2d_sprites_file")
.unwrap(),
Vec2::new(
tweak.get::<f32>("display2d_sprites_tile_size_x").unwrap(),
tweak.get::<f32>("display2d_sprites_tile_size_y").unwrap(),
),
tweak.get::<usize>("display2d_sprites_columns").unwrap(),
tweak.get::<usize>("display2d_sprites_rows").unwrap(),
None,
None,
);
commands.insert_resource(SpriteSheet {
handle: texture_atlases.add(atlas),
});
}
fn set_background(
server: Res<AssetServer>,
mut commands: Commands,
window: Query<&Window, With<PrimaryWindow>>,
) {
commands.spawn((
BackgroundImage,
Display2d,
SpriteBundle {
texture: server.load("images/mars-daybreak.png"),
sprite: Sprite {
custom_size: Some(Vec2 {
x: window.single().width(),
y: window.single().height(),
}),
..default()
},
transform: Transform {
translation: Vec3::NEG_Z,
..default()
},
visibility: Visibility::Hidden,
..default()
},
));
}
fn update_background(
mut sprites: Query<&mut Sprite, With<BackgroundImage>>,
mut events: EventReader<WindowResized>,
) {
events
.read()
.for_each(|&WindowResized { width, height, .. }| {
sprites.iter_mut().for_each(|mut sprite| {
sprite.custom_size = Some(Vec2 {
x: width,
y: height,
});
});
});
}
/// STARTUP: Initialize the board for representation
fn initialize_board(board: Option<Res<Board>>, mut commands: Commands) {
if let Some(board) = board {
commands
.spawn((
Display2d,
SpatialBundle {
visibility: Visibility::Hidden,
..default()
},
))
.with_children(|parent| {
// Spawn tiles
game::tiles().for_each(|(index, tile)| {
let game_sprite: GameSprite = tile.clone().into();
parent.spawn((
game_sprite,
tile.clone(),
index,
Display2d,
SpriteSheetBundle { ..default() },
game::Selectable,
));
});
// Spawn pieces
board.pieces().iter().for_each(|(index, piece)| {
let side = Board::side(*index).expect("Spawn valid side");
let game_sprite: GameSprite = (*piece, side).into();
parent.spawn((
game_sprite,
piece.clone(),
Display2d,
index.clone(),
side.clone(),
SpriteSheetBundle { ..default() },
game::Selectable,
));
});
});
}
}
fn sync_sprite(mut events: Query<(&mut GameSprite, &Piece, &Side), Changed<Side>>) {
events
.iter_mut()
.for_each(|(mut game_sprite, piece, side)| {
*game_sprite = (*piece, *side).into();
});
}
fn set_sprite(
mut entities: Query<
(
&mut TextureAtlasSprite,
&mut Handle<TextureAtlas>,
&GameSprite,
),
With<Display2d>,
>,
sprite_sheet: Option<Res<SpriteSheet>>,
tweaks: Res<Assets<Tweaks>>,
tweaks_file: Res<tweak::GameTweaks>,
) {
let tweak = tweaks
.get(tweaks_file.handle.clone())
.expect("Load tweaksfile in set piece sprite");
let sprite_sheet = sprite_sheet.expect("Sprite sheet");
entities
.iter_mut()
.for_each(|(mut sprite, mut texture_atlas, game_sprite)| {
if let Some(index) = tweak
.get::<Vec<GameSprite>>("display2d_sprites_sprite_order")
.unwrap()
.iter()
.position(|s| s == game_sprite)
{
if *texture_atlas != sprite_sheet.handle {
*texture_atlas = sprite_sheet.handle.clone();
}
sprite.index = index;
}
});
}
/// Sets a piece location given it's board index
fn set_transform(
mut events: Query<
(&mut Transform, &BoardIndex, Option<&Piece>, Option<&Tile>),
(
With<Display2d>,
Or<(Changed<BoardIndex>, Added<BoardIndex>)>,
),
>,
) {
events.iter_mut().for_each(|(mut t, i, piece, tile)| {
let x = SCALE * 16.0 * ((i.x as f32) - 3.5);
let y = SCALE * 16.0 * ((i.y as f32) - 1.5);
let z = match (piece, tile) {
(None, None) | (Some(_), Some(_)) => {
error!("Entity with BoardIndex is neither a Piece nor a Tile!");
0.0
}
// Piece
(Some(_), None) => 2.0,
// Tile
(None, Some(_)) => 1.0,
};
*t = Transform::from_scale(Vec3::splat(SCALE)).with_translation(Vec3::new(x, y, z));
debug!("setting position of {:?} to {:?}", i, t);
});
}
/// Select pieces and tiles in 2d
fn select(
candidates: Query<
(
Entity,
&TextureAtlasSprite,
&Handle<TextureAtlas>,
&GlobalTransform,
&BoardIndex,
),
(
With<game::Selectable>,
Without<game::Selected>,
With<Display2d>,
),
>,
windows: Query<&Window, With<PrimaryWindow>>,
cameras: Query<(&Camera, &GlobalTransform), With<Display2d>>,
atlases: Res<Assets<TextureAtlas>>,
mut selections: EventWriter<game::Selection>,
) {
// For each window (there is only one)
windows
.iter()
.filter_map(|window| window.cursor_position())
.for_each(|position| {
// For each 2d Camera (there is only one)
cameras
.iter()
.filter_map(|(camera, transform)| camera.viewport_to_world_2d(transform, position))
.for_each(|pos| {
// For each selectable sprite (there are many)
// Filter down to the list of hit objects
candidates
.iter()
.filter_map(
|(
_,
TextureAtlasSprite { index, anchor, .. },
handle,
transform,
board_index,
)| {
let sprite_size = atlases
.get(handle)
.map(|atlas| {
atlas.textures.get(*index).expect("Atlas Sprite Texture")
})
.expect("Atlas Sprite Rectangle")
.size();
hit::intersects2d(sprite_size, anchor, transform, pos)
.then_some((transform, board_index))
},
)
.max_by(|(a, _), (b, _)| {
a.translation().z.partial_cmp(&b.translation().z).unwrap()
})
.map(|(_, board_index)| board_index)
.iter()
.for_each(|&board_index| {
selections.send(game::Selection(board_index.clone()));
});
});
});
}
fn move_piece(
window: Query<&Window, With<PrimaryWindow>>,
mut query: Query<&mut Transform, (With<game::Selected>, With<game::Piece>, With<Display2d>)>,
camera_query: Query<(&Camera, &GlobalTransform), With<Display2d>>,
) {
query.iter_mut().for_each(|mut t| {
let (camera, camera_t) = camera_query.single();
if let Some(pos) = window
.single()
.cursor_position()
.and_then(|cursor| camera.viewport_to_world_2d(camera_t, cursor))
{
t.translation.x = pos.x;
t.translation.y = pos.y;
}
})
}
/// When a piece is captured, we make it invisible in 2D
fn capture_piece(
mut events: Query<(Entity, &mut Visibility), (With<Display2d>, Added<game::Captured>)>,
mut commands: Commands,
) {
events.iter_mut().for_each(|(entity, mut vis)| {
info!("Hiding captured piece");
*vis = Visibility::Hidden;
commands.entity(entity).remove::<game::Captured>();
});
}

@ -426,14 +426,6 @@ fn set_board_model(
With<Display3d>, With<Display3d>,
), ),
>, >,
mut tiles: Query<
&mut Handle<Scene>,
(
With<TilesComponent>,
Without<game::BoardComponent>,
With<Display3d>,
),
>,
gltfs: Res<Assets<Gltf>>, gltfs: Res<Assets<Gltf>>,
tweaks: Res<Assets<Tweaks>>, tweaks: Res<Assets<Tweaks>>,
tweaks_file: Res<tweak::GameTweaks>, tweaks_file: Res<tweak::GameTweaks>,

@ -1,97 +0,0 @@
use bevy::input::keyboard::KeyboardInput;
use crate::prelude::*;
pub struct EditorPlugin;
impl Plugin for EditorPlugin {
fn build(&self, app: &mut App) {
app.add_systems(Startup, init_editor)
.insert_resource(
GizmoConfig {
depth_bias: -0.1,
..default()
},
)
.add_systems(Update, (
toggle_editor.run_if(on_event::<KeyboardInput>()),
aabb_gizmo,
))
// Systems that run in the editor mode
.add_systems(Update, (
selected_gizmo.run_if(any_with_component::<game::Selected>()),
selected_position.run_if(any_with_component::<game::Selected>()),
).run_if(resource_exists::<EditorActive>()));
}
}
#[derive(Debug, Resource)]
struct EditorActive;
fn init_editor(
mut commands: Commands,
) {
info!("Starting editor");
}
fn toggle_editor(
mut events: EventReader<KeyboardInput>,
active: Option<Res<EditorActive>>,
mut commands: Commands,
) {
events
.read()
.filter(|KeyboardInput {
state, key_code, ..
}| *state == ButtonState::Pressed && *key_code == Some(KeyCode::F3))
.for_each(|_| match active {
Some(_) => commands.remove_resource::<EditorActive>(),
None => commands.insert_resource(EditorActive),
});
}
fn aabb_gizmo(
added: Query<Entity, Added<game::Selected>>,
mut removed: RemovedComponents<game::Selected>,
selected: Query<Entity, With<game::Selected>>,
active: Option<Res<EditorActive>>,
mut commands: Commands,
) {
added.iter().for_each(|e| {
commands.entity(e).insert(AabbGizmo { color: Some(Color::RED) });
});
removed.read().for_each(|e| {
commands.entity(e).remove::<AabbGizmo>();
});
match active {
Some(_) => selected.iter().for_each(|e| {
commands.entity(e).insert(AabbGizmo { color: Some(Color::RED) });
}),
None => selected.iter().for_each(|e| {
commands.entity(e).remove::<AabbGizmo>();
}),
}
}
/// Draw a gizmo showing cardinal directions for a selected object
fn selected_gizmo(
selected: Query<(Entity, &Transform, &GlobalTransform), With<game::Selected>>,
mut gizmos: Gizmos,
) {
selected.iter().for_each(|(e, t, g)| {
let s = g.translation();
gizmos.ray(s, Vec3::X, Color::RED);
gizmos.ray(s, Vec3::Y, Color::GREEN);
gizmos.ray(s, Vec3::Z, Color::BLUE);
});
}
fn selected_position(
selected: Query<(Entity, &GlobalTransform), With<game::Selected>>,
mut debug_info: ResMut<debug::DebugInfo>,
) {
let val = selected.iter().map(|(e, gt)| {
format!("\n{:?} {:?}", e, gt.translation())
}).collect::<Vec<String>>().join("");
debug_info.set("Position".into(), val);
}

@ -34,11 +34,7 @@ impl Plugin for GamePlugin {
) )
.add_systems( .add_systems(
PreUpdate, PreUpdate,
( asserts::<display3d::Display3d>.run_if(in_state(DisplayState::Display3d)).run_if(in_state(GameState::Play)),
asserts::<display2d::Display2d>.run_if(in_state(DisplayState::Display2d)),
asserts::<display3d::Display3d>.run_if(in_state(DisplayState::Display3d)),
)
.run_if(in_state(GameState::Play)),
) )
.add_systems( .add_systems(
PostUpdate, PostUpdate,
@ -481,13 +477,13 @@ fn debug_board(board: Res<Board>, mut debug_info: ResMut<debug::DebugInfo>) {
pub(crate) fn update_board( pub(crate) fn update_board(
mut audio_events: EventWriter<AudioEvent>, mut audio_events: EventWriter<AudioEvent>,
mut events: EventReader<Move>, mut events: EventReader<Move>,
mut pieces: Query<(Entity, &mut BoardIndex, &Side), With<Piece>>, mut pieces: Query<(Entity, &mut BoardIndex), With<Piece>>,
selected: Query<Entity, With<Selected>>, selected: Query<Entity, With<Selected>>,
mut commands: Commands, mut commands: Commands,
mut played: Local<bool>, mut played: Local<bool>,
) { ) {
events.read().for_each(|Move { from, to, .. }| { events.read().for_each(|Move { from, to, .. }| {
pieces.iter_mut().for_each(|(entity, mut index, side)| { pieces.iter_mut().for_each(|(entity, mut index)| {
if *index == *from { if *index == *from {
match to { match to {
Some(to_idx) => { Some(to_idx) => {

@ -3,7 +3,6 @@ use bevy::{
mesh::{MeshVertexAttribute, VertexAttributeValues}, mesh::{MeshVertexAttribute, VertexAttributeValues},
render_resource::VertexFormat, render_resource::VertexFormat,
}, },
sprite::Anchor,
}; };
use crate::prelude::*; use crate::prelude::*;
@ -123,24 +122,3 @@ pub(crate) fn intersects3d(ray: &Ray, mesh: &Mesh, gt: &GlobalTransform) -> Opti
None None
} }
} }
pub(crate) fn intersects2d(
sprite_size: Vec2,
anchor: &Anchor,
transform: &GlobalTransform,
pos: Vec2,
) -> bool {
// Implementation credit goes to the sprite bevy_mod_picking backend
// TODO: Upstream changes
let p = transform.translation();
let size = {
let (transform_scale, _, _) = transform.to_scale_rotation_translation();
sprite_size * transform_scale.truncate()
};
let center = p.truncate() - (anchor.as_vec() * size);
let rect = Rect::from_center_half_size(center, size / 2.0);
rect.contains(pos)
}

@ -6,9 +6,7 @@
mod audio; mod audio;
mod credits; mod credits;
mod debug; mod debug;
mod display2d;
mod display3d; mod display3d;
mod editor;
mod game; mod game;
mod hit; mod hit;
mod loading; mod loading;
@ -60,9 +58,7 @@ fn main() {
); );
app.add_plugins(credits::CreditsPlugin); app.add_plugins(credits::CreditsPlugin);
app.add_plugins(debug::DebugPlugin); app.add_plugins(debug::DebugPlugin);
app.add_plugins(display2d::Display2dPlugin);
app.add_plugins(display3d::Display3dPlugin); app.add_plugins(display3d::Display3dPlugin);
app.add_plugins(editor::EditorPlugin);
app.add_plugins(game::GamePlugin); app.add_plugins(game::GamePlugin);
app.add_plugins(loading::LoadingPlugin); app.add_plugins(loading::LoadingPlugin);
app.add_plugins(menu::MenuPlugin); app.add_plugins(menu::MenuPlugin);
@ -83,8 +79,8 @@ pub enum GameState {
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Default, States, Component)] #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Default, States, Component)]
pub(crate) enum DisplayState { pub(crate) enum DisplayState {
#[default]
Display2d, Display2d,
#[default]
Display3d, Display3d,
} }

@ -6,18 +6,9 @@ pub(crate) struct UiPlugin;
impl Plugin for UiPlugin { impl Plugin for UiPlugin {
fn build(&self, app: &mut App) { fn build(&self, app: &mut App) {
app.add_systems(Startup, initialize);
app.add_systems(OnEnter(GameState::Menu), sync_display_mode);
app.add_systems( app.add_systems(
Update, Update,
( (
toggle_display_mode.run_if(any_component_changed::<Interaction>),
sync_display_mode
.run_if(state_changed::<DisplayState>())
.run_if(in_state(GameState::Menu)),
sync_display_mode
.run_if(state_changed::<DisplayState>())
.run_if(in_state(GameState::Play)),
manage_cursor.run_if(any_component_changed::<Interaction>), manage_cursor.run_if(any_component_changed::<Interaction>),
interactive_button.run_if(any_component_changed::<Interaction>), interactive_button.run_if(any_component_changed::<Interaction>),
), ),
@ -28,79 +19,6 @@ impl Plugin for UiPlugin {
#[derive(Debug, Component)] #[derive(Debug, Component)]
struct UiRoot; struct UiRoot;
fn initialize(mut commands: Commands) {
commands
.spawn((
NodeBundle {
style: Style {
position_type: PositionType::Absolute,
right: Val::Px(5.0),
bottom: Val::Px(5.0),
..default()
},
background_color: Color::WHITE.into(),
visibility: Visibility::Hidden,
..default()
},
UiRoot,
))
.with_children(|parent| {
parent.spawn((
ButtonBundle {
style: Style {
width: Val::Px(25.0),
height: Val::Px(25.0),
..default()
},
background_color: Color::WHITE.into(),
..default()
},
DisplayState::Display2d,
));
});
}
/// When button is clicked, switch display state to that state and hide the other option.
/// Display2d -> Click -> Display2d mode & Display3d butto
fn toggle_display_mode(
events: Query<(&Interaction, &DisplayState), (Changed<Interaction>, With<Button>)>,
mut next_state: ResMut<NextState<DisplayState>>,
) {
events
.iter()
.for_each(|(interaction, display_state)| match interaction {
Interaction::Pressed => match display_state {
DisplayState::Display2d => next_state.set(DisplayState::Display2d),
DisplayState::Display3d => next_state.set(DisplayState::Display3d),
},
_ => (),
});
}
/// Syncs the display button with the UI
/// Runs every time the DisplayState changes
fn sync_display_mode(
mut query: Query<(&mut UiImage, &mut DisplayState), With<Button>>,
mut visibilities: Query<&mut Visibility, With<UiRoot>>,
state: Res<State<DisplayState>>,
server: Res<AssetServer>,
) {
query.iter_mut().for_each(|(mut image, mut display_state)| {
let (texture, target_state) = match state.get() {
DisplayState::Display2d => (server.load("images/3DIcon.png"), DisplayState::Display3d),
DisplayState::Display3d => (server.load("images/2DIcon.png"), DisplayState::Display2d),
};
*image = UiImage {
texture,
..default()
};
*display_state = target_state;
});
visibilities
.iter_mut()
.for_each(|mut visibility| *visibility = Visibility::Visible);
}
fn manage_cursor( fn manage_cursor(
mut window: Query<&mut Window, With<PrimaryWindow>>, mut window: Query<&mut Window, With<PrimaryWindow>>,
state: Res<State<GameState>>, state: Res<State<GameState>>,

Loading…
Cancel
Save