using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Input; namespace Theriapolis.Game.Input; /// /// Single-frame input snapshot with helper methods. /// Call Update() once per frame before any input queries. /// public sealed class InputManager { private KeyboardState _prevKeys; private KeyboardState _currKeys; private MouseState _prevMouse; private MouseState _currMouse; public void Update() { _prevKeys = _currKeys; _currKeys = Keyboard.GetState(); _prevMouse = _currMouse; _currMouse = Mouse.GetState(); } // ── Keyboard ────────────────────────────────────────────────────────────── public bool IsDown(Keys key) => _currKeys.IsKeyDown(key); public bool JustPressed(Keys key) => _currKeys.IsKeyDown(key) && _prevKeys.IsKeyUp(key); public bool JustReleased(Keys key) => _currKeys.IsKeyUp(key) && _prevKeys.IsKeyDown(key); // ── Mouse ───────────────────────────────────────────────────────────────── public Vector2 MousePosition => new(_currMouse.X, _currMouse.Y); public bool LeftDown => _currMouse.LeftButton == ButtonState.Pressed; public bool LeftJustDown => _currMouse.LeftButton == ButtonState.Pressed && _prevMouse.LeftButton == ButtonState.Released; public bool LeftJustUp => _currMouse.LeftButton == ButtonState.Released && _prevMouse.LeftButton == ButtonState.Pressed; public bool RightDown => _currMouse.RightButton == ButtonState.Pressed; public bool RightJustDown => _currMouse.RightButton == ButtonState.Pressed && _prevMouse.RightButton == ButtonState.Released; /// Mouse wheel delta in scroll "ticks" (positive = forward/up). public int ScrollDelta => _currMouse.ScrollWheelValue - _prevMouse.ScrollWheelValue; private Vector2 _dragStart; private bool _dragging; private bool _dragActivated; private const float DragActivationPixels = 4f; public bool IsDragging => _dragging; /// /// Returns the world-space pan delta from mouse dragging. Panning is /// suppressed until the mouse moves more than /// from the press position, so hand-jitter during a click doesn't pan the /// camera (at low zoom, one screen pixel can be many world pixels). /// public Vector2 ConsumeDragDelta(Rendering.Camera2D camera) { if (LeftJustDown) { _dragStart = MousePosition; _dragging = true; _dragActivated = false; } if (LeftJustUp) { _dragging = false; _dragActivated = false; } if (!_dragging || !LeftDown) return Vector2.Zero; if (!_dragActivated) { if (Vector2.Distance(MousePosition, _dragStart) < DragActivationPixels) return Vector2.Zero; _dragActivated = true; _dragStart = MousePosition; // start panning from here, not from press } Vector2 delta = MousePosition - _dragStart; _dragStart = MousePosition; return -delta / camera.Zoom; } }