moved some things around.
Signed-off-by: Sienna Meridian Satterwhite <sienna@r3t.io>
This commit is contained in:
@@ -1,342 +0,0 @@
|
||||
//! Input controller - maps raw InputEvents to semantic GameActions
|
||||
//!
|
||||
//! This layer provides:
|
||||
//! - Input remapping (change key bindings)
|
||||
//! - Accessibility (alternative input methods)
|
||||
//! - Context-aware bindings (different actions in different modes)
|
||||
|
||||
use super::game_actions::GameAction;
|
||||
use super::input_events::{InputEvent, KeyCode, MouseButton, TouchPhase};
|
||||
use glam::Vec2;
|
||||
use std::collections::HashMap;
|
||||
|
||||
/// Input binding - maps an input trigger to a game action
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub enum InputBinding {
|
||||
/// Mouse button press/release
|
||||
MouseButton(MouseButton),
|
||||
|
||||
/// Mouse drag with a specific button
|
||||
MouseDrag(MouseButton),
|
||||
|
||||
/// Mouse wheel scroll
|
||||
MouseWheel,
|
||||
|
||||
/// Keyboard key press
|
||||
Key(KeyCode),
|
||||
|
||||
/// Keyboard key with modifiers
|
||||
KeyWithModifiers {
|
||||
key: KeyCode,
|
||||
shift: bool,
|
||||
ctrl: bool,
|
||||
alt: bool,
|
||||
meta: bool,
|
||||
},
|
||||
|
||||
/// Stylus input (Apple Pencil, etc.)
|
||||
StylusDrag,
|
||||
|
||||
/// Touch input
|
||||
TouchDrag,
|
||||
}
|
||||
|
||||
/// Input context - different binding sets for different game modes
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub enum InputContext {
|
||||
/// Manipulating 3D entities
|
||||
EntityManipulation,
|
||||
|
||||
/// Camera control
|
||||
CameraControl,
|
||||
|
||||
/// UI interaction
|
||||
UI,
|
||||
|
||||
/// Text input
|
||||
TextInput,
|
||||
}
|
||||
|
||||
/// Accessibility settings for input processing
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct AccessibilitySettings {
|
||||
/// Mouse sensitivity multiplier (1.0 = normal)
|
||||
pub mouse_sensitivity: f32,
|
||||
|
||||
/// Scroll sensitivity multiplier (1.0 = normal)
|
||||
pub scroll_sensitivity: f32,
|
||||
|
||||
/// Stylus pressure sensitivity (1.0 = normal)
|
||||
pub stylus_sensitivity: f32,
|
||||
|
||||
/// Enable one-handed mode (use keyboard for rotation)
|
||||
pub one_handed_mode: bool,
|
||||
|
||||
/// Invert Y axis for rotation
|
||||
pub invert_y: bool,
|
||||
|
||||
/// Minimum drag distance before registering as drag (in pixels)
|
||||
pub drag_threshold: f32,
|
||||
}
|
||||
|
||||
impl Default for AccessibilitySettings {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
mouse_sensitivity: 1.0,
|
||||
scroll_sensitivity: 1.0,
|
||||
stylus_sensitivity: 1.0,
|
||||
one_handed_mode: false,
|
||||
invert_y: false,
|
||||
drag_threshold: 2.0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Input controller - converts InputEvents to GameActions
|
||||
pub struct InputController {
|
||||
/// Current input context
|
||||
current_context: InputContext,
|
||||
|
||||
/// Bindings for each context
|
||||
bindings: HashMap<InputContext, HashMap<InputBinding, GameAction>>,
|
||||
|
||||
/// Accessibility settings
|
||||
accessibility: AccessibilitySettings,
|
||||
|
||||
/// Drag state tracking
|
||||
drag_state: DragState,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct DragState {
|
||||
/// Is currently dragging
|
||||
active: bool,
|
||||
|
||||
/// Which button/input is dragging
|
||||
source: Option<DragSource>,
|
||||
|
||||
/// Start position
|
||||
start_pos: Vec2,
|
||||
|
||||
/// Last position
|
||||
last_pos: Vec2,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
enum DragSource {
|
||||
MouseLeft,
|
||||
MouseRight,
|
||||
Stylus,
|
||||
Touch,
|
||||
}
|
||||
|
||||
impl InputController {
|
||||
/// Create a new input controller with default bindings
|
||||
pub fn new() -> Self {
|
||||
let mut controller = Self {
|
||||
current_context: InputContext::EntityManipulation,
|
||||
bindings: HashMap::new(),
|
||||
accessibility: AccessibilitySettings::default(),
|
||||
drag_state: DragState::default(),
|
||||
};
|
||||
|
||||
controller.setup_default_bindings();
|
||||
controller
|
||||
}
|
||||
|
||||
/// Set the current input context
|
||||
pub fn set_context(&mut self, context: InputContext) {
|
||||
self.current_context = context;
|
||||
}
|
||||
|
||||
/// Get the current context
|
||||
pub fn context(&self) -> InputContext {
|
||||
self.current_context
|
||||
}
|
||||
|
||||
/// Update accessibility settings
|
||||
pub fn set_accessibility(&mut self, settings: AccessibilitySettings) {
|
||||
self.accessibility = settings;
|
||||
}
|
||||
|
||||
/// Get current accessibility settings
|
||||
pub fn accessibility(&self) -> &AccessibilitySettings {
|
||||
&self.accessibility
|
||||
}
|
||||
|
||||
/// Process an input event and produce game actions
|
||||
pub fn process_event(&mut self, event: &InputEvent) -> Vec<GameAction> {
|
||||
let mut actions = Vec::new();
|
||||
|
||||
match event {
|
||||
InputEvent::MouseMove { pos: _ } => {
|
||||
// Mouse hover - no game actions, just UI tracking
|
||||
// This is handled by egui's custom_input_system
|
||||
}
|
||||
|
||||
InputEvent::Mouse { pos, button, phase } => {
|
||||
self.process_mouse(*pos, *button, *phase, &mut actions);
|
||||
}
|
||||
|
||||
InputEvent::MouseWheel { delta, pos: _ } => {
|
||||
let adjusted_delta = delta.y * self.accessibility.scroll_sensitivity;
|
||||
actions.push(GameAction::MoveEntityDepth { delta: adjusted_delta });
|
||||
}
|
||||
|
||||
InputEvent::Keyboard { key, pressed, modifiers: _ } => {
|
||||
if *pressed {
|
||||
self.process_key(*key, &mut actions);
|
||||
}
|
||||
}
|
||||
|
||||
InputEvent::Stylus { pos, pressure: _, tilt: _, phase, timestamp: _ } => {
|
||||
self.process_stylus(*pos, *phase, &mut actions);
|
||||
}
|
||||
|
||||
InputEvent::Touch { pos, phase, id: _ } => {
|
||||
self.process_touch(*pos, *phase, &mut actions);
|
||||
}
|
||||
}
|
||||
|
||||
actions
|
||||
}
|
||||
|
||||
/// Process mouse input
|
||||
fn process_mouse(&mut self, pos: Vec2, button: MouseButton, phase: TouchPhase, actions: &mut Vec<GameAction>) {
|
||||
match phase {
|
||||
TouchPhase::Started => {
|
||||
// Single click = select
|
||||
actions.push(GameAction::SelectEntity { position: pos });
|
||||
|
||||
// Start drag tracking
|
||||
self.drag_state.active = true;
|
||||
self.drag_state.source = Some(match button {
|
||||
MouseButton::Left => DragSource::MouseLeft,
|
||||
MouseButton::Right => DragSource::MouseRight,
|
||||
MouseButton::Middle => return, // Don't handle middle button
|
||||
});
|
||||
self.drag_state.start_pos = pos;
|
||||
self.drag_state.last_pos = pos;
|
||||
|
||||
actions.push(GameAction::BeginDrag { position: pos });
|
||||
}
|
||||
|
||||
TouchPhase::Moved => {
|
||||
if self.drag_state.active {
|
||||
let delta = (pos - self.drag_state.last_pos) * self.accessibility.mouse_sensitivity;
|
||||
self.drag_state.last_pos = pos;
|
||||
|
||||
// Check if we've exceeded drag threshold
|
||||
let total_delta = pos - self.drag_state.start_pos;
|
||||
if total_delta.length() < self.accessibility.drag_threshold {
|
||||
return; // Too small to count as drag
|
||||
}
|
||||
|
||||
actions.push(GameAction::ContinueDrag { position: pos, delta });
|
||||
|
||||
// Context-specific drag actions
|
||||
match self.current_context {
|
||||
InputContext::EntityManipulation => {
|
||||
match self.drag_state.source {
|
||||
Some(DragSource::MouseLeft) => {
|
||||
actions.push(GameAction::MoveEntity { delta });
|
||||
}
|
||||
Some(DragSource::MouseRight) => {
|
||||
let adjusted_delta = if self.accessibility.invert_y {
|
||||
Vec2::new(delta.x, -delta.y)
|
||||
} else {
|
||||
delta
|
||||
};
|
||||
actions.push(GameAction::RotateEntity { delta: adjusted_delta });
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
InputContext::CameraControl => {
|
||||
actions.push(GameAction::MoveCamera { delta });
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TouchPhase::Ended | TouchPhase::Cancelled => {
|
||||
if self.drag_state.active {
|
||||
actions.push(GameAction::EndDrag { position: pos });
|
||||
self.drag_state.active = false;
|
||||
self.drag_state.source = None;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Process keyboard input
|
||||
fn process_key(&mut self, key: KeyCode, actions: &mut Vec<GameAction>) {
|
||||
match key {
|
||||
KeyCode::KeyR => actions.push(GameAction::ResetEntity),
|
||||
KeyCode::Delete | KeyCode::Backspace => actions.push(GameAction::DeleteEntity),
|
||||
KeyCode::KeyZ if self.accessibility.one_handed_mode => {
|
||||
// In one-handed mode, Z key can trigger actions
|
||||
actions.push(GameAction::Undo);
|
||||
}
|
||||
KeyCode::Escape => actions.push(GameAction::Cancel),
|
||||
KeyCode::Enter => actions.push(GameAction::Confirm),
|
||||
KeyCode::Tab => actions.push(GameAction::ToggleUI),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
/// Process stylus input (Apple Pencil, etc.)
|
||||
fn process_stylus(&mut self, pos: Vec2, phase: TouchPhase, actions: &mut Vec<GameAction>) {
|
||||
match phase {
|
||||
TouchPhase::Started => {
|
||||
actions.push(GameAction::SelectEntity { position: pos });
|
||||
actions.push(GameAction::BeginDrag { position: pos });
|
||||
self.drag_state.active = true;
|
||||
self.drag_state.source = Some(DragSource::Stylus);
|
||||
self.drag_state.start_pos = pos;
|
||||
self.drag_state.last_pos = pos;
|
||||
}
|
||||
|
||||
TouchPhase::Moved => {
|
||||
if self.drag_state.active {
|
||||
let delta = (pos - self.drag_state.last_pos) * self.accessibility.stylus_sensitivity;
|
||||
self.drag_state.last_pos = pos;
|
||||
|
||||
actions.push(GameAction::ContinueDrag { position: pos, delta });
|
||||
actions.push(GameAction::MoveEntity { delta });
|
||||
}
|
||||
}
|
||||
|
||||
TouchPhase::Ended | TouchPhase::Cancelled => {
|
||||
if self.drag_state.active {
|
||||
actions.push(GameAction::EndDrag { position: pos });
|
||||
self.drag_state.active = false;
|
||||
self.drag_state.source = None;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Process touch input
|
||||
fn process_touch(&mut self, pos: Vec2, phase: TouchPhase, actions: &mut Vec<GameAction>) {
|
||||
// For now, treat touch like stylus
|
||||
self.process_stylus(pos, phase, actions);
|
||||
}
|
||||
|
||||
/// Set up default input bindings
|
||||
fn setup_default_bindings(&mut self) {
|
||||
// For now, bindings are hardcoded in process_event
|
||||
// Later, we can make this fully data-driven
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for InputController {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[path = "input_controller_tests.rs"]
|
||||
mod tests;
|
||||
@@ -1,150 +0,0 @@
|
||||
//! Abstract input event types for the engine
|
||||
//!
|
||||
//! These types are platform-agnostic and represent all forms of input
|
||||
//! (stylus, mouse, touch) in a unified way. Platform-specific code
|
||||
//! (iOS pencil bridge, desktop mouse) converts to these types.
|
||||
|
||||
use glam::Vec2;
|
||||
|
||||
/// Phase of a touch/stylus/mouse input
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum TouchPhase {
|
||||
/// Input just started
|
||||
Started,
|
||||
/// Input moved
|
||||
Moved,
|
||||
/// Input ended normally
|
||||
Ended,
|
||||
/// Input was cancelled (e.g., system gesture)
|
||||
Cancelled,
|
||||
}
|
||||
|
||||
/// Mouse button types
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub enum MouseButton {
|
||||
Left,
|
||||
Right,
|
||||
Middle,
|
||||
}
|
||||
|
||||
/// Keyboard key (using winit's KeyCode for now - can abstract later)
|
||||
pub use winit::keyboard::KeyCode;
|
||||
|
||||
/// Keyboard modifiers
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
|
||||
pub struct Modifiers {
|
||||
pub shift: bool,
|
||||
pub ctrl: bool,
|
||||
pub alt: bool,
|
||||
pub meta: bool, // Command on macOS, Windows key on Windows
|
||||
}
|
||||
|
||||
/// Input event buffer for Bevy ECS integration
|
||||
///
|
||||
/// The executor fills this buffer each frame with input events from winit,
|
||||
/// and Bevy systems (like egui) consume these events.
|
||||
#[derive(bevy::prelude::Resource, Default, Clone)]
|
||||
pub struct InputEventBuffer {
|
||||
pub events: Vec<InputEvent>,
|
||||
}
|
||||
|
||||
/// Abstract input event that the engine processes
|
||||
///
|
||||
/// Platform-specific code converts native input (UITouch, winit events)
|
||||
/// into these engine-agnostic events.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum InputEvent {
|
||||
/// Stylus input (Apple Pencil, Surface Pen, etc.)
|
||||
Stylus {
|
||||
/// Screen position in pixels
|
||||
pos: Vec2,
|
||||
/// Pressure (0.0 = no pressure, 1.0+ = max pressure)
|
||||
/// Note: Apple Pencil reports 0.0-4.0 range
|
||||
pressure: f32,
|
||||
/// Tilt vector:
|
||||
/// - x: altitude angle (0 = flat on screen, π/2 = perpendicular)
|
||||
/// - y: azimuth angle (rotation around vertical axis)
|
||||
tilt: Vec2,
|
||||
/// Touch phase
|
||||
phase: TouchPhase,
|
||||
/// Platform timestamp (for input prediction)
|
||||
timestamp: f64,
|
||||
},
|
||||
|
||||
/// Mouse input (desktop)
|
||||
Mouse {
|
||||
/// Screen position in pixels
|
||||
pos: Vec2,
|
||||
/// Which button
|
||||
button: MouseButton,
|
||||
/// Touch phase
|
||||
phase: TouchPhase,
|
||||
},
|
||||
|
||||
/// Mouse cursor movement (no button pressed)
|
||||
/// This is separate from Mouse to distinguish hover from drag
|
||||
MouseMove {
|
||||
/// Screen position in pixels
|
||||
pos: Vec2,
|
||||
},
|
||||
|
||||
/// Touch input (fingers on touchscreen)
|
||||
Touch {
|
||||
/// Screen position in pixels
|
||||
pos: Vec2,
|
||||
/// Touch phase
|
||||
phase: TouchPhase,
|
||||
/// Touch ID (for multi-touch tracking)
|
||||
id: u64,
|
||||
},
|
||||
|
||||
/// Keyboard input
|
||||
Keyboard {
|
||||
/// Physical key code
|
||||
key: KeyCode,
|
||||
/// Whether the key was pressed or released
|
||||
pressed: bool,
|
||||
/// Modifier keys held during the event
|
||||
modifiers: Modifiers,
|
||||
},
|
||||
|
||||
/// Mouse wheel scroll
|
||||
MouseWheel {
|
||||
/// Scroll delta (pixels or lines depending on device)
|
||||
delta: Vec2,
|
||||
/// Current mouse position
|
||||
pos: Vec2,
|
||||
},
|
||||
}
|
||||
|
||||
impl InputEvent {
|
||||
/// Get the position for positional input types
|
||||
pub fn position(&self) -> Option<Vec2> {
|
||||
match self {
|
||||
InputEvent::Stylus { pos, .. } => Some(*pos),
|
||||
InputEvent::Mouse { pos, .. } => Some(*pos),
|
||||
InputEvent::MouseMove { pos } => Some(*pos),
|
||||
InputEvent::Touch { pos, .. } => Some(*pos),
|
||||
InputEvent::MouseWheel { pos, .. } => Some(*pos),
|
||||
InputEvent::Keyboard { .. } => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the phase for input types that have phases
|
||||
pub fn phase(&self) -> Option<TouchPhase> {
|
||||
match self {
|
||||
InputEvent::Stylus { phase, .. } => Some(*phase),
|
||||
InputEvent::Mouse { phase, .. } => Some(*phase),
|
||||
InputEvent::Touch { phase, .. } => Some(*phase),
|
||||
InputEvent::Keyboard { .. } | InputEvent::MouseWheel { .. } | InputEvent::MouseMove { .. } => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Check if this is an active input (not ended/cancelled)
|
||||
pub fn is_active(&self) -> bool {
|
||||
match self.phase() {
|
||||
Some(phase) => !matches!(phase, TouchPhase::Ended | TouchPhase::Cancelled),
|
||||
None => true, // Keyboard and wheel events are considered instantaneous
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,18 @@
|
||||
//! Core Engine module - networking and persistence outside Bevy
|
||||
//! Core Engine module - application logic and coordination
|
||||
//!
|
||||
//! This module handles the core application logic that sits between the
|
||||
//! platform layer and the game systems:
|
||||
//! - **bridge**: Communication bridge between async EngineCore and Bevy ECS
|
||||
//! - **core**: Async EngineCore running on tokio (CRDT sync, networking, persistence)
|
||||
//! - **commands**: Commands that can be sent to EngineCore
|
||||
//! - **events**: Events emitted by EngineCore
|
||||
//! - **game_actions**: High-level game actions (SelectEntity, MoveEntity, etc.)
|
||||
|
||||
mod bridge;
|
||||
mod commands;
|
||||
mod core;
|
||||
mod events;
|
||||
mod game_actions;
|
||||
mod input_controller;
|
||||
mod input_events;
|
||||
mod networking;
|
||||
mod persistence;
|
||||
|
||||
@@ -15,7 +21,5 @@ pub use commands::EngineCommand;
|
||||
pub use core::EngineCore;
|
||||
pub use events::EngineEvent;
|
||||
pub use game_actions::GameAction;
|
||||
pub use input_controller::{AccessibilitySettings, InputContext, InputController};
|
||||
pub use input_events::{InputEvent, InputEventBuffer, KeyCode, Modifiers, MouseButton, TouchPhase};
|
||||
pub use networking::NetworkingManager;
|
||||
pub use persistence::PersistenceManager;
|
||||
|
||||
Reference in New Issue
Block a user