using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;
namespace MrGameEng.Input;
/// Mouse buttons addressable through .
public enum MouseButton
{
/// Left button.
Left,
/// Right button.
Right,
/// Middle button (wheel click).
Middle,
}
///
/// Polls keyboard, mouse and gamepad once per frame and keeps the previous frame's state,
/// enabling edge queries (Pressed = went down this frame, Released = went up).
/// Registered as a service by scene.UseInput(); polled by
/// at the start of the update phase. While
/// is set (e.g. the developer console is open), game-facing input reads as released.
///
public sealed class InputManager
{
private readonly MrGameEng.Core.InputCapture? _capture;
private KeyboardState _keyboard;
private KeyboardState _previousKeyboard;
private MouseState _mouse;
private MouseState _previousMouse;
private GamePadState _gamePad;
private GamePadState _previousGamePad;
///
/// Creates a manager. With a , input is suppressed while a UI
/// overlay holds it (keys/buttons read as released, mouse position and wheel freeze).
///
public InputManager(MrGameEng.Core.InputCapture? capture = null) => _capture = capture;
/// Polls all devices. Called once per frame by .
public void Update()
{
if (_capture?.Captured == true)
{
// Оверлей (консоль) захватил ввод: клавиши и кнопки считаются отпущенными,
// позиция мыши и счётчик колеса замораживаются — все дельты нулевые.
Apply(
default,
new MouseState(
_mouse.X,
_mouse.Y,
_mouse.ScrollWheelValue,
ButtonState.Released,
ButtonState.Released,
ButtonState.Released,
ButtonState.Released,
ButtonState.Released
),
default
);
return;
}
Apply(Keyboard.GetState(), Mouse.GetState(), GamePad.GetState(PlayerIndex.One));
}
internal void Apply(KeyboardState keyboard, MouseState mouse, GamePadState gamePad)
{
_previousKeyboard = _keyboard;
_previousMouse = _mouse;
_previousGamePad = _gamePad;
_keyboard = keyboard;
_mouse = mouse;
_gamePad = gamePad;
}
/// True while the key is held down.
public bool IsKeyDown(Keys key) => _keyboard.IsKeyDown(key);
/// True only on the frame the key went down.
public bool IsKeyPressed(Keys key) =>
_keyboard.IsKeyDown(key) && _previousKeyboard.IsKeyUp(key);
/// True only on the frame the key went up.
public bool IsKeyReleased(Keys key) =>
_keyboard.IsKeyUp(key) && _previousKeyboard.IsKeyDown(key);
/// Mouse cursor position in window pixels.
public Point MousePosition => _mouse.Position;
/// Cursor movement since the previous frame.
public Point MouseDelta => _mouse.Position - _previousMouse.Position;
/// Scroll wheel change since the previous frame (positive = up).
public int WheelDelta => _mouse.ScrollWheelValue - _previousMouse.ScrollWheelValue;
/// True while the mouse button is held down.
public bool IsMouseDown(MouseButton button) => GetButton(_mouse, button) == ButtonState.Pressed;
/// True only on the frame the mouse button went down.
public bool IsMousePressed(MouseButton button) =>
GetButton(_mouse, button) == ButtonState.Pressed
&& GetButton(_previousMouse, button) == ButtonState.Released;
/// True only on the frame the mouse button went up.
public bool IsMouseReleased(MouseButton button) =>
GetButton(_mouse, button) == ButtonState.Released
&& GetButton(_previousMouse, button) == ButtonState.Pressed;
/// True while the gamepad button is held down.
public bool IsButtonDown(Buttons button) => _gamePad.IsButtonDown(button);
/// True only on the frame the gamepad button went down.
public bool IsButtonPressed(Buttons button) =>
_gamePad.IsButtonDown(button) && _previousGamePad.IsButtonUp(button);
/// True only on the frame the gamepad button went up.
public bool IsButtonReleased(Buttons button) =>
_gamePad.IsButtonUp(button) && _previousGamePad.IsButtonDown(button);
/// Left thumbstick, x/y in [-1, 1]. Y is inverted to match the engine's y-down world.
public Vector2 LeftStick => new(_gamePad.ThumbSticks.Left.X, -_gamePad.ThumbSticks.Left.Y);
private static ButtonState GetButton(in MouseState state, MouseButton button) =>
button switch
{
MouseButton.Left => state.LeftButton,
MouseButton.Right => state.RightButton,
MouseButton.Middle => state.MiddleButton,
_ => ButtonState.Released,
};
}