You're breathtaking!

This commit is contained in:
Wojtek Figat
2020-12-07 23:40:54 +01:00
commit 6fb9eee74c
5143 changed files with 1153594 additions and 0 deletions

356
Source/Engine/Input/Enums.h Normal file
View File

@@ -0,0 +1,356 @@
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
#pragma once
#include "Engine/Core/Config.h"
// The maximum amount of supported gamepads connected at once.
#define MAX_GAMEPADS 8
/// <summary>
/// Hardware mouse cursor behaviour.
/// </summary>
API_ENUM() enum class CursorLockMode
{
/// <summary>
/// The default mode.
/// </summary>
None = 0,
/// <summary>
/// Cursor position is locked to the center of the game window.
/// </summary>
Locked = 1,
};
/// <summary>
/// Mouse buttons types.
/// </summary>
API_ENUM() enum class MouseButton
{
/// <summary>
/// No button.
/// </summary>
None = 0,
/// <summary>
/// Left button.
/// </summary>
Left = 1,
/// <summary>
/// Middle button.
/// </summary>
Middle = 2,
/// <summary>
/// Right button.
/// </summary>
Right = 3,
/// <summary>
/// Extended button 1 (or XButton1).
/// </summary>
Extended1 = 4,
/// <summary>
/// Extended button 2 (or XButton2).
/// </summary>
Extended2 = 5,
MAX
};
/// <summary>
/// Axis for gamepad.
/// </summary>
API_ENUM() enum class GamepadAxis
{
/// <summary>
/// No axis.
/// </summary>
None = 0,
/// <summary>
/// The X-Axis of the left thumb stick.
/// </summary>
LeftStickX = 1,
/// <summary>
/// The Y-Axis of the left thumb stick.
/// </summary>
LeftStickY = 2,
/// <summary>
/// The X-Axis of the right thumb stick.
/// </summary>
RightStickX = 3,
/// <summary>
/// The Y-Axis of the right thumb stick.
/// </summary>
RightStickY = 4,
/// <summary>
/// The left trigger.
/// </summary>
LeftTrigger = 5,
/// <summary>
/// The right trigger.
/// </summary>
RightTrigger = 6,
MAX
};
/// <summary>
/// Buttons for gamepad.
/// </summary>
API_ENUM() enum class GamepadButton
{
/// <summary>
/// No buttons.
/// </summary>
None = 0,
/// <summary>
/// PadUp button (DPad / Directional Pad).
/// </summary>
DPadUp = 1,
/// <summary>
/// PadDown button (DPad / Directional Pad).
/// </summary>
DPadDown = 2,
/// <summary>
/// PadLeft button (DPad / Directional Pad).
/// </summary>
DPadLeft = 3,
/// <summary>
/// PadRight button (DPad / Directional Pad).
/// </summary>
DPadRight = 4,
/// <summary>
/// Start button.
/// </summary>
Start = 5,
/// <summary>
/// Back button.
/// </summary>
Back = 6,
/// <summary>
/// Left thumbstick button.
/// </summary>
LeftThumb = 7,
/// <summary>
/// Right thumbstick button.
/// </summary>
RightThumb = 8,
/// <summary>
/// Left shoulder button.
/// </summary>
LeftShoulder = 9,
/// <summary>
/// Right shoulder button.
/// </summary>
RightShoulder = 10,
/// <summary>
/// Left trigger button.
/// </summary>
LeftTrigger = 11,
/// <summary>
/// Right trigger button.
/// </summary>
RightTrigger = 12,
/// <summary>
/// A (face button down).
/// </summary>
A = 13,
/// <summary>
/// B (face button right).
/// </summary>
B = 14,
/// <summary>
/// X (face button left).
/// </summary>
X = 15,
/// <summary>
/// Y (face button up).
/// </summary>
Y = 16,
/// <summary>
/// The left stick up.
/// </summary>
LeftStickUp = 17,
/// <summary>
/// The left stick down.
/// </summary>
LeftStickDown = 18,
/// <summary>
/// The left stick left.
/// </summary>
LeftStickLeft = 19,
/// <summary>
/// The left stick right.
/// </summary>
LeftStickRight = 20,
/// <summary>
/// The right stick up.
/// </summary>
RightStickUp = 21,
/// <summary>
/// The right stick down.
/// </summary>
RightStickDown = 22,
/// <summary>
/// The right stick left.
/// </summary>
RightStickLeft = 23,
/// <summary>
/// The right stick right.
/// </summary>
RightStickRight = 24,
MAX
};
/// <summary>
/// The input action event trigger modes.
/// </summary>
API_ENUM() enum class InputActionMode
{
/// <summary>
/// User is pressing the key/button.
/// </summary>
Pressing = 0,
/// <summary>
/// User pressed the key/button (but wasn't pressing it in the previous frame).
/// </summary>
Press = 1,
/// <summary>
/// User released the key/button (was pressing it in the previous frame).
/// </summary>
Release = 2,
};
/// <summary>
/// The input gamepad index.
/// </summary>
API_ENUM() enum class InputGamepadIndex
{
/// <summary>
/// All detected gamepads.
/// </summary>
All = -1,
/// <summary>
/// The gamepad no. 0.
/// </summary>
Gamepad0 = 0,
/// <summary>
/// The gamepad no. 1.
/// </summary>
Gamepad1 = 1,
/// <summary>
/// The gamepad no. 2.
/// </summary>
Gamepad2 = 2,
/// <summary>
/// The gamepad no. 3.
/// </summary>
Gamepad3 = 3,
/// <summary>
/// The gamepad no. 4.
/// </summary>
Gamepad4 = 4,
/// <summary>
/// The gamepad no. 5.
/// </summary>
Gamepad5 = 5,
};
/// <summary>
/// The input axes types.
/// </summary>
API_ENUM() enum class InputAxisType
{
/// <summary>
/// The mouse X-Axis (mouse delta position scaled by the sensitivity).
/// </summary>
MouseX = 0,
/// <summary>
/// The mouse Y-Axis (mouse delta position scaled by the sensitivity).
/// </summary>
MouseY = 1,
/// <summary>
/// The mouse wheel (mouse wheel delta scaled by the sensitivity).
/// </summary>
MouseWheel = 2,
/// <summary>
/// The gamepad X-Axis of the left thumb stick.
/// </summary>
GamepadLeftStickX = 3,
/// <summary>
/// The gamepad Y-Axis of the left thumb stick.
/// </summary>
GamepadLeftStickY = 4,
/// <summary>
/// The gamepad X-Axis of the right thumb stick.
/// </summary>
GamepadRightStickX = 5,
/// <summary>
/// The gamepad Y-Axis of the right thumb stick.
/// </summary>
GamepadRightStickY = 6,
/// <summary>
/// The gamepad left trigger.
/// </summary>
GamepadLeftTrigger = 7,
/// <summary>
/// The gamepad right trigger.
/// </summary>
GamepadRightTrigger = 8,
/// <summary>
/// The keyboard only mode. For key inputs.
/// </summary>
KeyboardOnly = 9,
};

View File

@@ -0,0 +1,59 @@
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
#include "Gamepad.h"
void GamepadLayout::Init()
{
for (int32 i = 0; i < (int32)GamepadButton::MAX; i++)
Buttons[i] = (GamepadButton)i;
for (int32 i = 0; i < (int32)GamepadAxis::MAX; i++)
Axis[i] = (GamepadAxis)i;
for (int32 i = 0; i < (int32)GamepadAxis::MAX; i++)
AxisMap[i] = Vector2::UnitX;
}
Gamepad::Gamepad(const Guid& productId, const String& name)
: InputDevice(SpawnParams(Guid::New(), TypeInitializer), name)
, _productId(productId)
{
_state.Clear();
_mappedState.Clear();
_mappedPrevState.Clear();
Layout.Init();
}
void Gamepad::ResetState()
{
InputDevice::ResetState();
_state.Clear();
_mappedState.Clear();
_mappedPrevState.Clear();
}
bool Gamepad::Update(EventQueue& queue)
{
// Copy state
Platform::MemoryCopy(&_mappedPrevState, &_mappedState, sizeof(State));
_mappedState.Clear();
// Gather current state
if (UpdateState())
return true;
// Map state
for (int32 i = 0; i < (int32)GamepadButton::MAX; i++)
{
auto e = Layout.Buttons[i];
_mappedState.Buttons[static_cast<int32>(e)] = _state.Buttons[i];
}
for (int32 i = 0; i < (int32)GamepadAxis::MAX; i++)
{
auto e = Layout.Axis[i];
float value = _state.Axis[i];
value = Layout.AxisMap[i].X * value + Layout.AxisMap[i].Y;
_mappedState.Axis[static_cast<int32>(e)] = value;
}
return false;
}

View File

