Refactor menu input a bit
This commit is contained in:
@@ -21,9 +21,8 @@ pub fn sample_gamepad(pad: &Gamepad, input_map: &BaseInputMap) -> FighterInputFr
|
|||||||
// Digital buttons driving movement isn't used by the
|
// Digital buttons driving movement isn't used by the
|
||||||
// default bindings (the stick covers that), but stay
|
// default bindings (the stick covers that), but stay
|
||||||
// consistent with the keyboard backend if one is added.
|
// consistent with the keyboard backend if one is added.
|
||||||
InputMapAction::MovementXDir(sign) if pressed => movement.x += sign as f32,
|
InputMapAction::MovementXDir(sign) => if pressed { movement.x += sign as f32 },
|
||||||
InputMapAction::MovementYDir(sign) if pressed => movement.y += sign as f32,
|
InputMapAction::MovementYDir(sign) => if pressed { movement.y += sign as f32 },
|
||||||
_ => {}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// gamepad does not have a separate axis for left and right, so sign here should be 1
|
// gamepad does not have a separate axis for left and right, so sign here should be 1
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ pub fn sample_keyboard(key: &ButtonInput<KeyCode>, input_map: &BaseInputMap) ->
|
|||||||
InputMapAction::Jump => input_frame.jump = action_strength != FGi32::ZERO,
|
InputMapAction::Jump => input_frame.jump = action_strength != FGi32::ZERO,
|
||||||
InputMapAction::Shield => input_frame.shield = action_strength != FGi32::ZERO,
|
InputMapAction::Shield => input_frame.shield = action_strength != FGi32::ZERO,
|
||||||
InputMapAction::Attack => input_frame.attack = action_strength != FGi32::ZERO,
|
InputMapAction::Attack => input_frame.attack = action_strength != FGi32::ZERO,
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,6 +19,15 @@ pub enum InputMapAction {
|
|||||||
Attack,
|
Attack,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum MenuInputMapAction {
|
||||||
|
MovementXDir(i32),
|
||||||
|
MovementYDir(i32),
|
||||||
|
Accept,
|
||||||
|
Back,
|
||||||
|
Start,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct KeyboardInputMapElement {
|
pub struct KeyboardInputMapElement {
|
||||||
pub key: KeyCode,
|
pub key: KeyCode,
|
||||||
@@ -37,8 +46,26 @@ pub struct GamepadInputMapElement {
|
|||||||
pub action: InputMapAction,
|
pub action: InputMapAction,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct MenuKeyboardInputMapElement {
|
||||||
|
pub key: KeyCode,
|
||||||
|
pub action: MenuInputMapAction,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct MenuGamepadInputMapElement {
|
||||||
|
pub binding: GamepadBinding,
|
||||||
|
pub action: MenuInputMapAction,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Resource)]
|
#[derive(Resource)]
|
||||||
pub struct BaseInputMap {
|
pub struct BaseInputMap {
|
||||||
pub keyboard: Vec<KeyboardInputMapElement>,
|
pub keyboard: Vec<KeyboardInputMapElement>,
|
||||||
pub gamepad: Vec<GamepadInputMapElement>,
|
pub gamepad: Vec<GamepadInputMapElement>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Resource)]
|
||||||
|
pub struct MenuInputMap {
|
||||||
|
pub keyboard: Vec<MenuKeyboardInputMapElement>,
|
||||||
|
pub gamepad: Vec<MenuGamepadInputMapElement>,
|
||||||
|
}
|
||||||
|
|||||||
+32
-1
@@ -18,7 +18,8 @@ pub use buffer::{FighterCommands, FighterInput};
|
|||||||
pub use frame::FighterInputFrame;
|
pub use frame::FighterInputFrame;
|
||||||
pub use map::{
|
pub use map::{
|
||||||
BaseInputMap, GamepadBinding, GamepadInputMapElement, InputActionState, InputMapAction,
|
BaseInputMap, GamepadBinding, GamepadInputMapElement, InputActionState, InputMapAction,
|
||||||
KeyboardInputMapElement,
|
KeyboardInputMapElement, MenuGamepadInputMapElement, MenuInputMap, MenuInputMapAction,
|
||||||
|
MenuKeyboardInputMapElement,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
@@ -91,6 +92,9 @@ impl Plugin for FighterInputPlugin {
|
|||||||
key: KeyCode::KeyJ,
|
key: KeyCode::KeyJ,
|
||||||
action: InputMapAction::Attack,
|
action: InputMapAction::Attack,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
|
// Menu
|
||||||
],
|
],
|
||||||
gamepad: vec![
|
gamepad: vec![
|
||||||
// left stick
|
// left stick
|
||||||
@@ -121,6 +125,33 @@ impl Plugin for FighterInputPlugin {
|
|||||||
binding: GamepadBinding::Button(GamepadButton::East),
|
binding: GamepadBinding::Button(GamepadButton::East),
|
||||||
action: InputMapAction::Attack,
|
action: InputMapAction::Attack,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
],
|
||||||
|
})
|
||||||
|
.insert_resource(MenuInputMap {
|
||||||
|
keyboard: vec![
|
||||||
|
MenuKeyboardInputMapElement { key: KeyCode::ArrowUp, action: MenuInputMapAction::MovementYDir(1) },
|
||||||
|
MenuKeyboardInputMapElement { key: KeyCode::ArrowDown, action: MenuInputMapAction::MovementYDir(-1) },
|
||||||
|
MenuKeyboardInputMapElement { key: KeyCode::ArrowLeft, action: MenuInputMapAction::MovementXDir(-1) },
|
||||||
|
MenuKeyboardInputMapElement { key: KeyCode::ArrowRight, action: MenuInputMapAction::MovementXDir(1) },
|
||||||
|
MenuKeyboardInputMapElement { key: KeyCode::Enter, action: MenuInputMapAction::Accept },
|
||||||
|
MenuKeyboardInputMapElement { key: KeyCode::Escape, action: MenuInputMapAction::Back },
|
||||||
|
MenuKeyboardInputMapElement { key: KeyCode::Space, action: MenuInputMapAction::Start },
|
||||||
|
MenuKeyboardInputMapElement { key: KeyCode::KeyW, action: MenuInputMapAction::MovementYDir(1) },
|
||||||
|
MenuKeyboardInputMapElement { key: KeyCode::KeyS, action: MenuInputMapAction::MovementYDir(-1) },
|
||||||
|
MenuKeyboardInputMapElement { key: KeyCode::KeyA, action: MenuInputMapAction::MovementXDir(-1) },
|
||||||
|
MenuKeyboardInputMapElement { key: KeyCode::KeyD, action: MenuInputMapAction::MovementXDir(1) },
|
||||||
|
],
|
||||||
|
gamepad: vec![
|
||||||
|
MenuGamepadInputMapElement { binding: GamepadBinding::Button(GamepadButton::DPadUp), action: MenuInputMapAction::MovementYDir(1) },
|
||||||
|
MenuGamepadInputMapElement { binding: GamepadBinding::Button(GamepadButton::DPadDown), action: MenuInputMapAction::MovementYDir(-1) },
|
||||||
|
MenuGamepadInputMapElement { binding: GamepadBinding::Button(GamepadButton::DPadLeft), action: MenuInputMapAction::MovementXDir(-1) },
|
||||||
|
MenuGamepadInputMapElement { binding: GamepadBinding::Button(GamepadButton::DPadRight), action: MenuInputMapAction::MovementXDir(1) },
|
||||||
|
MenuGamepadInputMapElement { binding: GamepadBinding::Axis(GamepadAxis::LeftStickX, 1), action: MenuInputMapAction::MovementXDir(1) },
|
||||||
|
MenuGamepadInputMapElement { binding: GamepadBinding::Axis(GamepadAxis::LeftStickY, 1), action: MenuInputMapAction::MovementYDir(1) },
|
||||||
|
MenuGamepadInputMapElement { binding: GamepadBinding::Button(GamepadButton::South), action: MenuInputMapAction::Accept },
|
||||||
|
MenuGamepadInputMapElement { binding: GamepadBinding::Button(GamepadButton::West), action: MenuInputMapAction::Back },
|
||||||
|
MenuGamepadInputMapElement { binding: GamepadBinding::Button(GamepadButton::Start), action: MenuInputMapAction::Start },
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
.init_resource::<LocalInputAssignments>()
|
.init_resource::<LocalInputAssignments>()
|
||||||
|
|||||||
+6
-5
@@ -1,4 +1,4 @@
|
|||||||
use bevy::{input_focus::InputFocus, prelude::*, text::FontSourceTemplate, ui::auto_directional_navigation::AutoDirectionalNavigation};
|
use bevy::{input_focus::InputFocus, picking::{hover::PickingInteraction, Pickable}, prelude::*, text::FontSourceTemplate, ui::auto_directional_navigation::AutoDirectionalNavigation};
|
||||||
|
|
||||||
use crate::{game_settings::CommonAssets, menus::style::{FGUiButtonType, FGUiStyle}};
|
use crate::{game_settings::CommonAssets, menus::style::{FGUiButtonType, FGUiStyle}};
|
||||||
|
|
||||||
@@ -24,6 +24,7 @@ pub fn menu_button(label: &str) -> impl Scene {
|
|||||||
BackgroundColor(Color::srgb(0.15, 0.15, 0.15))
|
BackgroundColor(Color::srgb(0.15, 0.15, 0.15))
|
||||||
AutoDirectionalNavigation::default()
|
AutoDirectionalNavigation::default()
|
||||||
Children [(
|
Children [(
|
||||||
|
Pickable::IGNORE
|
||||||
Text(label)
|
Text(label)
|
||||||
TextFont {
|
TextFont {
|
||||||
font: FontSourceTemplate::Handle("fonts/roboto.ttf"),
|
font: FontSourceTemplate::Handle("fonts/roboto.ttf"),
|
||||||
@@ -43,14 +44,14 @@ pub fn button_setup(query: Query<(Entity, &FGUiButton, &mut Node), Added<FGUiBut
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn button_style_system(query: Query<(Entity, &FGUiButton, &Interaction, &mut Node), Changed<Interaction>>, common_assets: Res<CommonAssets>, styles: Res<Assets<FGUiStyle>>, mut commands: Commands) {
|
pub fn button_style_system(query: Query<(Entity, &FGUiButton, &PickingInteraction, &mut Node), Changed<PickingInteraction>>, common_assets: Res<CommonAssets>, styles: Res<Assets<FGUiStyle>>, mut commands: Commands) {
|
||||||
let styles = styles.get(&common_assets.ui_style).expect("UI styles should be loaded");
|
let styles = styles.get(&common_assets.ui_style).expect("UI styles should be loaded");
|
||||||
|
|
||||||
for (entity, button, interaction, mut node) in query {
|
for (entity, button, interaction, mut node) in query {
|
||||||
let button_style_to_use = match interaction {
|
let button_style_to_use = match interaction {
|
||||||
Interaction::Pressed => &styles.button_styles[&button.button_type].press,
|
PickingInteraction::Pressed => &styles.button_styles[&button.button_type].press,
|
||||||
Interaction::Hovered => &styles.button_styles[&button.button_type].hover,
|
PickingInteraction::Hovered => &styles.button_styles[&button.button_type].hover,
|
||||||
Interaction::None => &styles.button_styles[&button.button_type].normal,
|
PickingInteraction::None => &styles.button_styles[&button.button_type].normal,
|
||||||
};
|
};
|
||||||
button_style_to_use.apply(&mut commands, entity, &mut node);
|
button_style_to_use.apply(&mut commands, entity, &mut node);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use bevy::{prelude::*, text::FontSourceTemplate};
|
use bevy::{picking::Pickable, prelude::*, text::FontSourceTemplate};
|
||||||
|
|
||||||
use crate::{fighter::manifest::FighterManifest, menus::{button::FGUiButton, style::FGUiButtonType}};
|
use crate::{fighter::manifest::FighterManifest, menus::{button::FGUiButton, style::FGUiButtonType}};
|
||||||
|
|
||||||
@@ -25,6 +25,7 @@ impl CharacterBox {
|
|||||||
}
|
}
|
||||||
BackgroundColor(Color::srgb(1.0, 1.0, 1.0))
|
BackgroundColor(Color::srgb(1.0, 1.0, 1.0))
|
||||||
Children [
|
Children [
|
||||||
|
Pickable::IGNORE
|
||||||
Node {
|
Node {
|
||||||
width: percent(100)
|
width: percent(100)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ use bevy::prelude::*;
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
fighter::manifest::{FighterManifest, FighterManifestRegistry},
|
fighter::manifest::{FighterManifest, FighterManifestRegistry},
|
||||||
input::{LocalInputAssignments, LocalInputSource},
|
input::LocalInputAssignments,
|
||||||
menus::{MenuMarker, build_menu_with_props, cursor::FGMenuCursor, spawn_menu, wrap_menu},
|
menus::{MenuMarker, build_menu_with_props, cursor::FGMenuCursor, spawn_menu, wrap_menu},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -16,10 +16,12 @@ pub struct CharacterSelectScreen {
|
|||||||
|
|
||||||
pub fn setup_character_select(
|
pub fn setup_character_select(
|
||||||
existing_menu: Option<Single<Entity, With<MenuMarker>>>,
|
existing_menu: Option<Single<Entity, With<MenuMarker>>>,
|
||||||
commands: Commands,
|
mut commands: Commands,
|
||||||
manifest_registry: Res<FighterManifestRegistry>,
|
manifest_registry: Res<FighterManifestRegistry>,
|
||||||
assignments: Res<LocalInputAssignments>,
|
assignments: Res<LocalInputAssignments>,
|
||||||
manifests: Res<Assets<FighterManifest>>,
|
manifests: Res<Assets<FighterManifest>>,
|
||||||
|
mut meshes: ResMut<Assets<Mesh>>,
|
||||||
|
mut materials: ResMut<Assets<ColorMaterial>>,
|
||||||
) {
|
) {
|
||||||
let character_boxes = manifest_registry
|
let character_boxes = manifest_registry
|
||||||
.iter()
|
.iter()
|
||||||
@@ -32,6 +34,10 @@ pub fn setup_character_select(
|
|||||||
.map(|(slot, _)| fighter_portrait::FighterPortrait::create(*slot))
|
.map(|(slot, _)| fighter_portrait::FighterPortrait::create(*slot))
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
for (slot, _) in assignments.iter() {
|
||||||
|
commands.spawn(FGMenuCursor::create_cursor(*slot, &mut meshes, &mut materials));
|
||||||
|
}
|
||||||
|
|
||||||
let menu = bsn! {
|
let menu = bsn! {
|
||||||
// Characters
|
// Characters
|
||||||
CharacterSelectScreen {
|
CharacterSelectScreen {
|
||||||
@@ -75,7 +81,6 @@ pub fn create_portrait(
|
|||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
mut meshes: ResMut<Assets<Mesh>>,
|
mut meshes: ResMut<Assets<Mesh>>,
|
||||||
mut materials: ResMut<Assets<ColorMaterial>>,
|
mut materials: ResMut<Assets<ColorMaterial>>,
|
||||||
mapping: Res<LocalInputAssignments>,
|
|
||||||
) {
|
) {
|
||||||
let entity = commands
|
let entity = commands
|
||||||
.spawn_scene(fighter_portrait::FighterPortrait::create(player_slot))
|
.spawn_scene(fighter_portrait::FighterPortrait::create(player_slot))
|
||||||
@@ -83,7 +88,7 @@ pub fn create_portrait(
|
|||||||
commands.entity(container).add_child(entity);
|
commands.entity(container).add_child(entity);
|
||||||
|
|
||||||
commands.spawn(
|
commands.spawn(
|
||||||
FGMenuCursor::create_cursor(player_slot, meshes, materials, mapping)
|
FGMenuCursor::create_cursor(player_slot, &mut meshes, &mut materials)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -98,7 +103,6 @@ pub fn handle_fighter_slot_addition(
|
|||||||
commands: Commands,
|
commands: Commands,
|
||||||
meshes: ResMut<Assets<Mesh>>,
|
meshes: ResMut<Assets<Mesh>>,
|
||||||
materials: ResMut<Assets<ColorMaterial>>,
|
materials: ResMut<Assets<ColorMaterial>>,
|
||||||
mapping: Res<LocalInputAssignments>,
|
|
||||||
) {
|
) {
|
||||||
let slot = addition.slot;
|
let slot = addition.slot;
|
||||||
create_portrait(
|
create_portrait(
|
||||||
@@ -107,43 +111,20 @@ pub fn handle_fighter_slot_addition(
|
|||||||
commands,
|
commands,
|
||||||
meshes,
|
meshes,
|
||||||
materials,
|
materials,
|
||||||
mapping,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn css_input(
|
pub fn css_input(
|
||||||
key: Res<ButtonInput<KeyCode>>,
|
menu_inputs: Res<crate::menus::input::MenuInputState>,
|
||||||
pads: Query<(Entity, &Gamepad)>,
|
|
||||||
mut assignments: ResMut<LocalInputAssignments>,
|
mut assignments: ResMut<LocalInputAssignments>,
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
) {
|
) {
|
||||||
if key.get_pressed().len() != 0 {
|
for (source, input) in &menu_inputs.inputs {
|
||||||
if let None = assignments
|
if input.movement != Vec2::ZERO {
|
||||||
.iter()
|
let existed = assignments.iter().any(|(_, assigned)| assigned == source);
|
||||||
.find(|(_, source)| matches!(source, LocalInputSource::Keyboard))
|
|
||||||
{
|
|
||||||
if let Some(free_idx) = assignments.get_free_player_index() {
|
|
||||||
assignments.push((free_idx, LocalInputSource::Keyboard));
|
|
||||||
info!("New keyboard assignment");
|
|
||||||
commands.trigger(FighterSlotAssigned { slot: free_idx });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (entity, pad) in pads {
|
|
||||||
if pad.any_pressed(GamepadButton::all()) {
|
|
||||||
let existed = assignments
|
|
||||||
.iter()
|
|
||||||
.find(|(_, assignment)| {
|
|
||||||
if let LocalInputSource::Gamepad(assignment_entity) = assignment {
|
|
||||||
return entity == *assignment_entity;
|
|
||||||
}
|
|
||||||
false
|
|
||||||
})
|
|
||||||
.is_some();
|
|
||||||
|
|
||||||
if !existed && let Some(free_idx) = assignments.get_free_player_index() {
|
if !existed && let Some(free_idx) = assignments.get_free_player_index() {
|
||||||
assignments.push((free_idx, LocalInputSource::Gamepad(entity)));
|
assignments.push((free_idx, *source));
|
||||||
|
info!("New input assignment");
|
||||||
commands.trigger(FighterSlotAssigned { slot: free_idx });
|
commands.trigger(FighterSlotAssigned { slot: free_idx });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+46
-44
@@ -1,12 +1,11 @@
|
|||||||
use bevy::{
|
use bevy::{
|
||||||
asset::uuid::Uuid,
|
asset::uuid::Uuid,
|
||||||
math::VectorSpace,
|
picking::{pointer::{Location, PointerAction, PointerId, PointerInput, PointerLocation}, Pickable},
|
||||||
picking::pointer::{Location, PointerId, PointerInput, PointerLocation},
|
|
||||||
prelude::*,
|
prelude::*,
|
||||||
window::{NormalizedWindowRef, WindowRef},
|
window::WindowRef,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{game_settings::GameSettings, input::LocalInputAssignments, menus::input::MenuInput};
|
use crate::{input::LocalInputAssignments, menus::input::{MenuInput, MenuInputState}};
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub enum MenuCursorInputSource {
|
pub enum MenuCursorInputSource {
|
||||||
@@ -19,28 +18,20 @@ pub enum MenuCursorInputSource {
|
|||||||
pub struct FGMenuCursor {
|
pub struct FGMenuCursor {
|
||||||
pub input_source: MenuCursorInputSource,
|
pub input_source: MenuCursorInputSource,
|
||||||
pub velocity: Vec2,
|
pub velocity: Vec2,
|
||||||
|
pub accepting: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FGMenuCursor {
|
impl FGMenuCursor {
|
||||||
pub fn create_cursor(
|
pub fn create_cursor(
|
||||||
player_slot: usize,
|
player_slot: usize,
|
||||||
mut meshes: ResMut<Assets<Mesh>>,
|
meshes: &mut Assets<Mesh>,
|
||||||
mut materials: ResMut<Assets<ColorMaterial>>,
|
materials: &mut Assets<ColorMaterial>,
|
||||||
mapping: Res<LocalInputAssignments>,
|
|
||||||
) -> impl Bundle {
|
) -> impl Bundle {
|
||||||
let (_, mapping) = mapping
|
|
||||||
.iter()
|
|
||||||
.find(|(slot, _)| *slot == player_slot)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let material = materials.add(ColorMaterial::from_color(
|
let material = materials.add(ColorMaterial::from_color(
|
||||||
bevy::color::palettes::tailwind::ROSE_600,
|
bevy::color::palettes::tailwind::ROSE_600,
|
||||||
));
|
));
|
||||||
let circle_mesh = meshes.add(Circle::new(25.0));
|
let circle_mesh = meshes.add(Circle::new(25.0));
|
||||||
let pointer_id = match mapping {
|
let pointer_id = PointerId::Custom(Uuid::new_v4());
|
||||||
crate::input::LocalInputSource::Keyboard => PointerId::Mouse,
|
|
||||||
crate::input::LocalInputSource::Gamepad(_) => PointerId::Custom(Uuid::new_v4()),
|
|
||||||
};
|
|
||||||
(
|
(
|
||||||
FGMenuCursor {
|
FGMenuCursor {
|
||||||
input_source: super::cursor::MenuCursorInputSource::Player(player_slot),
|
input_source: super::cursor::MenuCursorInputSource::Player(player_slot),
|
||||||
@@ -51,42 +42,37 @@ impl FGMenuCursor {
|
|||||||
Mesh2d(circle_mesh),
|
Mesh2d(circle_mesh),
|
||||||
Transform::IDENTITY,
|
Transform::IDENTITY,
|
||||||
GlobalZIndex(1000),
|
GlobalZIndex(1000),
|
||||||
|
Pickable::IGNORE,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn cursor_input(
|
pub fn cursor_input(
|
||||||
window: Single<Entity, With<bevy::window::PrimaryWindow>>,
|
window: Single<(Entity, &Window), With<bevy::window::PrimaryWindow>>,
|
||||||
query: Query<(
|
query: Query<(
|
||||||
&mut FGMenuCursor,
|
&mut FGMenuCursor,
|
||||||
&PointerLocation,
|
&PointerLocation,
|
||||||
&PointerId,
|
&PointerId,
|
||||||
)>,
|
)>,
|
||||||
key: Res<ButtonInput<KeyCode>>,
|
|
||||||
assignments: Res<LocalInputAssignments>,
|
assignments: Res<LocalInputAssignments>,
|
||||||
game_settings: Res<GameSettings>,
|
menu_inputs: Res<MenuInputState>,
|
||||||
time: Res<Time>,
|
time: Res<Time>,
|
||||||
pads: Query<(Entity, &Gamepad)>,
|
|
||||||
mut pointer_writer: MessageWriter<PointerInput>,
|
mut pointer_writer: MessageWriter<PointerInput>,
|
||||||
) {
|
) {
|
||||||
|
let (window_entity, window) = window.into_inner();
|
||||||
|
|
||||||
for (mut cursor, location, pointer_id) in query {
|
for (mut cursor, location, pointer_id) in query {
|
||||||
let input = match cursor.input_source {
|
let input = match cursor.input_source {
|
||||||
MenuCursorInputSource::Any => {
|
MenuCursorInputSource::Any => {
|
||||||
let mut input = MenuInput::default();
|
let mut input = MenuInput::default();
|
||||||
for (_, source) in assignments.iter() {
|
for (_, source) in assignments.iter() {
|
||||||
input.accumulate(&MenuInput::from_source(
|
input.accumulate(&menu_inputs.get(source));
|
||||||
source,
|
|
||||||
&key,
|
|
||||||
&pads,
|
|
||||||
&assignments,
|
|
||||||
&game_settings,
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
input
|
input
|
||||||
}
|
}
|
||||||
MenuCursorInputSource::Player(slot) => {
|
MenuCursorInputSource::Player(slot) => {
|
||||||
if let Some((_, source)) = assignments.iter().find(|(idx, _)| *idx == slot) {
|
if let Some((_, source)) = assignments.iter().find(|(idx, _)| *idx == slot) {
|
||||||
MenuInput::from_source(source, &key, &pads, &assignments, &game_settings)
|
menu_inputs.get(source)
|
||||||
} else {
|
} else {
|
||||||
MenuInput::default()
|
MenuInput::default()
|
||||||
}
|
}
|
||||||
@@ -96,35 +82,45 @@ pub fn cursor_input(
|
|||||||
let movement_screen_space = input.movement * Vec2::new(1.0, -1.0);
|
let movement_screen_space = input.movement * Vec2::new(1.0, -1.0);
|
||||||
cursor.velocity = movement_screen_space * 500.0;
|
cursor.velocity = movement_screen_space * 500.0;
|
||||||
|
|
||||||
if cursor.velocity == Vec2::ZERO {
|
let initial_cursor_pos = Vec2::new(window.width(), window.height()) / 2.0;
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
let old_cursor_pos = location
|
let old_cursor_pos = location
|
||||||
.location
|
.location
|
||||||
.clone()
|
.clone()
|
||||||
.map(|loc| loc.position)
|
.map(|loc| loc.position)
|
||||||
.unwrap_or(Vec2::ZERO);
|
.unwrap_or(initial_cursor_pos);
|
||||||
let cursor_pos = old_cursor_pos + cursor.velocity * time.delta_secs();
|
let cursor_pos = (old_cursor_pos + cursor.velocity * time.delta_secs())
|
||||||
|
.clamp(Vec2::ZERO, Vec2::new(window.width(), window.height()));
|
||||||
dbg!(cursor_pos);
|
let pointer_location = Location {
|
||||||
|
|
||||||
pointer_writer.write(PointerInput {
|
|
||||||
pointer_id: *pointer_id,
|
|
||||||
location: Location {
|
|
||||||
target: bevy::camera::NormalizedRenderTarget::Window(
|
target: bevy::camera::NormalizedRenderTarget::Window(
|
||||||
WindowRef::Primary
|
WindowRef::Primary
|
||||||
.normalize(Some(window.entity()))
|
.normalize(Some(window_entity))
|
||||||
.expect("Primary window should be valid"),
|
.expect("Primary window should be valid"),
|
||||||
),
|
),
|
||||||
position: cursor_pos,
|
position: cursor_pos,
|
||||||
},
|
};
|
||||||
action: bevy::picking::pointer::PointerAction::Move {
|
|
||||||
|
if location.location.is_none() || cursor_pos != old_cursor_pos {
|
||||||
|
pointer_writer.write(PointerInput {
|
||||||
|
pointer_id: *pointer_id,
|
||||||
|
location: pointer_location.clone(),
|
||||||
|
action: PointerAction::Move {
|
||||||
delta: cursor_pos - old_cursor_pos,
|
delta: cursor_pos - old_cursor_pos,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
//cursor.position
|
if input.accept != cursor.accepting {
|
||||||
|
cursor.accepting = input.accept;
|
||||||
|
pointer_writer.write(PointerInput {
|
||||||
|
pointer_id: *pointer_id,
|
||||||
|
location: pointer_location,
|
||||||
|
action: if input.accept {
|
||||||
|
PointerAction::Press(PointerButton::Primary)
|
||||||
|
} else {
|
||||||
|
PointerAction::Release(PointerButton::Primary)
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -150,3 +146,9 @@ pub fn update_cursor_transform(
|
|||||||
trf.translation = Vec3::new(cursor_pos_viewport.x, cursor_pos_viewport.y, 0.0);
|
trf.translation = Vec3::new(cursor_pos_viewport.x, cursor_pos_viewport.y, 0.0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn despawn_cursors(query: Query<Entity, With<FGMenuCursor>>, mut commands: Commands) {
|
||||||
|
for entity in query {
|
||||||
|
commands.entity(entity).despawn();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+83
-42
@@ -1,65 +1,106 @@
|
|||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
|
|
||||||
use crate::{game_settings::GameSettings, input::{LocalInputAssignments, LocalInputSource}};
|
use crate::input::{
|
||||||
|
GamepadBinding, LocalInputSource, MenuInputMap, MenuInputMapAction,
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default, Clone)]
|
||||||
pub struct MenuInput {
|
pub struct MenuInput {
|
||||||
pub movement: Vec2,
|
pub movement: Vec2,
|
||||||
pub accept: bool
|
pub accept: bool,
|
||||||
|
pub back: bool,
|
||||||
|
pub start: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Resource, Default)]
|
||||||
|
pub struct MenuInputState {
|
||||||
|
pub inputs: Vec<(LocalInputSource, MenuInput)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MenuInputState {
|
||||||
|
pub fn get(&self, source: &LocalInputSource) -> MenuInput {
|
||||||
|
self.inputs.iter().find(|(candidate, _)| candidate == source).map(|(_, input)| input.clone()).unwrap_or_default()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn aggregate(&self) -> MenuInput {
|
||||||
|
self.inputs.iter().fold(MenuInput::default(), |mut result, (_, input)| {
|
||||||
|
result.movement += input.movement;
|
||||||
|
result.accept |= input.accept;
|
||||||
|
result.back |= input.back;
|
||||||
|
result.start |= input.start;
|
||||||
|
result
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MenuInput {
|
impl MenuInput {
|
||||||
|
|
||||||
pub fn accumulate(&mut self, other: &Self) {
|
pub fn accumulate(&mut self, other: &Self) {
|
||||||
self.movement += other.movement;
|
self.movement += other.movement;
|
||||||
self.movement = self.movement.normalize();
|
self.accept |= other.accept;
|
||||||
self.accept = self.accept || other.accept;
|
self.back |= other.back;
|
||||||
|
self.start |= other.start;
|
||||||
|
self.movement = self.movement.clamp_length_max(1.0);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn from_gamepad(gamepad: &Gamepad, game_settings: &GameSettings) -> Self {
|
fn keyboard_input(key: &ButtonInput<KeyCode>, map: &MenuInputMap) -> MenuInput {
|
||||||
let mut x = gamepad.left_stick().x;
|
let mut input = MenuInput::default();
|
||||||
let mut y = gamepad.left_stick().y;
|
for binding in &map.keyboard {
|
||||||
|
let pressed = key.pressed(binding.key);
|
||||||
let deadzone = game_settings.input_common.stick_deadzone.to_num::<f32>();
|
match binding.action {
|
||||||
|
MenuInputMapAction::MovementXDir(sign) => {
|
||||||
if x.abs() < deadzone {
|
input.movement.x += if pressed { sign as f32 } else { 0.0 };
|
||||||
x = 0.0;
|
|
||||||
}
|
}
|
||||||
|
MenuInputMapAction::MovementYDir(sign) => {
|
||||||
if y.abs() < deadzone {
|
input.movement.y += if pressed { sign as f32 } else { 0.0 };
|
||||||
y = 0.0;
|
|
||||||
}
|
}
|
||||||
|
MenuInputMapAction::Accept => input.accept |= pressed,
|
||||||
MenuInput {
|
MenuInputMapAction::Back => input.back |= pressed,
|
||||||
movement: Vec2::new(x, y),
|
MenuInputMapAction::Start => input.start |= pressed,
|
||||||
accept: gamepad.pressed(GamepadButton::South)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn from_keyboard(keeb: &ButtonInput<KeyCode>) -> Self {
|
input.accept = map.keyboard.iter().any(|binding| matches!(binding.action, MenuInputMapAction::Accept) && key.pressed(binding.key));
|
||||||
let x = if keeb.pressed(KeyCode::KeyA) { -1.0 } else { 0.0 };
|
input.back = map.keyboard.iter().any(|binding| matches!(binding.action, MenuInputMapAction::Back) && key.pressed(binding.key));
|
||||||
let x = x + if keeb.pressed(KeyCode::KeyD) { 1.0 } else { 0.0 };
|
input.start = map.keyboard.iter().any(|binding| matches!(binding.action, MenuInputMapAction::Start) && key.pressed(binding.key));
|
||||||
let y = if keeb.pressed(KeyCode::KeyS) { -1.0 } else { 0.0 };
|
input.movement = input.movement.normalize_or_zero();
|
||||||
let y = y + if keeb.pressed(KeyCode::KeyW) { 1.0 } else { 0.0 };
|
input
|
||||||
|
}
|
||||||
|
|
||||||
let movement = Vec2::new(x, y);
|
fn gamepad_input(pad: &Gamepad, map: &MenuInputMap, deadzone: f32) -> MenuInput {
|
||||||
MenuInput {
|
let mut input = MenuInput::default();
|
||||||
movement: movement,
|
for binding in &map.gamepad {
|
||||||
accept: keeb.pressed(KeyCode::Enter)
|
let (value, just_pressed) = match binding.binding {
|
||||||
|
GamepadBinding::Button(button) => (if pad.pressed(button) { 1.0 } else { 0.0 }, pad.just_pressed(button)),
|
||||||
|
GamepadBinding::Axis(axis, sign) => (pad.get(axis).unwrap_or_default() * sign as f32, false),
|
||||||
|
};
|
||||||
|
match binding.action {
|
||||||
|
MenuInputMapAction::MovementXDir(sign) => {
|
||||||
|
input.movement.x += value * sign as f32;
|
||||||
}
|
}
|
||||||
|
MenuInputMapAction::MovementYDir(sign) => {
|
||||||
|
input.movement.y += value * sign as f32;
|
||||||
}
|
}
|
||||||
|
MenuInputMapAction::Accept => { input.accept |= value > 0.5; input.accept |= just_pressed; }
|
||||||
pub fn from_source(source: &LocalInputSource, key: &ButtonInput<KeyCode>, gamepads: &Query<(Entity, &Gamepad)>, assignments: &LocalInputAssignments, game_settings: &GameSettings) -> Self {
|
MenuInputMapAction::Back => { input.back |= value > 0.5; input.back |= just_pressed; }
|
||||||
match source {
|
MenuInputMapAction::Start => { input.start |= value > 0.5; input.start |= just_pressed; }
|
||||||
LocalInputSource::Keyboard => Self::from_keyboard(key),
|
|
||||||
LocalInputSource::Gamepad(entity) => {
|
|
||||||
let gamepad = gamepads.get(*entity);
|
|
||||||
|
|
||||||
if let Ok((_, gamepad)) = gamepad {
|
|
||||||
Self::from_gamepad(gamepad, game_settings)
|
|
||||||
} else {
|
|
||||||
Self::default()
|
|
||||||
}
|
}
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
if input.movement.x.abs() < deadzone { input.movement.x = 0.0; }
|
||||||
|
if input.movement.y.abs() < deadzone { input.movement.y = 0.0; }
|
||||||
|
input.movement = input.movement.clamp_length_max(1.0);
|
||||||
|
input
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn sample_menu_inputs(
|
||||||
|
key: Res<ButtonInput<KeyCode>>,
|
||||||
|
map: Res<MenuInputMap>,
|
||||||
|
pads: Query<(Entity, &Gamepad)>,
|
||||||
|
mut state: ResMut<MenuInputState>,
|
||||||
|
) {
|
||||||
|
state.inputs.clear();
|
||||||
|
let deadzone = 0.275;
|
||||||
|
state.inputs.push((LocalInputSource::Keyboard, keyboard_input(&key, &map)));
|
||||||
|
for (entity, pad) in pads.iter() {
|
||||||
|
state.inputs.push((LocalInputSource::Gamepad(entity), gamepad_input(pad, &map, deadzone)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+19
-3
@@ -117,6 +117,8 @@ impl Plugin for MenuPlugin {
|
|||||||
character_select::setup_character_select,
|
character_select::setup_character_select,
|
||||||
)
|
)
|
||||||
.init_resource::<navigation::UINavigationActionState>()
|
.init_resource::<navigation::UINavigationActionState>()
|
||||||
|
.init_resource::<navigation::UINavigationRepeat>()
|
||||||
|
.init_resource::<input::MenuInputState>()
|
||||||
.insert_resource(InputFocusVisible(true))
|
.insert_resource(InputFocusVisible(true))
|
||||||
.insert_resource(GlobalUiDebugOptions {
|
.insert_resource(GlobalUiDebugOptions {
|
||||||
enabled: true,
|
enabled: true,
|
||||||
@@ -138,18 +140,32 @@ impl Plugin for MenuPlugin {
|
|||||||
navigation::give_default_focus,
|
navigation::give_default_focus,
|
||||||
scaling::ui_scaling_system,
|
scaling::ui_scaling_system,
|
||||||
button::button_setup,
|
button::button_setup,
|
||||||
button::button_style_system,
|
button::button_style_system.after(bevy::picking::PickingSystems::Hover),
|
||||||
button::button_focus_style_system,
|
button::button_focus_style_system,
|
||||||
)
|
)
|
||||||
.run_if(resource_exists::<CommonAssets>),
|
.run_if(resource_exists::<CommonAssets>),
|
||||||
)
|
)
|
||||||
.add_systems(Startup, scaling::ui_update_scale_on_startup)
|
.add_systems(Startup, scaling::ui_update_scale_on_startup)
|
||||||
.add_systems(
|
.add_systems(
|
||||||
PostUpdate,
|
PreUpdate,
|
||||||
(character_select::css_input, (cursor::cursor_input, cursor::update_cursor_transform).chain())
|
input::sample_menu_inputs
|
||||||
|
.before(cursor::cursor_input)
|
||||||
|
.run_if(resource_exists::<crate::input::MenuInputMap>),
|
||||||
|
)
|
||||||
|
.add_systems(
|
||||||
|
PreUpdate,
|
||||||
|
cursor::cursor_input
|
||||||
|
.before(bevy::picking::PickingSystems::ProcessInput)
|
||||||
.run_if(in_state(AppState::CharacterSelect))
|
.run_if(in_state(AppState::CharacterSelect))
|
||||||
.run_if(any_with_component::<CharacterSelectScreen>),
|
.run_if(any_with_component::<CharacterSelectScreen>),
|
||||||
)
|
)
|
||||||
|
.add_systems(
|
||||||
|
PostUpdate,
|
||||||
|
(character_select::css_input, cursor::update_cursor_transform)
|
||||||
|
.run_if(in_state(AppState::CharacterSelect))
|
||||||
|
.run_if(any_with_component::<CharacterSelectScreen>),
|
||||||
|
)
|
||||||
|
.add_systems(OnExit(AppState::CharacterSelect), cursor::despawn_cursors)
|
||||||
.add_observer(character_select::handle_fighter_slot_addition)
|
.add_observer(character_select::handle_fighter_slot_addition)
|
||||||
.add_plugins(bevy_common_assets::ron::RonAssetPlugin::<FGUiStyle>::new(
|
.add_plugins(bevy_common_assets::ron::RonAssetPlugin::<FGUiStyle>::new(
|
||||||
&["stylesheet.ron"],
|
&["stylesheet.ron"],
|
||||||
|
|||||||
+36
-43
@@ -1,5 +1,6 @@
|
|||||||
use std::{collections::HashSet, time::Duration};
|
use std::collections::HashSet;
|
||||||
use bevy::{camera::NormalizedRenderTarget, input_focus::{InputFocus, InputFocusVisible}, math::CompassOctant, picking::{backend::HitData, pointer::{Location, PointerId}}, prelude::*, ui::auto_directional_navigation::AutoDirectionalNavigator};
|
use bevy::{camera::NormalizedRenderTarget, input_focus::{InputFocus, InputFocusVisible}, math::CompassOctant, picking::{backend::HitData, pointer::{Location, PointerId}}, prelude::*, ui::auto_directional_navigation::AutoDirectionalNavigator};
|
||||||
|
use super::input::MenuInputState;
|
||||||
|
|
||||||
// Action state and input handling
|
// Action state and input handling
|
||||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||||
@@ -12,35 +13,6 @@ enum DirectionalNavigationAction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl DirectionalNavigationAction {
|
impl DirectionalNavigationAction {
|
||||||
fn variants() -> Vec<Self> {
|
|
||||||
vec![
|
|
||||||
DirectionalNavigationAction::Up,
|
|
||||||
DirectionalNavigationAction::Down,
|
|
||||||
DirectionalNavigationAction::Left,
|
|
||||||
DirectionalNavigationAction::Right,
|
|
||||||
DirectionalNavigationAction::Select,
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
fn keycode(&self) -> KeyCode {
|
|
||||||
match self {
|
|
||||||
DirectionalNavigationAction::Up => KeyCode::ArrowUp,
|
|
||||||
DirectionalNavigationAction::Down => KeyCode::ArrowDown,
|
|
||||||
DirectionalNavigationAction::Left => KeyCode::ArrowLeft,
|
|
||||||
DirectionalNavigationAction::Right => KeyCode::ArrowRight,
|
|
||||||
DirectionalNavigationAction::Select => KeyCode::Enter,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn gamepad_button(&self) -> GamepadButton {
|
|
||||||
match self {
|
|
||||||
DirectionalNavigationAction::Up => GamepadButton::DPadUp,
|
|
||||||
DirectionalNavigationAction::Down => GamepadButton::DPadDown,
|
|
||||||
DirectionalNavigationAction::Left => GamepadButton::DPadLeft,
|
|
||||||
DirectionalNavigationAction::Right => GamepadButton::DPadRight,
|
|
||||||
DirectionalNavigationAction::Select => GamepadButton::South,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Resource)]
|
#[derive(Default, Resource)]
|
||||||
@@ -48,29 +20,50 @@ pub struct UINavigationActionState {
|
|||||||
pressed_actions: HashSet<DirectionalNavigationAction>,
|
pressed_actions: HashSet<DirectionalNavigationAction>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Default, Resource)]
|
||||||
|
pub struct UINavigationRepeat {
|
||||||
|
held_for: f32,
|
||||||
|
repeating: bool,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Default, Component, Clone)]
|
#[derive(Default, Component, Clone)]
|
||||||
pub struct NavigationDefaultFocus;
|
pub struct NavigationDefaultFocus;
|
||||||
|
|
||||||
pub fn process_inputs(
|
pub fn process_inputs(
|
||||||
mut action_state: ResMut<UINavigationActionState>,
|
mut action_state: ResMut<UINavigationActionState>,
|
||||||
keyboard_input: Res<ButtonInput<KeyCode>>,
|
mut repeat: ResMut<UINavigationRepeat>,
|
||||||
gamepad_input: Query<&Gamepad>,
|
menu_inputs: Res<MenuInputState>,
|
||||||
|
time: Res<Time>,
|
||||||
) {
|
) {
|
||||||
action_state.pressed_actions.clear();
|
action_state.pressed_actions.clear();
|
||||||
|
let input = menu_inputs.aggregate();
|
||||||
for action in DirectionalNavigationAction::variants() {
|
/*let repeat_press = if has_direction && !initial_press {
|
||||||
if keyboard_input.just_pressed(action.keycode()) {
|
repeat.held_for += time.delta_secs();
|
||||||
action_state.pressed_actions.insert(action);
|
if !repeat.repeating && repeat.held_for >= 0.3 {
|
||||||
}
|
repeat.repeating = true;
|
||||||
}
|
true
|
||||||
|
} else if repeat.repeating && (repeat.held_for - 0.3) % 0.08 < time.delta_secs() {
|
||||||
for gamepad in gamepad_input.iter() {
|
true
|
||||||
for action in DirectionalNavigationAction::variants() {
|
} else {
|
||||||
if gamepad.just_pressed(action.gamepad_button()) {
|
false
|
||||||
action_state.pressed_actions.insert(action);
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
if !has_direction {
|
||||||
|
repeat.held_for = 0.0;
|
||||||
|
repeat.repeating = false;
|
||||||
|
} else if initial_press {
|
||||||
|
repeat.held_for = 0.0;
|
||||||
|
repeat.repeating = false;
|
||||||
}
|
}
|
||||||
|
false
|
||||||
|
};
|
||||||
|
if initial_press || repeat_press {
|
||||||
|
if input.movement.y > 0.0 { action_state.pressed_actions.insert(DirectionalNavigationAction::Up); }
|
||||||
|
if input.movement.y < 0.0 { action_state.pressed_actions.insert(DirectionalNavigationAction::Down); }
|
||||||
|
if input.movement.x < 0.0 { action_state.pressed_actions.insert(DirectionalNavigationAction::Left); }
|
||||||
|
if input.movement.x > 0.0 { action_state.pressed_actions.insert(DirectionalNavigationAction::Right); }
|
||||||
}
|
}
|
||||||
|
if input.accept { action_state.pressed_actions.insert(DirectionalNavigationAction::Select); }*/
|
||||||
}
|
}
|
||||||
|
|
||||||
const FOCUSED_BORDER: Srgba = bevy::color::palettes::tailwind::BLUE_50;
|
const FOCUSED_BORDER: Srgba = bevy::color::palettes::tailwind::BLUE_50;
|
||||||
|
|||||||
Reference in New Issue
Block a user