@@ -0,0 +1,211 @@
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
#pragma once
#include "InputDevice.h"
#include "Engine/Core/Types/Guid.h"
#include "Engine/Core/Math/Vector2.h"
/// <summary>
/// General identifiers for potential force feedback channels. These will be mapped according to the platform specific implementation.
/// </summary>
API_STRUCT() struct FLAXENGINE_API GamepadVibrationState
{
DECLARE_SCRIPTING_TYPE_MINIMAL(GamepadVibrationState);
/// <summary>
/// The left large motor vibration.
/// </summary>
API_FIELD() float LeftLarge;
/// <summary>
/// The left small motor vibration.
/// </summary>
API_FIELD() float LeftSmall;
/// <summary>
/// The right large motor vibration.
/// </summary>
API_FIELD() float RightLarge;
/// <summary>
/// The right small motor vibration.
/// </summary>
API_FIELD() float RightSmall;
GamepadVibrationState()
: LeftLarge(0.0f)
, LeftSmall(0.0f)
, RightLarge(0.0f)
, RightSmall(0.0f)
{
}
};
/// <summary>
/// Gamepad buttons and axis mapping description.
/// Allows converting input from the different gamepads into a universal format (see <see cref="Gamepad::State::ButtonTypes"/> and <see cref="Gamepad::State::AxisTypes"/>).
/// </summary>
struct FLAXENGINE_API GamepadLayout
{
/// <summary>
/// The buttons mapping. Index by gamepad button id from 0 to 31 (see <see cref="Gamepad::State::ButtonTypes"/>).
/// </summary>
GamepadButton Buttons[(int32)GamepadButton::MAX];
/// <summary>
/// The axis mapping. Index by gamepad axis id from 0 to 5 (see <see cref="Gamepad::State::AxisTypes"/>).
/// </summary>
GamepadAxis Axis[(int32)GamepadAxis::MAX];
/// <summary>
/// The axis ranges mapping (X is scale, Y is offset. Eg. mappedVal = X * value + Y). It allows to invert any axis or map axis range.
/// </summary>
Vector2 AxisMap[(int32)GamepadAxis::MAX];
/// <summary>
/// Initializes layout with default values.
/// </summary>
void Init();
};
/// <summary>
/// Represents a single hardware gamepad device. Used by the Input to report raw gamepad input events.
/// </summary>
API_CLASS(NoSpawn, Sealed) class FLAXENGINE_API Gamepad : public InputDevice
{
DECLARE_SCRIPTING_TYPE_NO_SPAWN(Gamepad);
public:
/// <summary>
/// The universal gamepad state description. All hardware gamepad device handlers should map input to match this structure.
/// Later on, each gamepad may use individual layout for a game.
/// </summary>
struct State
{
/// <summary>
/// The buttons state (pressed if true).
/// </summary>
bool Buttons[(int32)GamepadButton::MAX];
/// <summary>
/// The axis state (normalized value).
/// </summary>
float Axis[(int32)GamepadAxis::MAX];
/// <summary>
/// Clears the state.
/// </summary>
void Clear()
{
Platform::MemoryClear(this, sizeof(State));
}
};
protected:
Guid _productId;
State _state;
State _mappedState;
State _mappedPrevState;
explicit Gamepad(const Guid& productId, const String& name);
public:
/// <summary>
/// The gamepad layout.
/// </summary>
GamepadLayout Layout;
public:
/// <summary>
/// Gets the gamepad device type identifier.
/// </summary>
/// <returns>The id.</returns>
API_PROPERTY() FORCE_INLINE const Guid& GetProductID() const
{
return _productId;
}
/// <summary>
/// Gets the current gamepad state.
/// </summary>
/// <param name="result">The result.</param>
FORCE_INLINE void GetState(State& result) const
{
result = _state;
}
/// <summary>
/// Gets the gamepad axis value.
/// </summary>
/// <param name="axis">Gamepad axis to check</param>
/// <returns>Axis value.</returns>
API_FUNCTION() FORCE_INLINE float GetAxis(const GamepadAxis axis) const
{
return _mappedState.Axis[static_cast<int32>(axis)];
}
/// <summary>
/// Gets the gamepad button state (true if being pressed during the current frame).
/// </summary>
/// <param name="button">Gamepad button to check</param>
/// <returns>True if user holds down the button, otherwise false.</returns>
API_FUNCTION() FORCE_INLINE bool GetButton(const GamepadButton button) const
{
return _mappedState.Buttons[static_cast<int32>(button)];
}
/// <summary>
/// Gets the gamepad button down state (true if was pressed during the current frame).
/// </summary>
/// <param name="button">Gamepad button to check</param>
/// <returns>True if user starts pressing down the button, otherwise false.</returns>
API_FUNCTION() FORCE_INLINE bool GetButtonDown(const GamepadButton button) const
{
return _mappedState.Buttons[static_cast<int32>(button)] && !_mappedPrevState.Buttons[static_cast<int32>(button)];
}
/// <summary>
/// Gets the gamepad button up state (true if was released during the current frame).
/// </summary>
/// <param name="button">Gamepad button to check</param>
/// <returns>True if user releases the button, otherwise false.</returns>
API_FUNCTION() FORCE_INLINE bool GetButtonUp(const GamepadButton button) const
{
return !_mappedState.Buttons[static_cast<int32>(button)] && _mappedPrevState.Buttons[static_cast<int32>(button)];
}
public:
/// <summary>
/// Sets the state of the gamepad vibration. Ignored if controller does not support this.
/// </summary>
/// <param name="state">The state.</param>
API_FUNCTION() virtual void SetVibration(const GamepadVibrationState& state)
{
}
/// <summary>
/// Sets the color of the gamepad light. Ignored if controller does not support this.
/// </summary>
/// <param name="color">The color.</param>
API_FUNCTION() virtual void SetColor(const Color& color)
{
}
/// <summary>
/// Resets the color of the gamepad light to the default. Ignored if controller does not support this.
/// </summary>
API_FUNCTION() virtual void ResetColor()
{
}
public:
// [InputDevice]
void ResetState() override;
bool Update(EventQueue& queue) final override;
};

View File

@@ -0,0 +1,10 @@
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
using Flax.Build;
/// <summary>
/// Input module.
/// </summary>
public class Input : EngineModule
{
}

View File

@@ -0,0 +1,693 @@
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
#include "Input.h"
#include "InputSettings.h"
#include "Keyboard.h"
#include "Mouse.h"
#include "Gamepad.h"
#include "FlaxEngine.Gen.h"
#include "Engine/Platform/Window.h"
#include "Engine/Engine/Engine.h"
#include "Engine/Engine/EngineService.h"
#include "Engine/Engine/Screen.h"
#include "Engine/Engine/Time.h"
#include "Engine/Platform/WindowsManager.h"
#include "Engine/Scripting/ScriptingType.h"
#include "Engine/Scripting/BinaryModule.h"
#include "Engine/Profiler/ProfilerCPU.h"
struct AxisEvaluation
{
float RawValue;
float Value;
float PrevKeyValue;
bool Used;
};
struct ActionData
{
bool Active;
uint64 FrameIndex;
ActionData()
{
Active = false;
FrameIndex = 0;
}
};
struct AxisData
{
float Value;
float ValueRaw;
float PrevKeyValue;
uint64 FrameIndex;
AxisData()
{
Value = 0.0f;
ValueRaw = 0.0f;
PrevKeyValue = 0.0f;
FrameIndex = 0;
}
};
namespace InputImpl
{
Dictionary<StringView, ActionData> Actions;
Dictionary<StringView, AxisData> Axes;
bool GamepadsChanged = true;
Array<AxisEvaluation> AxesValues;
InputDevice::EventQueue InputEvents;
}
using namespace InputImpl;
class InputService : public EngineService
{
public:
InputService()
: EngineService(TEXT("Input"), -60)
{
}
void Update() override;
};
InputService InputServiceInstance;
Mouse* Input::Mouse = nullptr;
Keyboard* Input::Keyboard = nullptr;
Array<Gamepad*, FixedAllocation<MAX_GAMEPADS>> Input::Gamepads;
Action Input::GamepadsChanged;
Array<InputDevice*, InlinedAllocation<16>> Input::CustomDevices;
Input::CharDelegate Input::CharInput;
Input::KeyboardDelegate Input::KeyDown;
Input::KeyboardDelegate Input::KeyUp;
Input::MouseButtonDelegate Input::MouseDown;
Input::MouseButtonDelegate Input::MouseUp;
Input::MouseButtonDelegate Input::MouseDoubleClick;
Input::MouseWheelDelegate Input::MouseWheel;
Input::MouseDelegate Input::MouseMove;
Action Input::MouseLeave;
Input::TouchDelegate Input::TouchDown;
Input::TouchDelegate Input::TouchMove;
Input::TouchDelegate Input::TouchUp;
Delegate<StringView> Input::ActionTriggered;
Array<ActionConfig> Input::ActionMappings;
Array<AxisConfig> Input::AxisMappings;
int32 Input::GetGamepadsCount()
{
return Gamepads.Count();
}
Gamepad* Input::GetGamepad(int32 index)
{
if (index >= 0 && index < Gamepads.Count())
return Gamepads[index];
return nullptr;
}
void Input::OnGamepadsChanged()
{
::GamepadsChanged = true;
}
StringView Input::GetInputText()
{
return Keyboard ? Keyboard->GetInputText() : StringView::Empty;
}
bool Input::GetKey(const KeyboardKeys key)
{
return Keyboard ? Keyboard->GetKey(key) : false;
}
bool Input::GetKeyDown(const KeyboardKeys key)
{
return Keyboard ? Keyboard->GetKeyDown(key) : false;
}
bool Input::GetKeyUp(const KeyboardKeys key)
{
return Keyboard ? Keyboard->GetKeyUp(key) : false;
}
Vector2 Input::GetMousePosition()
{
return Mouse ? Engine::ScreenToGameViewport(Mouse->GetPosition()) : Vector2::Minimum;
}
void Input::SetMousePosition(const Vector2& position)
{
if (Mouse && Engine::HasGameViewportFocus())
{
const auto pos = Engine::GameViewportToScreen(position);
if (pos > Vector2::Minimum)
Mouse->SetMousePosition(pos);
}
}
Vector2 Input::GetMouseScreenPosition()
{
return Mouse ? Mouse->GetPosition() : Vector2::Minimum;
}
void Input::SetMouseScreenPosition(const Vector2& position)
{
if (Mouse && Engine::HasFocus)
{
Mouse->SetMousePosition(position);
}
}
Vector2 Input::GetMousePositionDelta()
{
return Mouse ? Mouse->GetPositionDelta() : Vector2::Zero;
}
float Input::GetMouseScrollDelta()
{
return Mouse ? Mouse->GetScrollDelta() : 0.0f;
}
bool Input::GetMouseButton(const MouseButton button)
{
return Mouse ? Mouse->GetButton(button) : false;
}
bool Input::GetMouseButtonDown(const MouseButton button)
{
return Mouse ? Mouse->GetButtonDown(button) : false;
}
bool Input::GetMouseButtonUp(const MouseButton button)
{
return Mouse ? Mouse->GetButtonUp(button) : false;
}
float Input::GetGamepadAxis(int32 gamepadIndex, GamepadAxis axis)
{
if (gamepadIndex >= 0 && gamepadIndex < Gamepads.Count())
return Gamepads[gamepadIndex]->GetAxis(axis);
return 0.0f;
}
bool Input::GetGamepadButton(int32 gamepadIndex, GamepadButton button)
{
if (gamepadIndex >= 0 && gamepadIndex < Gamepads.Count())
return Gamepads[gamepadIndex]->GetButton(button);
return false;
}
bool Input::GetGamepadButtonDown(int32 gamepadIndex, GamepadButton button)
{
if (gamepadIndex >= 0 && gamepadIndex < Gamepads.Count())
return Gamepads[gamepadIndex]->GetButtonDown(button);
return false;
}
bool Input::GetGamepadButtonUp(int32 gamepadIndex, GamepadButton button)
{
if (gamepadIndex >= 0 && gamepadIndex < Gamepads.Count())
return Gamepads[gamepadIndex]->GetButtonUp(button);
return false;
}
float Input::GetGamepadAxis(InputGamepadIndex gamepad, GamepadAxis axis)
{
if (gamepad == InputGamepadIndex::All)
{
for (auto g : Gamepads)
{
if (g->GetAxis(axis))
return true;
}
}
else
{
const auto index = static_cast<int32>(gamepad);
if (index < Gamepads.Count())
return Gamepads[index]->GetAxis(axis);
}
return false;
}
bool Input::GetGamepadButton(InputGamepadIndex gamepad, GamepadButton button)
{
if (gamepad == InputGamepadIndex::All)
{
for (auto g : Gamepads)
{
if (g->GetButton(button))
return true;
}
}
else
{
const auto index = static_cast<int32>(gamepad);
if (index < Gamepads.Count())
return Gamepads[index]->GetButton(button);
}
return false;
}
bool Input::GetGamepadButtonDown(InputGamepadIndex gamepad, GamepadButton button)
{
if (gamepad == InputGamepadIndex::All)
{
for (auto g : Gamepads)
{
if (g->GetButtonDown(button))
return true;
}
}
else
{
const auto index = static_cast<int32>(gamepad);
if (index < Gamepads.Count())
return Gamepads[index]->GetButtonDown(button);
}
return false;
}
bool Input::GetGamepadButtonUp(InputGamepadIndex gamepad, GamepadButton button)
{
if (gamepad == InputGamepadIndex::All)
{
for (auto g : Gamepads)
{
if (g->GetButtonUp(button))
return true;
}
}
else
{
const auto index = static_cast<int32>(gamepad);
if (index < Gamepads.Count())
return Gamepads[index]->GetButtonUp(button);
}
return false;
}
bool Input::GetAction(const StringView& name)
{
const auto e = Actions.TryGet(name);
return e ? e->Active : false;
}
float Input::GetAxis(const StringView& name)
{
const auto e = Axes.TryGet(name);
return e ? e->Value : false;
}
float Input::GetAxisRaw(const StringView& name)
{
const auto e = Axes.TryGet(name);
return e ? e->ValueRaw : false;
}
void InputService::Update()
{
PROFILE_CPU();
const auto frame = Time::Update.TicksCount;
const auto dt = Time::Update.UnscaledDeltaTime.GetTotalSeconds();
InputEvents.Clear();
// If application has no user focus then simply clear the state
if (!Engine::HasFocus)
{
if (Input::Mouse)
Input::Mouse->ResetState();
if (Input::Keyboard)
Input::Keyboard->ResetState();
for (int32 i = 0; i < Input::Gamepads.Count(); i++)
Input::Gamepads[i]->ResetState();
Axes.Clear();
Actions.Clear();
return;
}
// Update input devices state
if (Input::Mouse)
{
if (Input::Mouse->Update(InputEvents))
{
Input::Mouse->DeleteObject();
Input::Mouse = nullptr;
}
}
if (Input::Keyboard)
{
if (Input::Keyboard->Update(InputEvents))
{
Input::Keyboard->DeleteObject();
Input::Keyboard = nullptr;
}
}
for (int32 i = 0; i < Input::Gamepads.Count(); i++)
{
if (Input::Gamepads[i]->Update(InputEvents))
{
Input::Gamepads[i]->DeleteObject();
Input::Gamepads.RemoveAtKeepOrder(i);
Input::OnGamepadsChanged();
i--;
if (Input::Gamepads.IsEmpty())
break;
}
}
for (int32 i = 0; i < Input::CustomDevices.Count(); i++)
{
if (Input::CustomDevices[i]->Update(InputEvents))
{
Input::CustomDevices[i]->DeleteObject();
Input::CustomDevices.RemoveAtKeepOrder(i);
i--;
if (Input::CustomDevices.IsEmpty())
break;
}
}
// Send gamepads change events
if (GamepadsChanged)
{
GamepadsChanged = false;
Input::GamepadsChanged();
}
// Pick the first focused window for input events
WindowsManager::WindowsLocker.Lock();
Window* defaultWindow = nullptr;
for (auto window : WindowsManager::Windows)
{
if (window->IsFocused() && window->GetSettings().AllowInput)
{
defaultWindow = window;
break;
}
}
WindowsManager::WindowsLocker.Unlock();
// Send input events for the focused window
WindowsManager::WindowsLocker.Lock();
for (const auto& e : InputEvents)
{
auto window = e.Target ? e.Target : defaultWindow;
if (!window)
continue;
switch (e.Type)
{
// Keyboard events
case InputDevice::EventType::Char:
window->OnCharInput(e.CharData.Char);
break;
case InputDevice::EventType::KeyDown:
window->OnKeyDown(e.KeyData.Key);
break;
case InputDevice::EventType::KeyUp:
window->OnKeyUp(e.KeyData.Key);
break;
// Mouse events
case InputDevice::EventType::MouseDown:
window->OnMouseDown(window->ScreenToClient(e.MouseData.Position), e.MouseData.Button);
break;
case InputDevice::EventType::MouseUp:
window->OnMouseUp(window->ScreenToClient(e.MouseData.Position), e.MouseData.Button);
break;
case InputDevice::EventType::MouseDoubleClick:
window->OnMouseDoubleClick(window->ScreenToClient(e.MouseData.Position), e.MouseData.Button);
break;
case InputDevice::EventType::MouseWheel:
window->OnMouseWheel(window->ScreenToClient(e.MouseWheelData.Position), e.MouseWheelData.WheelDelta);
break;
case InputDevice::EventType::MouseMove:
window->OnMouseMove(window->ScreenToClient(e.MouseData.Position));
break;
case InputDevice::EventType::MouseLeave:
window->OnMouseLeave();
break;
// Touch events
case InputDevice::EventType::TouchDown:
window->OnTouchDown(window->ScreenToClient(e.TouchData.Position), e.TouchData.PointerId);
break;
case InputDevice::EventType::TouchMove:
window->OnTouchMove(window->ScreenToClient(e.TouchData.Position), e.TouchData.PointerId);
break;
case InputDevice::EventType::TouchUp:
window->OnTouchUp(window->ScreenToClient(e.TouchData.Position), e.TouchData.PointerId);
break;
}
}
WindowsManager::WindowsLocker.Unlock();
// Skip if game has no focus to handle the input
if (!Engine::HasGameViewportFocus())
{
Axes.Clear();
Actions.Clear();
return;
}
// Send input events
for (const auto& e : InputEvents)
{
switch (e.Type)
{
// Keyboard events
case InputDevice::EventType::Char:
Input::CharInput(e.CharData.Char);
break;
case InputDevice::EventType::KeyDown:
Input::KeyDown(e.KeyData.Key);
break;
case InputDevice::EventType::KeyUp:
Input::KeyUp(e.KeyData.Key);
break;
// Mouse events
case InputDevice::EventType::MouseDown:
Input::MouseDown(e.MouseData.Position, e.MouseData.Button);
break;
case InputDevice::EventType::MouseUp:
Input::MouseUp(e.MouseData.Position, e.MouseData.Button);
break;
case InputDevice::EventType::MouseDoubleClick:
Input::MouseDoubleClick(e.MouseData.Position, e.MouseData.Button);
break;
case InputDevice::EventType::MouseWheel:
Input::MouseWheel(e.MouseWheelData.Position, e.MouseWheelData.WheelDelta);
break;
case InputDevice::EventType::MouseMove:
Input::MouseMove(e.MouseData.Position);
break;
case InputDevice::EventType::MouseLeave:
Input::MouseLeave();
break;
// Touch events
case InputDevice::EventType::TouchDown:
Input::TouchDown(e.TouchData.Position, e.TouchData.PointerId);
break;
case InputDevice::EventType::TouchMove:
Input::TouchMove(e.TouchData.Position, e.TouchData.PointerId);
break;
case InputDevice::EventType::TouchUp:
Input::TouchUp(e.TouchData.Position, e.TouchData.PointerId);
break;
}
}
// Update all actions
for (int32 i = 0; i < Input::ActionMappings.Count(); i++)
{
const auto& config = Input::ActionMappings[i];
const StringView name = config.Name;
ActionData& data = Actions[name];
data.Active = false;
// Mark as updated in this frame
data.FrameIndex = frame;
}
for (int32 i = 0; i < Input::ActionMappings.Count(); i++)
{
const auto& config = Input::ActionMappings[i];
const StringView name = config.Name;
ActionData& data = Actions[name];
bool isActive;
if (config.Mode == InputActionMode::Pressing)
{
isActive = Input::GetKey(config.Key) || Input::GetMouseButton(config.MouseButton) || Input::GetGamepadButton(config.Gamepad, config.GamepadButton);
}
else if (config.Mode == InputActionMode::Press)
{
isActive = Input::GetKeyDown(config.Key) || Input::GetMouseButtonDown(config.MouseButton) || Input::GetGamepadButtonDown(config.Gamepad, config.GamepadButton);
}
else
{
isActive = Input::GetKeyUp(config.Key) || Input::GetMouseButtonUp(config.MouseButton) || Input::GetGamepadButtonUp(config.Gamepad, config.GamepadButton);
}
data.Active |= isActive;
}
// Update all axes
AxesValues.Resize(Input::AxisMappings.Count(), false);
for (int32 i = 0; i < Input::AxisMappings.Count(); i++)
{
const auto& config = Input::AxisMappings[i];
const StringView name = config.Name;
const AxisData& data = Axes[name];
// Get key raw value
const bool isPositiveKey = Input::GetKey(config.PositiveButton);
const bool isNegativeKey = Input::GetKey(config.NegativeButton);
float keyRawValue = 0;
if (isPositiveKey && !isNegativeKey)
{
keyRawValue = 1;
}
else if (!isPositiveKey && isNegativeKey)
{
keyRawValue = -1;
}
// Apply keyboard curve smoothing and snapping
float prevKeyValue = data.PrevKeyValue;
if (config.Snap && Math::NotSameSign(data.PrevKeyValue, keyRawValue))
{
prevKeyValue = 0;
}
float keyValue;
if (Math::Abs(prevKeyValue) <= Math::Abs(keyRawValue))
{
keyValue = Math::LerpStable(prevKeyValue, keyRawValue, Math::Saturate(dt * config.Sensitivity));
}
else
{
keyValue = Math::LerpStable(prevKeyValue, keyRawValue, Math::Saturate(dt * config.Gravity));
}
// Get axis raw value
float axisRawValue;
switch (config.Axis)
{
case InputAxisType::MouseX:
axisRawValue = Input::GetMousePositionDelta().X * config.Sensitivity;
break;
case InputAxisType::MouseY:
axisRawValue = Input::GetMousePositionDelta().Y * config.Sensitivity;
break;
case InputAxisType::MouseWheel:
axisRawValue = Input::GetMouseScrollDelta() * config.Sensitivity;
break;
case InputAxisType::GamepadLeftStickX:
axisRawValue = Input::GetGamepadAxis(config.Gamepad, GamepadAxis::LeftStickX);
break;
case InputAxisType::GamepadLeftStickY:
axisRawValue = Input::GetGamepadAxis(config.Gamepad, GamepadAxis::LeftStickY);
break;
case InputAxisType::GamepadRightStickX:
axisRawValue = Input::GetGamepadAxis(config.Gamepad, GamepadAxis::RightStickX);
break;
case InputAxisType::GamepadRightStickY:
axisRawValue = Input::GetGamepadAxis(config.Gamepad, GamepadAxis::RightStickY);
break;
case InputAxisType::GamepadLeftTrigger:
axisRawValue = Input::GetGamepadAxis(config.Gamepad, GamepadAxis::LeftTrigger);
break;
case InputAxisType::GamepadRightTrigger:
axisRawValue = Input::GetGamepadAxis(config.Gamepad, GamepadAxis::RightTrigger);
break;
default:
axisRawValue = 0.0f;
break;
}
// Apply dead zone
const float deadZone = config.DeadZone;
float axisValue = axisRawValue >= deadZone || axisRawValue <= -deadZone ? axisRawValue : 0.0f;
keyValue = keyValue >= deadZone || keyValue <= -deadZone ? keyValue : 0.0f;
auto& e = AxesValues[i];
e.Used = false;
e.PrevKeyValue = keyRawValue;
// Select keyboard input or axis input (choose the higher absolute values)
e.Value = Math::Abs(keyValue) > Math::Abs(axisValue) ? keyValue : axisValue;
e.RawValue = Math::Abs(keyRawValue) > Math::Abs(axisRawValue) ? keyRawValue : axisRawValue;
// Scale
e.Value *= config.Scale;
}
for (int32 i = 0; i < Input::AxisMappings.Count(); i++)
{
auto& e = AxesValues[i];
if (e.Used)
continue;
const auto& config = Input::AxisMappings[i];
const StringView name = config.Name;
AxisData& data = Axes[name];
// Blend final axis raw value between all entries
// Virtual axis with the same name may be used more than once, select the highest absolute value
for (int32 j = i + 1; j < Input::AxisMappings.Count(); j++)
{
auto& other = AxesValues[j];
if (!other.Used && Input::AxisMappings[j].Name == config.Name)
{
if (Math::Abs(other.Value) > Math::Abs(e.Value))
{
e = other;
}
other.Used = true;
}
}
// Setup axis data
data.PrevKeyValue = e.PrevKeyValue;
data.ValueRaw = e.RawValue;
data.Value = e.Value;
// Mark as updated in this frame
data.FrameIndex = frame;
}
// Remove not used entries
for (auto i = Actions.Begin(); i.IsNotEnd(); ++i)
{
if (i->Value.FrameIndex != frame)
{
Actions.Remove(i);
}
}
for (auto i = Axes.Begin(); i.IsNotEnd(); ++i)
{
if (i->Value.FrameIndex != frame)
{
Axes.Remove(i);
}
}
// Lock mouse if need to
const auto lockMode = Screen::GetCursorLock();
if (lockMode == CursorLockMode::Locked)
{
Input::SetMousePosition(Screen::GetSize() * 0.5f);
}
// Send events for the active actions (send events only in play mode)
if (!Time::GetGamePaused())
{
for (auto i = Actions.Begin(); i.IsNotEnd(); ++i)
{
if (i->Value.Active)
{
Input::ActionTriggered(i->Key);
}
}
}
}

333
Source/Engine/Input/Input.h Normal file
View File

@@ -0,0 +1,333 @@
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
#pragma once
#include "Engine/Core/Delegate.h"
#include "Engine/Core/Types/StringView.h"
#include "Engine/Core/Collections/Array.h"
#include "Engine/Scripting/ScriptingType.h"
#include "KeyboardKeys.h"
#include "Enums.h"
#include "VirtualInput.h"
class Mouse;
class Keyboard;
class Gamepad;
class InputDevice;
/// <summary>
/// The user input handling service.
/// </summary>
API_CLASS(Static) class FLAXENGINE_API Input
{
DECLARE_SCRIPTING_TYPE_NO_SPAWN(Input);
public:
/// <summary>
/// Gets the mouse (null if platform does not support mouse or it is not connected).
/// </summary>
API_FIELD(ReadOnly) static Mouse* Mouse;
/// <summary>
/// Gets the keyboard (null if platform does not support keyboard or it is not connected).
/// </summary>
API_FIELD(ReadOnly) static Keyboard* Keyboard;
/// <summary>
/// Gets the gamepads.
/// </summary>
API_FIELD(ReadOnly) static Array<Gamepad*, FixedAllocation<MAX_GAMEPADS>> Gamepads;
/// <summary>
/// Gets the gamepads count.
/// </summary>
/// <returns>The amount of active gamepads devices.</returns>
API_PROPERTY() static int32 GetGamepadsCount();
/// <summary>
/// Gets the gamepads count.
/// </summary>
/// <param name="index">The gamepad index.</param>
/// <returns>The gamepad device or null if index is invalid.</returns>
API_FUNCTION() static Gamepad* GetGamepad(int32 index);
/// <summary>
/// Action called when gamepads collection gets changed (during input update).
/// </summary>
API_EVENT() static Action GamepadsChanged;
/// <summary>
/// Called when gamepads collection gets changed.
/// </summary>
static void OnGamepadsChanged();
/// <summary>
/// Gets or sets the custom input devices.
/// </summary>
static Array<InputDevice*, InlinedAllocation<16>> CustomDevices;
public:
typedef Delegate<Char> CharDelegate;
typedef Delegate<KeyboardKeys> KeyboardDelegate;
typedef Delegate<const Vector2&> MouseDelegate;
typedef Delegate<const Vector2&, MouseButton> MouseButtonDelegate;
typedef Delegate<const Vector2&, float> MouseWheelDelegate;
typedef Delegate<const Vector2&, int32> TouchDelegate;
/// <summary>
/// Event fired on character input.
/// </summary>
static CharDelegate CharInput;
/// <summary>
/// Event fired on key pressed.
/// </summary>
static KeyboardDelegate KeyDown;
/// <summary>
/// Event fired on key released.
/// </summary>
static KeyboardDelegate KeyUp;
/// <summary>
/// Event fired when mouse button goes down.
/// </summary>
static MouseButtonDelegate MouseDown;
/// <summary>
/// Event fired when mouse button goes up.
/// </summary>
static MouseButtonDelegate MouseUp;
/// <summary>
/// Event fired when mouse button double clicks.
/// </summary>
static MouseButtonDelegate MouseDoubleClick;
/// <summary>
/// Event fired when mouse wheel is scrolling (wheel delta is normalized).
/// </summary>
static MouseWheelDelegate MouseWheel;
/// <summary>
/// Event fired when mouse moves.
/// </summary>
static MouseDelegate MouseMove;
/// <summary>
/// Event fired when mouse leaves window.
/// </summary>
static Action MouseLeave;
/// <summary>
/// Event fired when touch action begins.
/// </summary>
static TouchDelegate TouchDown;
/// <summary>
/// Event fired when touch action moves.
/// </summary>
static TouchDelegate TouchMove;
/// <summary>
/// Event fired when touch action ends.
/// </summary>
static TouchDelegate TouchUp;
public:
/// <summary>
/// Gets the text entered during the current frame (Unicode).
/// </summary>
/// <returns>The input text (Unicode).</returns>
API_PROPERTY() static StringView GetInputText();
/// <summary>
/// Gets the key state (true if key is being pressed during this frame).
/// </summary>
/// <param name="key">Key ID to check</param>
/// <returns>True while the user holds down the key identified by id</returns>
API_FUNCTION() static bool GetKey(KeyboardKeys key);
/// <summary>
/// Gets the key 'down' state (true if key was pressed in this frame).
/// </summary>
/// <param name="key">Key ID to check</param>
/// <returns>True during the frame the user starts pressing down the key</returns>
API_FUNCTION() static bool GetKeyDown(KeyboardKeys key);
/// <summary>
/// Gets the key 'up' state (true if key was released in this frame).
/// </summary>
/// <param name="key">Key ID to check</param>
/// <returns>True during the frame the user releases the key</returns>
API_FUNCTION() static bool GetKeyUp(KeyboardKeys key);
public:
/// <summary>
/// Gets the mouse position in game window coordinates.
/// </summary>
/// <returns>Mouse cursor coordinates</returns>
API_PROPERTY() static Vector2 GetMousePosition();
/// <summary>
/// Sets the mouse position in game window coordinates.
/// </summary>
/// <param name="position">Mouse position to set on</param>
API_PROPERTY() static void SetMousePosition(const Vector2& position);
/// <summary>
/// Gets the mouse position in screen-space coordinates.
/// </summary>
/// <returns>Mouse cursor coordinates</returns>
API_PROPERTY() static Vector2 GetMouseScreenPosition();
/// <summary>
/// Sets the mouse position in screen-space coordinates.
/// </summary>
/// <param name="position">Mouse position to set on</param>
API_PROPERTY() static void SetMouseScreenPosition(const Vector2& position);
/// <summary>
/// Gets the mouse position change during the last frame.
/// </summary>
/// <returns>Mouse cursor position delta</returns>
API_PROPERTY() static Vector2 GetMousePositionDelta();
/// <summary>
/// Gets the mouse wheel change during the last frame.
/// </summary>
/// <returns>Mouse wheel value delta</returns>
API_PROPERTY() static float GetMouseScrollDelta();
/// <summary>
/// Gets the mouse button state.
/// </summary>
/// <param name="button">Mouse button to check</param>
/// <returns>True while the user holds down the button</returns>
API_FUNCTION() static bool GetMouseButton(MouseButton button);
/// <summary>
/// Gets the mouse button down state.
/// </summary>
/// <param name="button">Mouse button to check</param>
/// <returns>True during the frame the user starts pressing down the button</returns>
API_FUNCTION() static bool GetMouseButtonDown(MouseButton button);
/// <summary>
/// Gets the mouse button up state.
/// </summary>
/// <param name="button">Mouse button to check</param>
/// <returns>True during the frame the user releases the button</returns>
API_FUNCTION() static bool GetMouseButtonUp(MouseButton button);
public:
/// <summary>
/// Gets the gamepad axis value.
/// </summary>
/// <param name="gamepadIndex">The gamepad index</param>
/// <param name="axis">Gamepad axis to check</param>
/// <returns>Axis value.</returns>
API_FUNCTION() static float GetGamepadAxis(int32 gamepadIndex, GamepadAxis axis);
/// <summary>
/// Gets the gamepad button state (true if being pressed during the current frame).
/// </summary>
/// <param name="gamepadIndex">The gamepad index</param>
/// <param name="button">Gamepad button to check</param>
/// <returns>True if user holds down the button, otherwise false.</returns>
API_FUNCTION() static bool GetGamepadButton(int32 gamepadIndex, GamepadButton button);
/// <summary>
/// Gets the gamepad button down state (true if was pressed during the current frame).
/// </summary>
/// <param name="gamepadIndex">The gamepad index</param>
/// <param name="button">Gamepad button to check</param>
/// <returns>True if user starts pressing down the button, otherwise false.</returns>
API_FUNCTION() static bool GetGamepadButtonDown(int32 gamepadIndex, GamepadButton button);
/// <summary>
/// Gets the gamepad button up state (true if was released during the current frame).
/// </summary>
/// <param name="gamepadIndex">The gamepad index</param>
/// <param name="button">Gamepad button to check</param>
/// <returns>True if user releases the button, otherwise false.</returns>
API_FUNCTION() static bool GetGamepadButtonUp(int32 gamepadIndex, GamepadButton button);
/// <summary>
/// Gets the gamepad axis value.
/// </summary>
/// <param name="gamepad">The gamepad</param>
/// <param name="axis">Gamepad axis to check</param>
/// <returns>Axis value.</returns>
API_FUNCTION() static float GetGamepadAxis(InputGamepadIndex gamepad, GamepadAxis axis);
/// <summary>
/// Gets the gamepad button state (true if being pressed during the current frame).
/// </summary>
/// <param name="gamepad">The gamepad</param>
/// <param name="button">Gamepad button to check</param>
/// <returns>True if user holds down the button, otherwise false.</returns>
API_FUNCTION() static bool GetGamepadButton(InputGamepadIndex gamepad, GamepadButton button);
/// <summary>
/// Gets the gamepad button down state (true if was pressed during the current frame).
/// </summary>
/// <param name="gamepad">The gamepad</param>
/// <param name="button">Gamepad button to check</param>
/// <returns>True if user starts pressing down the button, otherwise false.</returns>
API_FUNCTION() static bool GetGamepadButtonDown(InputGamepadIndex gamepad, GamepadButton button);
/// <summary>
/// Gets the gamepad button up state (true if was released during the current frame).
/// </summary>
/// <param name="gamepad">The gamepad</param>
/// <param name="button">Gamepad button to check</param>
/// <returns>True if user releases the button, otherwise false.</returns>
API_FUNCTION() static bool GetGamepadButtonUp(InputGamepadIndex gamepad, GamepadButton button);
public:
/// <summary>
/// Maps a discrete button or key press events to a "friendly name" that will later be bound to event-driven behavior. The end effect is that pressing (and/or releasing) a key, mouse button, or keypad button.
/// </summary>
API_FIELD() static Array<ActionConfig> ActionMappings;
/// <summary>
/// Maps keyboard, controller, or mouse inputs to a "friendly name" that will later be bound to continuous game behavior, such as movement. The inputs mapped in AxisMappings are continuously polled, even if they are just reporting that their input value.
/// </summary>
API_FIELD() static Array<AxisConfig> AxisMappings;
/// <summary>
/// Event fired when virtual input action is triggered. Called before scripts update. See <see cref="ActionMappings"/> to edit configuration.
/// </summary>
/// <seealso cref="InputEvent"/>
API_EVENT() static Delegate<StringView> ActionTriggered;
/// <summary>
/// Gets the value of the virtual action identified by name. Use <see cref="ActionMappings"/> to get the current config.
/// </summary>
/// <param name="name">The action name.</param>
/// <returns>True if action has been triggered in the current frame (e.g. button pressed), otherwise false.</returns>
/// <seealso cref="ActionMappings"/>
API_FUNCTION() static bool GetAction(const StringView& name);
/// <summary>
/// Gets the value of the virtual axis identified by name. Use <see cref="AxisMappings"/> to get the current config.
/// </summary>
/// <param name="name">The action name.</param>
/// <returns>The current axis value (e.g for gamepads it's in the range -1..1). Value is smoothed to reduce artifacts.</returns>
/// <seealso cref="AxisMappings"/>
API_FUNCTION() static float GetAxis(const StringView& name);
/// <summary>
/// Gets the raw value of the virtual axis identified by name with no smoothing filtering applied. Use <see cref="AxisMappings"/> to get the current config.
/// </summary>
/// <param name="name">The action name.</param>
/// <returns>The current axis value (e.g for gamepads it's in the range -1..1). No smoothing applied.</returns>
/// <seealso cref="AxisMappings"/>
API_FUNCTION() static float GetAxisRaw(const StringView& name);
};

View File

@@ -0,0 +1,134 @@
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
#pragma once
#include "Engine/Core/Math/Vector2.h"
#include "Engine/Core/Collections/Array.h"
#include "Engine/Scripting/ScriptingObject.h"
#include "Enums.h"
#include "KeyboardKeys.h"
/// <summary>
/// Base class for all input device objects.
/// </summary>
API_CLASS(Abstract, NoSpawn) class FLAXENGINE_API InputDevice : public PersistentScriptingObject
{
DECLARE_SCRIPTING_TYPE_NO_SPAWN(InputDevice);
public:
enum class EventType
{
Char,
KeyDown,
KeyUp,
MouseDown,
MouseUp,
MouseDoubleClick,
MouseWheel,
MouseMove,
MouseLeave,
TouchDown,
TouchMove,
TouchUp,
};
struct Event
{
EventType Type;
Window* Target;
union
{
struct
{
Char Char;
} CharData;
struct
{
KeyboardKeys Key;
} KeyData;
struct
{
MouseButton Button;
Vector2 Position;
} MouseData;
struct
{
float WheelDelta;
Vector2 Position;
} MouseWheelData;
struct
{
Vector2 Position;
int32 PointerId;
} TouchData;
};
Event()
{
}
Event(const Event& e)
{
Platform::MemoryCopy(this, &e, sizeof(Event));
}
};
typedef Array<Event, InlinedAllocation<32>> EventQueue;
protected:
String _name;
EventQueue _queue;
explicit InputDevice(const SpawnParams& params, const StringView& name)
: PersistentScriptingObject(params)
, _name(name)
{
}
public:
/// <summary>
/// Gets the name.
/// </summary>
API_PROPERTY() FORCE_INLINE const String& GetName() const
{
return _name;
}
/// <summary>
/// Resets the input device state. Called when application looses focus.
/// </summary>
virtual void ResetState()
{
_queue.Clear();
}
/// <summary>
/// Captures the input since the last call and triggers the input events.
/// </summary>
/// <param name="queue">The input events queue.</param>
/// <returns>True if device has been disconnected, otherwise false.<returns>
virtual bool Update(EventQueue& queue)
{
if (UpdateState())
return true;
queue.Add(_queue);
_queue.Clear();
return false;
}
/// <summary>
/// Updates only the current state of the device.
/// </summary>
/// <returns>True if device has been disconnected, otherwise false.<returns>
virtual bool UpdateState()
{
return false;
}
};

View File

@@ -0,0 +1,97 @@
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
#pragma once
#include "Engine/Core/Config/Settings.h"
#include "Engine/Serialization/JsonTools.h"
#include "VirtualInput.h"
#include "Input.h"
/// <summary>
/// Input settings container.
/// </summary>
/// <seealso cref="Settings{InputSettings}" />
class InputSettings : public Settings<InputSettings>
{
public:
/// <summary>
/// Maps a discrete button or key press events to a "friendly name" that will later be bound to event-driven behavior. The end effect is that pressing (and/or releasing) a key, mouse button, or keypad button.
/// </summary>
Array<ActionConfig> ActionMappings;
/// <summary>
/// Maps keyboard, controller, or mouse inputs to a "friendly name" that will later be bound to continuous game behavior, such as movement. The inputs mapped in AxisMappings are continuously polled, even if they are just reporting that their input value.
/// </summary>
Array<AxisConfig> AxisMappings;
public:
// [Settings]
void Apply() override
{
Input::ActionMappings = ActionMappings;
Input::AxisMappings = AxisMappings;
}
void RestoreDefault() override
{
ActionMappings.Resize(0);
AxisMappings.Resize(0);
}
void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) final override
{
const auto actionMappings = stream.FindMember("ActionMappings");
if (actionMappings != stream.MemberEnd())
{
auto& actionMappingsArray = actionMappings->value;
ASSERT(actionMappingsArray.IsArray());
ActionMappings.Resize(actionMappingsArray.Size(), false);
for (uint32 i = 0; i < actionMappingsArray.Size(); i++)
{
auto& v = actionMappingsArray[i];
if (!v.IsObject())
continue;
ActionConfig& config = ActionMappings[i];
config.Name = JsonTools::GetString(v, "Name");
config.Mode = JsonTools::GetEnum(v, "Mode", InputActionMode::Pressing);
config.Key = JsonTools::GetEnum(v, "Key", KeyboardKeys::None);
config.MouseButton = JsonTools::GetEnum(v, "MouseButton", MouseButton::None);
config.GamepadButton = JsonTools::GetEnum(v, "GamepadButton", GamepadButton::None);
config.Gamepad = JsonTools::GetEnum(v, "Gamepad", InputGamepadIndex::All);
}
}
const auto axisMappings = stream.FindMember("AxisMappings");
if (axisMappings != stream.MemberEnd())
{
auto& axisMappingsArray = axisMappings->value;
ASSERT(axisMappingsArray.IsArray());
AxisMappings.Resize(axisMappingsArray.Size(), false);
for (uint32 i = 0; i < axisMappingsArray.Size(); i++)
{
auto& v = axisMappingsArray[i];
if (!v.IsObject())
continue;
AxisConfig& config = AxisMappings[i];
config.Name = JsonTools::GetString(v, "Name");
config.Axis = JsonTools::GetEnum(v, "Axis", InputAxisType::MouseX);
config.Gamepad = JsonTools::GetEnum(v, "Gamepad", InputGamepadIndex::All);
config.PositiveButton = JsonTools::GetEnum(v, "PositiveButton", KeyboardKeys::None);
config.NegativeButton = JsonTools::GetEnum(v, "NegativeButton", KeyboardKeys::None);
config.DeadZone = JsonTools::GetFloat(v, "DeadZone", 0.1f);
config.Sensitivity = JsonTools::GetFloat(v, "Sensitivity", 0.4f);
config.Gravity = JsonTools::GetFloat(v, "Gravity", 1.0f);
config.Scale = JsonTools::GetFloat(v, "Scale", 1.0f);
config.Snap = JsonTools::GetBool(v, "Snap", false);
}
}
}
};

View File

@@ -0,0 +1,190 @@
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
#pragma once
#include "InputDevice.h"
/// <summary>
/// Represents a single hardware keyboard device. Used by the Input to report raw keyboard input events.
/// </summary>
API_CLASS(NoSpawn) class FLAXENGINE_API Keyboard : public InputDevice
{
DECLARE_SCRIPTING_TYPE_NO_SPAWN(Keyboard);
public:
/// <summary>
/// The mouse state.
/// </summary>
struct State
{
/// <summary>
/// The input text length (characters count).
/// </summary>
uint16 InputTextLength;
/// <summary>
/// The input text.
/// </summary>
Char InputText[32];
/// <summary>
/// The keys.
/// </summary>
bool Keys[(int32)KeyboardKeys::MAX];
/// <summary>
/// Clears the state.
/// </summary>
void Clear()
{
Platform::MemoryClear(this, sizeof(State));
}
};
protected:
State _state;
State _prevState;
explicit Keyboard()
: InputDevice(SpawnParams(Guid::New(), TypeInitializer), TEXT("Keyboard"))
{
_state.Clear();
_prevState.Clear();
}
public:
/// <summary>
/// Gets the text entered during the current frame.
/// </summary>
/// <returns>The input text (Unicode).</returns>
API_PROPERTY() StringView GetInputText() const
{
return StringView(_state.InputText, _state.InputTextLength);
}
/// <summary>
/// Gets keyboard key state.
/// </summary>
/// <param name="key">Key ID to check.</param>
/// <returns>True if user holds down the key identified by id, otherwise false.</returns>
API_FUNCTION() FORCE_INLINE bool GetKey(KeyboardKeys key) const
{
return _state.Keys[static_cast<int32>(key)];
}
/// <summary>
/// Gets keyboard key down state.
/// </summary>
/// <param name="key">Key ID to check</param>
/// <returns>True if user starts pressing down the key, otherwise false.</returns>
API_FUNCTION() FORCE_INLINE bool GetKeyDown(KeyboardKeys key) const
{
return _state.Keys[static_cast<int32>(key)] && !_prevState.Keys[static_cast<int32>(key)];
}
/// <summary>
/// Gets keyboard key up state.
/// </summary>
/// <param name="key">Key ID to check</param>
/// <returns>True if user releases the key, otherwise false.</returns>
API_FUNCTION() FORCE_INLINE bool GetKeyUp(KeyboardKeys key) const
{
return !_state.Keys[static_cast<int32>(key)] && _prevState.Keys[static_cast<int32>(key)];
}
/// <summary>
/// Called when keyboard enters input character.
/// </summary>
/// <param name="c">The Unicode character entered by the user.</param>
/// <param name="target">The target window to receive this event, otherwise input system will pick the window automatically.</param>
void OnCharInput(const Char c, Window* target = nullptr)
{
// Skip control characters
if (c < 32)
return;
Event& e = _queue.AddOne();
e.Type = EventType::Char;
e.Target = target;
e.CharData.Char = c;
}
/// <summary>
/// Called when key goes up.
/// </summary>
/// <param name="key">The keyboard key.</param>
/// <param name="target">The target window to receive this event, otherwise input system will pick the window automatically.</param>
void OnKeyUp(const KeyboardKeys key, Window* target = nullptr)
{
Event& e = _queue.AddOne();
e.Type = EventType::KeyUp;
e.Target = target;
e.KeyData.Key = key;
}
/// <summary>
/// Called when key goes down.
/// </summary>
/// <param name="key">The keyboard key.</param>
/// <param name="target">The target window to receive this event, otherwise input system will pick the window automatically.</param>
void OnKeyDown(const KeyboardKeys key, Window* target = nullptr)
{
Event& e = _queue.AddOne();
e.Type = EventType::KeyDown;
e.Target = target;
e.KeyData.Key = key;
}
public:
// [InputDevice]
void ResetState() override
{
InputDevice::ResetState();
_prevState.Clear();
_state.Clear();
}
bool Update(EventQueue& queue) final override
{
// Move the current state to the previous
Platform::MemoryCopy(&_prevState, &_state, sizeof(State));
// Gather new events
if (UpdateState())
return true;
// Handle events
for (int32 i = 0; i < _queue.Count(); i++)
{
const Event& e = _queue[i];
switch (e.Type)
{
case EventType::Char:
{
if (_state.InputTextLength < ARRAY_COUNT(_state.InputText) - 1)
_state.InputText[_state.InputTextLength++] = e.CharData.Char;
break;
}
case EventType::KeyDown:
{
_state.Keys[static_cast<int32>(e.KeyData.Key)] = true;
break;
}
case EventType::KeyUp:
{
_state.Keys[static_cast<int32>(e.KeyData.Key)] = false;
break;
}
}
}
// Send events further
queue.Add(_queue);
_queue.Clear();
return false;
}
};

View File

@@ -0,0 +1,825 @@
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
#pragma once
#include "Engine/Core/Config.h"
/// <summary>
/// Enumeration for key codes.
/// </summary>
API_ENUM() enum class KeyboardKeys
{
/// <summary>
/// The 'none' key
/// </summary>
None = 0,
/// <summary>
/// BACKSPACE key
/// </summary>
Backspace = 0x08,
/// <summary>
/// TAB key
/// </summary>
Tab = 0x09,
/// <summary>
/// CLEAR key
/// </summary>
Clear = 0x0C,
/// <summary>
/// ENTER key
/// </summary>
Return = 0x0D,
/// <summary>
/// Any SHIFT key, right or left
/// </summary>
Shift = 0x10,
/// <summary>
/// Any CTRL key, right or left
/// </summary>
Control = 0x11,
/// <summary>
/// Any ALT key, right or left
/// </summary>
Alt = 0x12,
/// <summary>
/// PAUSE key
/// </summary>
Pause = 0x13,
/// <summary>
/// CAPS LOCK key
/// </summary>
Capital = 0x14,
/// <summary>
/// IME Kana mode
/// </summary>
Kana = 0x15,
/// <summary>
/// IME Hangul mode
/// </summary>
Hangul = 0x15,
/// <summary>
/// IME Junja mode
/// </summary>
Junja = 0x17,
/// <summary>
/// IME final mode
/// </summary>
Final = 0x18,
/// <summary>
/// IME Hanja mode
/// </summary>
Hanja = 0x19,
/// <summary>
/// IME Kanji mode
/// </summary>
Kanji = 0x19,
/// <summary>
/// ESC key
/// </summary>
Escape = 0x1B,
/// <summary>
/// IME convert
/// </summary>
Convert = 0x1C,
/// <summary>
/// IME nonconvert
/// </summary>
Nonconvert = 0x1D,
/// <summary>
/// IME accept
/// </summary>
Accept = 0x1E,
/// <summary>
/// IME mode change request
/// </summary>
Modechange = 0x1F,
/// <summary>
/// SPACEBAR key
/// </summary>
Spacebar = 0x20,
/// <summary>
/// PAGE UP key
/// </summary>
PageUp = 0x21,
/// <summary>
/// PAGE DOWN key
/// </summary>
PageDown = 0x22,
/// <summary>
/// END key
/// </summary>
End = 0x23,
/// <summary>
/// HOME key
/// </summary>
Home = 0x24,
/// <summary>
/// LEFT ARROW key
/// </summary>
ArrowLeft = 0x25,
/// <summary>
/// UP ARROW key
/// </summary>
ArrowUp = 0x26,
/// <summary>
/// RIGHT ARROW key
/// </summary>
ArrowRight = 0x27,
/// <summary>
/// DOWN ARROW key
/// </summary>
ArrowDown = 0x28,
/// <summary>
/// SELECT key
/// </summary>
Select = 0x29,
/// <summary>
/// PRINT key
/// </summary>
Print = 0x2A,
/// <summary>
/// EXECUTE key
/// </summary>
Execute = 0x2B,
/// <summary>
/// PRINT SCREEN key
/// </summary>
PrintScreen = 0x2C,
/// <summary>
/// INSERT key
/// </summary>
Insert = 0x2D,
/// <summary>
/// DELETE key
/// </summary>
Delete = 0x2E,
/// <summary>
/// HELP key
/// </summary>
Help = 0x2F,
/// <summary>
/// The '0' key on the top of the alphanumeric keyboard.
/// </summary>
Alpha0 = 0x30,
/// <summary>
/// The '1' key on the top of the alphanumeric keyboard.
/// </summary>
Alpha1 = 0x31,
/// <summary>
/// The '2' key on the top of the alphanumeric keyboard.
/// </summary>
Alpha2 = 0x32,
/// <summary>
/// The '3' key on the top of the alphanumeric keyboard.
/// </summary>
Alpha3 = 0x33,
/// <summary>
/// The '4' key on the top of the alphanumeric keyboard.
/// </summary>
Alpha4 = 0x34,
/// <summary>
/// The '5' key on the top of the alphanumeric keyboard.
/// </summary>
Alpha5 = 0x35,
/// <summary>
/// The '6' key on the top of the alphanumeric keyboard.
/// </summary>
Alpha6 = 0x36,
/// <summary>
/// The '7' key on the top of the alphanumeric keyboard.
/// </summary>
Alpha7 = 0x37,
/// <summary>
/// The '8' key on the top of the alphanumeric keyboard.
/// </summary>
Alpha8 = 0x38,
/// <summary>
/// The '9' key on the top of the alphanumeric keyboard.
/// </summary>
Alpha9 = 0x39,
/// <summary>
/// A key
/// </summary>
A = 0x41,
/// <summary>
/// B key
/// </summary>
B = 0x42,
/// <summary>
/// C key
/// </summary>
C = 0x43,
/// <summary>
/// D key
/// </summary>
D = 0x44,
/// <summary>
/// E key
/// </summary>
E = 0x45,
/// <summary>
/// F key
/// </summary>
F = 0x46,
/// <summary>
/// G key
/// </summary>
G = 0x47,
/// <summary>
/// H key
/// </summary>
H = 0x48,
/// <summary>
/// I key
/// </summary>
I = 0x49,
/// <summary>
/// J key
/// </summary>
J = 0x4A,
/// <summary>
/// K key
/// </summary>
K = 0x4B,
/// <summary>
/// L key
/// </summary>
L = 0x4C,
/// <summary>
/// M key
/// </summary>
M = 0x4D,
/// <summary>
/// N key
/// </summary>
N = 0x4E,
/// <summary>
/// O key
/// </summary>
O = 0x4F,
/// <summary>
/// P key
/// </summary>
P = 0x50,
/// <summary>
/// Q key
/// </summary>
Q = 0x51,
/// <summary>
/// R key
/// </summary>
R = 0x52,
/// <summary>
/// S key
/// </summary>
S = 0x53,
/// <summary>
/// T key
/// </summary>
T = 0x54,
/// <summary>
/// U key
/// </summary>
U = 0x55,
/// <summary>
/// V key
/// </summary>
V = 0x56,
/// <summary>
/// W key
/// </summary>
W = 0x57,
/// <summary>
/// X key
/// </summary>
X = 0x58,
/// <summary>
/// Y key
/// </summary>
Y = 0x59,
/// <summary>
/// Z key
/// </summary>
Z = 0x5A,
/// <summary>
/// Left Windows key (Natural keyboard)
/// </summary>
LeftWindows = 0x5B,
/// <summary>
/// Right Windows key (Natural keyboard)
/// </summary>
RightWindows = 0x5C,
/// <summary>
/// Applications key (Natural keyboard)
/// </summary>
Applications = 0x5D,
/// <summary>
/// Computer Sleep key
/// </summary>
Sleep = 0x5F,
/// <summary>
/// Numeric keypad 0 key
/// </summary>
Numpad0 = 0x60,
/// <summary>
/// Numeric keypad 1 key
/// </summary>
Numpad1 = 0x61,
/// <summary>
/// Numeric keypad 2 key
/// </summary>
Numpad2 = 0x62,
/// <summary>
/// Numeric keypad 3 key
/// </summary>
Numpad3 = 0x63,
/// <summary>
/// Numeric keypad 4 key
/// </summary>
Numpad4 = 0x64,
/// <summary>
/// Numeric keypad 5 key
/// </summary>
Numpad5 = 0x65,
/// <summary>
/// Numeric keypad 6 key
/// </summary>
Numpad6 = 0x66,
/// <summary>
/// Numeric keypad 7 key
/// </summary>
Numpad7 = 0x67,
/// <summary>
/// Numeric keypad 8 key
/// </summary>
Numpad8 = 0x68,
/// <summary>
/// Numeric keypad 9 key
/// </summary>
Numpad9 = 0x69,
/// <summary>
/// Numeric keypad Multiply key '*'
/// </summary>
NumpadMultiply = 0x6A,
/// <summary>
/// Numeric keypad Add key '+'
/// </summary>
NumpadAdd = 0x6B,
/// <summary>
/// Numeric Separator key
/// </summary>
NumpadSeparator = 0x6C,
/// <summary>
/// Numeric keypad Subtract key '-'
/// </summary>
NumpadSubtract = 0x6D,
/// <summary>
/// Numeric keypad Decimal key ','
/// </summary>
NumpadDecimal = 0x6E,
/// <summary>
/// Numeric keypad Divide key '/'
/// </summary>
NumpadDivide = 0x6F,
/// <summary>
/// F1 function key
/// </summary>
F1 = 0x70,
/// <summary>
/// F2 function key
/// </summary>
F2 = 0x71,
/// <summary>
/// F3 function key
/// </summary>
F3 = 0x72,
/// <summary>
/// F4 function key
/// </summary>
F4 = 0x73,
/// <summary>
/// F5 function key
/// </summary>
F5 = 0x74,
/// <summary>
/// F6 function key
/// </summary>
F6 = 0x75,
/// <summary>
/// F7 function key
/// </summary>
F7 = 0x76,
/// <summary>
/// F8 function key
/// </summary>
F8 = 0x77,
/// <summary>
/// F9 function key
/// </summary>
F9 = 0x78,
/// <summary>
/// F10 function key
/// </summary>
F10 = 0x79,
/// <summary>
/// F11 function key
/// </summary>
F11 = 0x7A,
/// <summary>
/// F12 function key
/// </summary>
F12 = 0x7B,
/// <summary>
/// F13 function key
/// </summary>
F13 = 0x7C,
/// <summary>
/// F14 function key
/// </summary>
F14 = 0x7D,
/// <summary>
/// F15 function key
/// </summary>
F15 = 0x7E,
/// <summary>
/// F16 function key
/// </summary>
F16 = 0x7F,
/// <summary>
/// F17 function key
/// </summary>
F17 = 0x80,
/// <summary>
/// F18 function key
/// </summary>
F18 = 0x81,
/// <summary>
/// F19 function key
/// </summary>
F19 = 0x82,
/// <summary>
/// F20 function key
/// </summary>
F20 = 0x83,
/// <summary>
/// F21 function key
/// </summary>
F21 = 0x84,
/// <summary>
/// F22 function key
/// </summary>
F22 = 0x85,
/// <summary>
/// F23 function key
/// </summary>
F23 = 0x86,
/// <summary>
/// F24 function key
/// </summary>
F24 = 0x87,
/// <summary>
/// Numeric keypad NUM LOCK key
/// </summary>
Numlock = 0x90,
/// <summary>
/// SCROLL LOCK key
/// </summary>
Scroll = 0x91,
/// <summary>
/// Left MENU key
/// </summary>
LeftMenu = 0xA4,
/// <summary>
/// Right MENU key
/// </summary>
RightMenu = 0xA5,
/// <summary>
/// Browser Back key
/// </summary>
BrowserBack = 0xA6,
/// <summary>
/// Browser Forward key
/// </summary>
BrowserForward = 0xA7,
/// <summary>
/// Browser Refresh key
/// </summary>
BrowserRefresh = 0xA8,
/// <summary>
/// Browser Stop key
/// </summary>
BrowserStop = 0xA9,
/// <summary>
/// Browser Search key
/// </summary>
BrowserSearch = 0xAA,
/// <summary>
/// Browser Favorites key
/// </summary>
BrowserFavorites = 0xAB,
/// <summary>
/// Browser Start and Home key
/// </summary>
BrowserHome = 0xAC,
/// <summary>
/// Volume Mute key
/// </summary>
VolumeMute = 0xAD,
/// <summary>
/// Volume Down key
/// </summary>
VolumeDown = 0xAE,
/// <summary>
/// Volume Up key
/// </summary>
VolumeUp = 0xAF,
/// <summary>
/// Next Track key
/// </summary>
MediaNextTrack = 0xB0,
/// <summary>
/// Previous Track key
/// </summary>
MediaPrevTrack = 0xB1,
/// <summary>
/// Stop Media key
/// </summary>
MediaStop = 0xB2,
/// <summary>
/// Play/Pause Media key
/// </summary>
MediaPlayPause = 0xB3,
/// <summary>
/// Start Mail key
/// </summary>
LaunchMail = 0xB4,
/// <summary>
/// Select Media key
/// </summary>
LaunchMediaSelect = 0xB5,
/// <summary>
/// Start Application 1 key
/// </summary>
LaunchApp1 = 0xB6,
/// <summary>
/// Start Application 2 key
/// </summary>
LaunchApp2 = 0xB7,
/// <summary>
/// Used for miscellaneous characters; it can vary by keyboard. For the US standard keyboard the ';:' key
/// </summary>
Colon = 0xBA,
/// <summary>
/// For any country/region the '+' key
/// </summary>
Plus = 0xBB,
/// <summary>
/// For any country/region the ',' key
/// </summary>
Comma = 0xBC,
/// <summary>
/// For any country/region the '-' key
/// </summary>
Minus = 0xBD,
/// <summary>
/// For any country/region the '.' key
/// </summary>
Period = 0xBE,
/// <summary>
/// Used for miscellaneous characters; it can vary by keyboard. For the US standard keyboard the '/?' key
/// </summary>
Slash = 0xBF,
/// <summary>
/// Used for miscellaneous characters; it can vary by keyboard. For the US standard keyboard the '`~' key
/// </summary>
BackQuote = 0xC0,
/// <summary>
/// Used for miscellaneous characters; it can vary by keyboard. For the US standard keyboard the '[{' key
/// </summary>
LeftBracket = 0xDB,
/// <summary>
/// Used for miscellaneous characters; it can vary by keyboard. For the US standard keyboard the '\\|' key
/// </summary>
Backslash = 0xDC,
/// <summary>
/// Used for miscellaneous characters; it can vary by keyboard. For the US standard keyboard the ']}' key
/// </summary>
RightBracket = 0xDD,
/// <summary>
/// Used for miscellaneous characters; it can vary by keyboard. For the US standard keyboard the
/// 'single-quote/double-quote' key
/// </summary>
Quote = 0xDE,
/// <summary>
/// Used for miscellaneous characters; it can vary by keyboard.
/// </summary>
Oem8 = 0xDF,
/// <summary>
/// Either the angle bracket key or the backslash key on the RT 102-key keyboard
/// </summary>
Oem102 = 0xE2,
/// <summary>
/// IME PROCESS key
/// </summary>
Processkey = 0xE5,
/// <summary>
/// Used to pass Unicode characters as if they were keystrokes. The PACKET key is the low word of a 32-bit Virtual Key
/// value used for non-keyboard input methods.
/// </summary>
Packet = 0xE7,
/// <summary>
/// Attn key
/// </summary>
Attn = 0xF6,
/// <summary>
/// CrSel key
/// </summary>
Crsel = 0xF7,
/// <summary>
/// ExSel key
/// </summary>
Exsel = 0xF8,
/// <summary>
/// Erase EOF key
/// </summary>
Ereof = 0xF9,
/// <summary>
/// Play key
/// </summary>
Play = 0xFA,
/// <summary>
/// Zoom key
/// </summary>
Zoom = 0xFB,
/// <summary>
/// PA1 key
/// </summary>
Pa1 = 0xFD,
/// <summary>
/// Clear key
/// </summary>
OemClear = 0xFE,
MAX
};

283
Source/Engine/Input/Mouse.h Normal file
View File

@@ -0,0 +1,283 @@
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
#pragma once
#include "InputDevice.h"
/// <summary>
/// Represents a single hardware mouse device. Used by the Input to report raw mouse input events.
/// </summary>
/// <remarks>
/// The mouse device position is in screen-space (not game client window space).
/// </remarks>
API_CLASS(NoSpawn) class FLAXENGINE_API Mouse : public InputDevice
{
DECLARE_SCRIPTING_TYPE_NO_SPAWN(Mouse);
public:
/// <summary>
/// The mouse state.
/// </summary>
struct State
{
/// <summary>
/// The mouse position.
/// </summary>
Vector2 MousePosition;
/// <summary>
/// The mouse wheel delta.
/// </summary>
float MouseWheelDelta;
/// <summary>
/// The mouse buttons state.
/// </summary>
bool MouseButtons[(int32)MouseButton::MAX];
/// <summary>
/// Clears the state.
/// </summary>
void Clear()
{
Platform::MemoryClear(this, sizeof(State));
}
};
protected:
State _state;
State _prevState;
explicit Mouse()
: InputDevice(SpawnParams(Guid::New(), TypeInitializer), TEXT("Mouse"))
{
_state.Clear();
_prevState.Clear();
}
public:
/// <summary>
/// Gets the position of the mouse in the screen-space coordinates.
/// </summary>
/// <returns>The mouse position</returns>
API_PROPERTY() FORCE_INLINE Vector2 GetPosition() const
{
return _state.MousePosition;
}
/// <summary>
/// Gets the delta position of the mouse in the screen-space coordinates.
/// </summary>
/// <returns>The mouse position delta</returns>
API_PROPERTY() FORCE_INLINE Vector2 GetPositionDelta() const
{
return _state.MousePosition - _prevState.MousePosition;
}
/// <summary>
/// Gets the mouse wheel change during the last frame.
/// </summary>
/// <returns>Mouse wheel value delta</returns>
API_PROPERTY() FORCE_INLINE float GetScrollDelta() const
{
return _state.MouseWheelDelta;
}
/// <summary>
/// Gets the mouse button state (true if being pressed during the current frame).
/// </summary>
/// <param name="button">Mouse button to check</param>
/// <returns>True if user holds down the button, otherwise false.</returns>
API_FUNCTION() FORCE_INLINE bool GetButton(MouseButton button) const
{
return _state.MouseButtons[static_cast<int32>(button)];
}
/// <summary>
/// Gets the mouse button down state (true if was pressed during the current frame).
/// </summary>
/// <param name="button">Mouse button to check</param>
/// <returns>True if user starts pressing down the button, otherwise false.</returns>
API_FUNCTION() FORCE_INLINE bool GetButtonDown(MouseButton button) const
{
return _state.MouseButtons[static_cast<int32>(button)] && !_prevState.MouseButtons[static_cast<int32>(button)];
}
/// <summary>
/// Gets the mouse button up state (true if was released during the current frame).
/// </summary>
/// <param name="button">Mouse button to check</param>
/// <returns>True if user releases the button, otherwise false.</returns>
API_FUNCTION() FORCE_INLINE bool GetButtonUp(MouseButton button) const
{
return !_state.MouseButtons[static_cast<int32>(button)] && _prevState.MouseButtons[static_cast<int32>(button)];
}
public:
/// <summary>
/// Sets the mouse position.
/// </summary>
/// <param name="newPosition">The new position.</param>
virtual void SetMousePosition(const Vector2& newPosition) = 0;
/// <summary>
/// Called when mouse cursor gets moved by the application. Invalidates the previous cached mouse position to prevent mouse jitter when locking the cursor programatically.
/// </summary>
/// <param name="newPosition">The new mouse position.</param>
void OnMouseMoved(const Vector2& newPosition)
{
_prevState.MousePosition = newPosition;
_state.MousePosition = newPosition;
}
/// <summary>
/// Called when mouse button goes down.
/// </summary>
/// <param name="position">The mouse position.</param>
/// <param name="button">The button.</param>
/// <param name="target">The target window to receive this event, otherwise input system will pick the window automatically.</param>
void OnMouseDown(const Vector2& position, const MouseButton button, Window* target = nullptr)
{
Event& e = _queue.AddOne();
e.Type = EventType::MouseDown;
e.Target = target;
e.MouseData.Button = button;
e.MouseData.Position = position;
}
/// <summary>
/// Called when mouse button goes up.
/// </summary>
/// <param name="position">The mouse position.</param>
/// <param name="button">The button.</param>
/// <param name="target">The target window to receive this event, otherwise input system will pick the window automatically.</param>
void OnMouseUp(const Vector2& position, const MouseButton button, Window* target = nullptr)
{
Event& e = _queue.AddOne();
e.Type = EventType::MouseUp;
e.Target = target;
e.MouseData.Button = button;
e.MouseData.Position = position;
}
/// <summary>
/// Called when mouse double clicks.
/// </summary>
/// <param name="position">The mouse position.</param>
/// <param name="button">The button.</param>
/// <param name="target">The target window to receive this event, otherwise input system will pick the window automatically.</param>
void OnMouseDoubleClick(const Vector2& position, const MouseButton button, Window* target = nullptr)
{
Event& e = _queue.AddOne();
e.Type = EventType::MouseDoubleClick;
e.Target = target;
e.MouseData.Button = button;
e.MouseData.Position = position;
}
/// <summary>
/// Called when mouse moves.
/// </summary>
/// <param name="position">The mouse position.</param>
/// <param name="target">The target window to receive this event, otherwise input system will pick the window automatically.</param>
void OnMouseMove(const Vector2& position, Window* target = nullptr)
{
Event& e = _queue.AddOne();
e.Type = EventType::MouseMove;
e.Target = target;
e.MouseData.Position = position;
}
/// <summary>
/// Called when mouse leaves the input source area.
/// </summary>
/// <param name="target">The target window to receive this event, otherwise input system will pick the window automatically.</param>
void OnMouseLeave(Window* target = nullptr)
{
Event& e = _queue.AddOne();
e.Type = EventType::MouseLeave;
e.Target = target;
}
/// <summary>
/// Called when mouse wheel moves.
/// </summary>
/// <param name="position">The mouse position.</param>
/// <param name="delta">The normalized delta (range [-1;1]).</param>
/// <param name="target">The target window to receive this event, otherwise input system will pick the window automatically.</param>
void OnMouseWheel(const Vector2& position, const float delta, Window* target = nullptr)
{
Event& e = _queue.AddOne();
e.Type = EventType::MouseWheel;
e.Target = target;
e.MouseWheelData.WheelDelta = delta;
e.MouseWheelData.Position = position;
}
public:
// [InputDevice]
void ResetState() override
{
InputDevice::ResetState();
_prevState.Clear();
_state.Clear();
}
bool Update(EventQueue& queue) final override
{
// Move the current state to the previous
Platform::MemoryCopy(&_prevState, &_state, sizeof(State));
// Gather new events
if (UpdateState())
return true;
// Handle events
_state.MouseWheelDelta = 0;
for (int32 i = 0; i < _queue.Count(); i++)
{
const Event& e = _queue[i];
switch (e.Type)
{
case EventType::MouseDown:
{
_state.MouseButtons[static_cast<int32>(e.MouseData.Button)] = true;
break;
}
case EventType::MouseUp:
{
_state.MouseButtons[static_cast<int32>(e.MouseData.Button)] = false;
break;
}
case EventType::MouseDoubleClick:
{
break;
}
case EventType::MouseWheel:
{
_state.MouseWheelDelta += e.MouseWheelData.WheelDelta;
break;
}
case EventType::MouseMove:
{
_state.MousePosition = e.MouseData.Position;
break;
}
case EventType::MouseLeave:
{
break;
}
}
}
// Send events further
queue.Add(_queue);
_queue.Clear();
return false;
}
};

View File

@@ -0,0 +1,119 @@
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
#pragma once
#include "Engine/Core/Types/String.h"
#include "Enums.h"
#include "KeyboardKeys.h"
/// <summary>
/// Maps keyboard, controller, or mouse inputs to a "friendly name" that will later be bound to continuous game behavior, such as movement. The inputs mapped in AxisMappings are continuously polled, even if they are just reporting that their input value.
/// </summary>
API_STRUCT() struct ActionConfig
{
DECLARE_SCRIPTING_TYPE_MINIMAL(ActionConfig);
/// <summary>
/// The action "friendly name" used to access it from code.
/// </summary>
API_FIELD(Attributes="EditorOrder(0)")
String Name;
/// <summary>
/// The trigger mode. Allows to specify when input event should be fired.
/// </summary>
API_FIELD(Attributes="EditorOrder(5)")
InputActionMode Mode;
/// <summary>
/// The keyboard key to map for this action. Use <see cref="KeyboardKeys.None"/> to ignore it.
/// </summary>
API_FIELD(Attributes="EditorOrder(10)")
KeyboardKeys Key;
/// <summary>
/// The mouse button to map for this action. Use <see cref="MouseButton.None"/> to ignore it.
/// </summary>
API_FIELD(Attributes="EditorOrder(20)")
MouseButton MouseButton;
/// <summary>
/// The gamepad button to map for this action. Use <see cref="GamepadButton.None"/> to ignore it.
/// </summary>
API_FIELD(Attributes="EditorOrder(30)")
GamepadButton GamepadButton;
/// <summary>
/// Which gamepad should be used.
/// </summary>
API_FIELD(Attributes="EditorOrder(40)")
InputGamepadIndex Gamepad;
};
/// <summary>
/// Maps keyboard, controller, or mouse inputs to a "friendly name" that will later be bound to continuous game behavior, such as movement. The inputs mapped in AxisMappings are continuously polled, even if they are just reporting that their input value.
/// </summary>
API_STRUCT() struct AxisConfig
{
DECLARE_SCRIPTING_TYPE_MINIMAL(AxisConfig);
/// <summary>
/// The axis "friendly name" used to access it from code.
/// </summary>
API_FIELD(Attributes="EditorOrder(0)")
String Name;
/// <summary>
/// The axis type (mouse, gamepad, etc.).
/// </summary>
API_FIELD(Attributes="EditorOrder(10)")
InputAxisType Axis;
/// <summary>
/// Which gamepad should be used.
/// </summary>
API_FIELD(Attributes="EditorOrder(20)")
InputGamepadIndex Gamepad;
/// <summary>
/// The button to be pressed for movement in positive direction. Use <see cref="KeyboardKeys.None"/> to ignore it.
/// </summary>
API_FIELD(Attributes="EditorOrder(30)")
KeyboardKeys PositiveButton;
/// <summary>
/// The button to be pressed for movement in negative direction. Use <see cref="KeyboardKeys.None"/> to ignore it.
/// </summary>
API_FIELD(Attributes="EditorOrder(40)")
KeyboardKeys NegativeButton;
/// <summary>
/// Any positive or negative values that are less than this number will register as zero. Useful for gamepads to specify the deadzone.
/// </summary>
API_FIELD(Attributes="EditorOrder(50)")
float DeadZone;
/// <summary>
/// For keyboard input, a larger value will result in faster response time (in units/s). A lower value will be more smooth. For Mouse delta the value will scale the actual mouse delta.
/// </summary>
API_FIELD(Attributes="EditorOrder(60)")
float Sensitivity;
/// <summary>
/// For keyboard input describes how fast will the input recenter. Speed (in units/s) that output value will rest to neutral value if not when device at rest.
/// </summary>
API_FIELD(Attributes="EditorOrder(70)")
float Gravity;
/// <summary>
/// Additional scale parameter applied to the axis value. Allows to invert it or modify the range.
/// </summary>
API_FIELD(Attributes="EditorOrder(80)")
float Scale;
/// <summary>
/// If enabled, the axis value will be immediately reset to zero after it receives opposite inputs. For keyboard input only.
/// </summary>
API_FIELD(Attributes="EditorOrder(90)")
bool Snap;
};