Integrating latest 47acbe8
This commit is contained in:
@@ -25,10 +25,13 @@ ly_add_target(
|
||||
PUBLIC
|
||||
AZ::AzCore
|
||||
AZ::AzFramework
|
||||
CryCommon
|
||||
Gem::ScriptCanvas
|
||||
AUTOGEN_RULES
|
||||
*.ScriptCanvasNode.xml,ScriptCanvasNode_Header.jinja,$path/$fileprefix.generated.h
|
||||
*.ScriptCanvasNode.xml,ScriptCanvasNode_Source.jinja,$path/$fileprefix.generated.cpp
|
||||
*.ScriptCanvasNodeable.xml,ScriptCanvasNodeable_Header.jinja,$path/$fileprefix.generated.h
|
||||
*.ScriptCanvasNodeable.xml,ScriptCanvasNodeable_Source.jinja,$path/$fileprefix.generated.cpp
|
||||
)
|
||||
|
||||
ly_add_target(
|
||||
@@ -45,6 +48,7 @@ ly_add_target(
|
||||
Include
|
||||
BUILD_DEPENDENCIES
|
||||
PRIVATE
|
||||
AZ::AzFramework
|
||||
Gem::StartingPointInput.Static
|
||||
)
|
||||
ly_add_source_properties(
|
||||
@@ -68,6 +72,7 @@ if(PAL_TRAIT_BUILD_HOST_TOOLS)
|
||||
Include
|
||||
BUILD_DEPENDENCIES
|
||||
PRIVATE
|
||||
AZ::AzFramework
|
||||
Gem::StartingPointInput.Static
|
||||
)
|
||||
endif()
|
||||
@@ -77,7 +82,7 @@ endif()
|
||||
################################################################################
|
||||
if(PAL_TRAIT_BUILD_TESTS_SUPPORTED)
|
||||
ly_add_target(
|
||||
NAME StartingPointInput.Tests ${PAL_TRAIT_TEST_TARGET_TYPE}
|
||||
NAME StartingPointInput.Tests MODULE
|
||||
NAMESPACE Gem
|
||||
FILES_CMAKE
|
||||
startingpointinput_tests_files.cmake
|
||||
|
||||
@@ -0,0 +1,494 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
#include "StartingPointInput_precompiled.h"
|
||||
#include "Input.h"
|
||||
#include "LyToAzInputNameConversions.h"
|
||||
#include <AzCore/EBus/EBus.h>
|
||||
#include <AzCore/RTTI/ReflectContext.h>
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
#include <AzCore/Serialization/EditContext.h>
|
||||
#include <AzCore/Component/TickBus.h>
|
||||
#include <AzCore/RTTI/BehaviorContext.h>
|
||||
#include <AzCore/std/containers/set.h>
|
||||
#include <AzCore/std/sort.h>
|
||||
|
||||
#include <AzFramework/Input/Buses/Requests/InputDeviceRequestBus.h>
|
||||
#include <AzFramework/Input/Devices/Gamepad/InputDeviceGamepad.h>
|
||||
|
||||
// CryCommon includes
|
||||
#include <PlayerProfileRequestBus.h>
|
||||
#include <InputTypes.h>
|
||||
|
||||
|
||||
namespace Input
|
||||
{
|
||||
static const int s_inputVersion = 2;
|
||||
|
||||
bool ConvertInputVersion1To2(AZ::SerializeContext& context, AZ::SerializeContext::DataElementNode& classElement)
|
||||
{
|
||||
const int deviceTypeElementIndex = classElement.FindElement(AZ_CRC("Input Device Type"));
|
||||
if (deviceTypeElementIndex == -1)
|
||||
{
|
||||
AZ_Error("Input", false, "Could not find 'Input Device Type' element");
|
||||
return false;
|
||||
}
|
||||
|
||||
AZ::SerializeContext::DataElementNode& deviceTypeElementNode = classElement.GetSubElement(deviceTypeElementIndex);
|
||||
AZStd::string deviceTypeElementValue;
|
||||
if (!deviceTypeElementNode.GetData(deviceTypeElementValue))
|
||||
{
|
||||
AZ_Error("Input", false, "Could not get 'Input Device Type' element as a string");
|
||||
return false;
|
||||
}
|
||||
|
||||
const AZStd::string convertedDeviceType = ConvertInputDeviceName(deviceTypeElementValue);
|
||||
if (!deviceTypeElementNode.SetData(context, convertedDeviceType))
|
||||
{
|
||||
AZ_Error("Input", false, "Could not set 'Input Device Type' element as a string");
|
||||
return false;
|
||||
}
|
||||
|
||||
const int eventNameElementIndex = classElement.FindElement(AZ_CRC("Input Name"));
|
||||
if (eventNameElementIndex == -1)
|
||||
{
|
||||
AZ_Error("Input", false, "Could not find 'Input Name' element");
|
||||
return false;
|
||||
}
|
||||
|
||||
AZ::SerializeContext::DataElementNode& eventNameElementNode = classElement.GetSubElement(eventNameElementIndex);
|
||||
AZStd::string eventNameElementValue;
|
||||
if (!eventNameElementNode.GetData(eventNameElementValue))
|
||||
{
|
||||
AZ_Error("Input", false, "Could not get 'Input Name' element as a string");
|
||||
return false;
|
||||
}
|
||||
|
||||
const AZStd::string convertedElementName = ConvertInputEventName(eventNameElementValue);
|
||||
if (!eventNameElementNode.SetData(context, convertedElementName))
|
||||
{
|
||||
AZ_Error("Input", false, "Could not set 'Input Name' element as a string");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ConvertInputVersion(AZ::SerializeContext& context, AZ::SerializeContext::DataElementNode& classElement)
|
||||
{
|
||||
int currentUpgradedVersion = classElement.GetVersion();
|
||||
while (currentUpgradedVersion < s_inputVersion)
|
||||
{
|
||||
switch (currentUpgradedVersion)
|
||||
{
|
||||
case 1:
|
||||
{
|
||||
if (ConvertInputVersion1To2(context, classElement))
|
||||
{
|
||||
currentUpgradedVersion = 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ_Warning("Input", false, "Failed to convert Input from version 1 to 2, its data will be lost on save");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 0:
|
||||
default:
|
||||
{
|
||||
AZ_Warning("Input", false, "Unable to convert Input: unsupported version %i, its data will be lost on save", currentUpgradedVersion);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void Input::Reflect(AZ::ReflectContext* reflection)
|
||||
{
|
||||
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(reflection);
|
||||
if (serializeContext)
|
||||
{
|
||||
serializeContext->Class<Input>()
|
||||
->Version(s_inputVersion, &ConvertInputVersion)
|
||||
->Field("Input Device Type", &Input::m_inputDeviceType)
|
||||
->Field("Input Name", &Input::m_inputName)
|
||||
->Field("Event Value Multiplier", &Input::m_eventValueMultiplier)
|
||||
->Field("Dead Zone", &Input::m_deadZone);
|
||||
|
||||
AZ::EditContext* editContext = serializeContext->GetEditContext();
|
||||
if (editContext)
|
||||
{
|
||||
editContext->Class<Input>("Input", "Hold an input to generate an event")
|
||||
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
|
||||
->Attribute(AZ::Edit::Attributes::NameLabelOverride, &Input::GetEditorText)
|
||||
->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ::Edit::PropertyRefreshLevels::AttributesAndValues)
|
||||
->DataElement(AZ::Edit::UIHandlers::ComboBox, &Input::m_inputDeviceType, "Input Device Type", "The type of input device, ex keyboard")
|
||||
->Attribute(AZ::Edit::Attributes::ChangeNotify, &Input::OnDeviceSelected)
|
||||
->Attribute(AZ::Edit::Attributes::StringList, &Input::GetInputDeviceTypes)
|
||||
->DataElement(AZ::Edit::UIHandlers::ComboBox, &Input::m_inputName, "Input Name", "The name of the input you want to hold ex. space")
|
||||
->Attribute(AZ::Edit::Attributes::StringList, &Input::GetInputNamesBySelectedDevice)
|
||||
->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ::Edit::PropertyRefreshLevels::AttributesAndValues)
|
||||
->DataElement(0, &Input::m_eventValueMultiplier, "Event value multiplier", "When the event fires, the value will be scaled by this multiplier")
|
||||
->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ::Edit::PropertyRefreshLevels::AttributesAndValues)
|
||||
->DataElement(0, &Input::m_deadZone, "Dead zone", "An event will only be sent out if the value is above this threshold")
|
||||
->Attribute(AZ::Edit::Attributes::Min, 0.0f);
|
||||
}
|
||||
|
||||
if (AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(reflection))
|
||||
{
|
||||
behaviorContext->EBus<AZ::InputEventNotificationBus>("InputEventNotificationBus")
|
||||
->Event("OnPressed", &AZ::InputEventNotificationBus::Events::OnPressed)
|
||||
->Event("OnHeld", &AZ::InputEventNotificationBus::Events::OnHeld)
|
||||
->Event("OnReleased", &AZ::InputEventNotificationBus::Events::OnReleased);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Input::Input()
|
||||
{
|
||||
if (m_inputDeviceType.empty())
|
||||
{
|
||||
auto&& deviceTypes = GetInputDeviceTypes();
|
||||
if (!deviceTypes.empty())
|
||||
{
|
||||
m_inputDeviceType = deviceTypes[0];
|
||||
OnDeviceSelected();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Input::OnInputChannelEventFiltered(const AzFramework::InputChannel& inputChannel)
|
||||
{
|
||||
const LocalUserId localUserIdOfEvent = static_cast<LocalUserId>(inputChannel.GetInputDevice().GetAssignedLocalUserId());
|
||||
const float value = CalculateEventValue(inputChannel);
|
||||
const bool isPressed = fabs(value) > m_deadZone;
|
||||
if (!m_wasPressed && isPressed)
|
||||
{
|
||||
SendEventsInternal(value, localUserIdOfEvent, m_outgoingBusId, &AZ::InputEventNotificationBus::Events::OnPressed);
|
||||
}
|
||||
else if (m_wasPressed && isPressed)
|
||||
{
|
||||
SendEventsInternal(value, localUserIdOfEvent, m_outgoingBusId, &AZ::InputEventNotificationBus::Events::OnHeld);
|
||||
}
|
||||
else if (m_wasPressed && !isPressed)
|
||||
{
|
||||
SendEventsInternal(value, localUserIdOfEvent, m_outgoingBusId, &AZ::InputEventNotificationBus::Events::OnReleased);
|
||||
}
|
||||
m_wasPressed = isPressed;
|
||||
|
||||
// Return false so we don't consume the event. This should perhaps be a configurable option?
|
||||
return false;
|
||||
}
|
||||
|
||||
float Input::CalculateEventValue(const AzFramework::InputChannel& inputChannel) const
|
||||
{
|
||||
return inputChannel.GetValue();
|
||||
}
|
||||
|
||||
void Input::SendEventsInternal(float value, const AzFramework::LocalUserId& localUserIdOfEvent, const AZ::InputEventNotificationId busId, InputEventType eventType)
|
||||
{
|
||||
value *= m_eventValueMultiplier;
|
||||
|
||||
AZ::InputEventNotificationId localUserBusId = AZ::InputEventNotificationId(localUserIdOfEvent, busId.m_actionNameCrc);
|
||||
AZ::InputEventNotificationBus::Event(localUserBusId, eventType, value);
|
||||
|
||||
AZ::InputEventNotificationId wildCardBusId = AZ::InputEventNotificationId(AzFramework::LocalUserIdAny, busId.m_actionNameCrc);
|
||||
AZ::InputEventNotificationBus::Event(wildCardBusId, eventType, value);
|
||||
}
|
||||
|
||||
void Input::Activate(const AZ::InputEventNotificationId& eventNotificationId)
|
||||
{
|
||||
const AzFramework::InputDevice* inputDevice = AzFramework::InputDeviceRequests::FindInputDevice(AzFramework::InputDeviceId(m_inputDeviceType.c_str()));
|
||||
if (!inputDevice || !inputDevice->IsSupported())
|
||||
{
|
||||
// The input device that this input binding would be listening for input from
|
||||
// is not supported on the current platform, so don't bother even activating.
|
||||
// Please note distinction between InputDevice::IsSupported and IsConnected.
|
||||
return;
|
||||
}
|
||||
|
||||
const AZ::Crc32 channelNameFilter(m_inputName.c_str());
|
||||
const AZ::Crc32 deviceNameFilter(m_inputDeviceType.c_str());
|
||||
const AzFramework::LocalUserId localUserIdFilter(eventNotificationId.m_localUserId);
|
||||
AZStd::shared_ptr<InputChannelEventFilterInclusionList> filter = AZStd::make_shared<InputChannelEventFilterInclusionList>(channelNameFilter, deviceNameFilter, localUserIdFilter);
|
||||
InputChannelEventListener::SetFilter(filter);
|
||||
InputChannelEventListener::Connect();
|
||||
m_wasPressed = false;
|
||||
|
||||
m_outgoingBusId = eventNotificationId;
|
||||
AZ::GlobalInputRecordRequestBus::Handler::BusConnect();
|
||||
AZ::EditableInputRecord editableRecord;
|
||||
editableRecord.m_localUserId = eventNotificationId.m_localUserId;
|
||||
editableRecord.m_deviceName = m_inputDeviceType;
|
||||
editableRecord.m_eventGroup = eventNotificationId.m_actionNameCrc;
|
||||
editableRecord.m_inputName = m_inputName;
|
||||
AZ::InputRecordRequestBus::Handler::BusConnect(editableRecord);
|
||||
}
|
||||
|
||||
void Input::Deactivate(const AZ::InputEventNotificationId& eventNotificationId)
|
||||
{
|
||||
if (m_wasPressed)
|
||||
{
|
||||
AZ::InputEventNotificationBus::Event(m_outgoingBusId, &AZ::InputEventNotifications::OnReleased, 0.0f);
|
||||
}
|
||||
InputChannelEventListener::Disconnect();
|
||||
AZ::GlobalInputRecordRequestBus::Handler::BusDisconnect();
|
||||
AZ::InputRecordRequestBus::Handler::BusDisconnect();
|
||||
}
|
||||
|
||||
AZStd::string Input::GetEditorText() const
|
||||
{
|
||||
return m_inputName.empty() ? "<Select input>" : m_inputName;
|
||||
}
|
||||
|
||||
const AZStd::vector<AZStd::string> Input::GetInputDeviceTypes() const
|
||||
{
|
||||
AZStd::set<AZStd::string> uniqueInputDeviceTypes;
|
||||
AzFramework::InputDeviceRequests::InputDeviceIdSet availableInputDeviceIds;
|
||||
AzFramework::InputDeviceRequestBus::Broadcast(&AzFramework::InputDeviceRequests::GetInputDeviceIds,
|
||||
availableInputDeviceIds);
|
||||
for (const AzFramework::InputDeviceId& inputDeviceId : availableInputDeviceIds)
|
||||
{
|
||||
uniqueInputDeviceTypes.insert(inputDeviceId.GetName());
|
||||
}
|
||||
return AZStd::vector<AZStd::string>(uniqueInputDeviceTypes.begin(), uniqueInputDeviceTypes.end());
|
||||
}
|
||||
|
||||
const AZStd::vector<AZStd::string> Input::GetInputNamesBySelectedDevice() const
|
||||
{
|
||||
AZStd::vector<AZStd::string> retval;
|
||||
AzFramework::InputDeviceId selectedDeviceId(m_inputDeviceType.c_str());
|
||||
AzFramework::InputDeviceRequests::InputChannelIdSet availableInputChannelIds;
|
||||
AzFramework::InputDeviceRequestBus::Event(selectedDeviceId,
|
||||
&AzFramework::InputDeviceRequests::GetInputChannelIds,
|
||||
availableInputChannelIds);
|
||||
for (const AzFramework::InputChannelId& inputChannelId : availableInputChannelIds)
|
||||
{
|
||||
retval.push_back(inputChannelId.GetName());
|
||||
}
|
||||
AZStd::sort(retval.begin(), retval.end());
|
||||
return retval;
|
||||
}
|
||||
|
||||
AZ::Crc32 Input::OnDeviceSelected()
|
||||
{
|
||||
auto&& inputList = GetInputNamesBySelectedDevice();
|
||||
if (!inputList.empty())
|
||||
{
|
||||
m_inputName = inputList[0];
|
||||
}
|
||||
return AZ::Edit::PropertyRefreshLevels::AttributesAndValues;
|
||||
}
|
||||
|
||||
void Input::GatherEditableInputRecords(AZ::EditableInputRecords& outResults)
|
||||
{
|
||||
AZ::EditableInputRecord inputRecord;
|
||||
inputRecord.m_deviceName = m_inputDeviceType;
|
||||
inputRecord.m_inputName = m_inputName;
|
||||
inputRecord.m_eventGroup = m_outgoingBusId.m_actionNameCrc;
|
||||
inputRecord.m_localUserId = m_outgoingBusId.m_localUserId;
|
||||
outResults.push_back(inputRecord);
|
||||
}
|
||||
|
||||
void Input::SetInputRecord(const AZ::EditableInputRecord& newInputRecord)
|
||||
{
|
||||
Deactivate(m_outgoingBusId);
|
||||
m_inputName = newInputRecord.m_inputName;
|
||||
m_inputDeviceType = newInputRecord.m_deviceName;
|
||||
Activate(m_outgoingBusId);
|
||||
}
|
||||
|
||||
ThumbstickInput::ThumbstickInput()
|
||||
{
|
||||
m_inputDeviceType = AzFramework::InputDeviceGamepad::Name;
|
||||
OnDeviceSelected();
|
||||
}
|
||||
|
||||
void ThumbstickInput::Reflect(AZ::ReflectContext* reflection)
|
||||
{
|
||||
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(reflection);
|
||||
if (serializeContext)
|
||||
{
|
||||
serializeContext->Class<ThumbstickInput, Input>()
|
||||
->Version(1, nullptr)
|
||||
->Field("Inner Dead Zone Radius", &ThumbstickInput::m_innerDeadZoneRadius)
|
||||
->Field("Outer Dead Zone Radius", &ThumbstickInput::m_outerDeadZoneRadius)
|
||||
->Field("Axis Dead Zone Value", &ThumbstickInput::m_axisDeadZoneValue)
|
||||
->Field("Sensitivity Exponent", &ThumbstickInput::m_sensitivityExponent)
|
||||
->Field("Output Axis", &ThumbstickInput::m_outputAxis)
|
||||
;
|
||||
|
||||
AZ::EditContext* editContext = serializeContext->GetEditContext();
|
||||
if (editContext)
|
||||
{
|
||||
editContext->Class<ThumbstickInput>("ThumbstickInput", "Generate events from thumbstick input")
|
||||
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
|
||||
->Attribute(AZ::Edit::Attributes::NameLabelOverride, &ThumbstickInput::GetEditorText)
|
||||
->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ::Edit::PropertyRefreshLevels::AttributesAndValues)
|
||||
->DataElement(0, &ThumbstickInput::m_innerDeadZoneRadius, "Inner Dead Zone Radius", "The thumbstick axes vector (x,y) will be normalized between this value and Outer Dead Zone Radius")
|
||||
->Attribute(AZ::Edit::Attributes::Min, 0.0f)
|
||||
->Attribute(AZ::Edit::Attributes::Max, 1.0f)
|
||||
->DataElement(0, &ThumbstickInput::m_outerDeadZoneRadius, "Outer Dead Zone Radius", "The thumbstick axes vector (x,y) will be normalized between Inner Dead Zone Radius and this value")
|
||||
->Attribute(AZ::Edit::Attributes::Min, 0.0f)
|
||||
->Attribute(AZ::Edit::Attributes::Max, 1.0f)
|
||||
->DataElement(0, &ThumbstickInput::m_axisDeadZoneValue, "Axis Dead Zone Value", "The individual axis values will be normalized between this and 1.0f")
|
||||
->Attribute(AZ::Edit::Attributes::Min, 0.0f)
|
||||
->Attribute(AZ::Edit::Attributes::Max, 1.0f)
|
||||
->DataElement(0, &ThumbstickInput::m_sensitivityExponent, "Sensitivity Exponent", "The sensitivity exponent to apply to the normalized thumbstick components")
|
||||
->DataElement(AZ::Edit::UIHandlers::ComboBox, &ThumbstickInput::m_outputAxis, "Output Axis", "The axis value to output after peforming the dead-zone and sensitivity calculations")
|
||||
->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ::Edit::PropertyRefreshLevels::AttributesAndValues)
|
||||
->Attribute(AZ::Edit::Attributes::EnumValues, AZStd::vector<AZ::Edit::EnumConstant<OutputAxis>>
|
||||
{
|
||||
AZ::Edit::EnumConstant<OutputAxis>(OutputAxis::X, "x"),
|
||||
AZ::Edit::EnumConstant<OutputAxis>(OutputAxis::Y, "y")
|
||||
})
|
||||
;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AZStd::string ThumbstickInput::GetEditorText() const
|
||||
{
|
||||
return m_inputName.empty() ?
|
||||
"<Select input>" :
|
||||
m_inputName + (m_outputAxis == OutputAxis::X ? " (x-axis)" : " (y-axis)");
|
||||
}
|
||||
|
||||
const AZStd::vector<AZStd::string> ThumbstickInput::GetInputDeviceTypes() const
|
||||
{
|
||||
// Gamepads are currently the only device type that support thumbstick input.
|
||||
// We could (should) be more robust here by iterating over all input devices,
|
||||
// looking for any with associated input channels of type InputChannelAxis2D.
|
||||
AZStd::vector<AZStd::string> retval;
|
||||
retval.push_back(AzFramework::InputDeviceGamepad::Name);
|
||||
return retval;
|
||||
}
|
||||
|
||||
const AZStd::vector<AZStd::string> ThumbstickInput::GetInputNamesBySelectedDevice() const
|
||||
{
|
||||
// Gamepads are currently the only device type that support thumbstick input.
|
||||
// We could (should) be more robust here by iterating over all input devices,
|
||||
// looking for any with associated input channels of type InputChannelAxis2D.
|
||||
AZStd::vector<AZStd::string> retval;
|
||||
retval.push_back(AzFramework::InputDeviceGamepad::ThumbStickAxis2D::L.GetName());
|
||||
retval.push_back(AzFramework::InputDeviceGamepad::ThumbStickAxis2D::R.GetName());
|
||||
return retval;
|
||||
}
|
||||
|
||||
bool ThumbstickInput::OnInputChannelEventFiltered(const AzFramework::InputChannel& inputChannel)
|
||||
{
|
||||
// Because we are sending all thumbstick events regardless of if they are inside the dead-zone
|
||||
// (see InputChannelAxis2D::ProcessRawInputEvent)
|
||||
// ThumbstickInput components can effectively cancel themselves out if they happen to be setup
|
||||
// to receive input from a local user id that is signed into multiple controllers at the same
|
||||
// time. If the controller not being used is updated last, the (~0, ~0) events it sends every
|
||||
// frame cause the base Input::OnInputChannelEventFiltered function to determine that we need
|
||||
// to send an InputEventNotificationBus::Events::OnReleased event because m_wasPressed is set
|
||||
// to true by the other controller that is actually in use (and that is being updated first).
|
||||
//
|
||||
// To combat this, anytime we enter the m_wasPressed == true state we'll store a reference to
|
||||
// the input device id that sent the event (see below). Each time we receive an event we will
|
||||
// then check whether it's originating from the same input device id, and if not we will just
|
||||
// ignore it. Please note that while taking the address of the device id is a little sketchy,
|
||||
// we can do this because it's lifecycle is guaranteed to be longer than that of this object
|
||||
// UNLESS we ever start calling InputSystemComponent::RecreateEnabledInputDevices somewhere.
|
||||
//
|
||||
// Now in this case, the old InputDevice that owns the InputChannelId will be destroyed, but
|
||||
// not before the InputChannels it owns are destroyed first meaning InputChannel::ResetState
|
||||
// will be called, the internal state of the input channels will be reset, and an event will
|
||||
// be broadcast that will ultimately result in m_wasPressed being set to false and therefore
|
||||
// m_wasLastPressedByInputDeviceId being set to nullptr below instead of becoming a dangling
|
||||
// pointer. I definitely don't like this much, as it is risky behavior entirely dependent on
|
||||
// the internal workings of the AzFramework input system, but it's the fastest way to perform
|
||||
// this additional check. The alternative is to store the last pressed InputDeviceId by value,
|
||||
// but this would involve a (slightly) more expensive check (see InputDeviceId::operator==),
|
||||
// along with a string copy each time we set/reset the value (see InputDeviceId::operator=).
|
||||
//
|
||||
// At some point it may be worth looking at doing this check (or a safer version of it) in
|
||||
// the base Input::OnInputChannelEventFiltered function, because the 'one user logged into
|
||||
// multiple controllers' situation could conceivably cause strange behaviour for all types
|
||||
// of input bindings (albeit only if the user were to actively use both controllers at the
|
||||
// same time, in which case who can even really say what the correct behaviour should be?).
|
||||
// But that would be a far riskier change, and because it's only a problem for thumb-stick
|
||||
// input we're sending even when the controller is completely idle this fix will do for now.
|
||||
const InputDeviceId* inputDeviceId = &(inputChannel.GetInputDevice().GetInputDeviceId());
|
||||
if (m_wasLastPressedByInputDeviceId && m_wasLastPressedByInputDeviceId != inputDeviceId)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const bool shouldBeConsumed = Input::OnInputChannelEventFiltered(inputChannel);
|
||||
m_wasLastPressedByInputDeviceId = m_wasPressed ? inputDeviceId : nullptr;
|
||||
return shouldBeConsumed;
|
||||
}
|
||||
|
||||
float ThumbstickInput::CalculateEventValue(const AzFramework::InputChannel& inputChannel) const
|
||||
{
|
||||
const AzFramework::InputChannelAxis2D::AxisData2D* axisData2D = inputChannel.GetCustomData<AzFramework::InputChannelAxis2D::AxisData2D>();
|
||||
if (axisData2D == nullptr)
|
||||
{
|
||||
AZ_Warning("ThumbstickInput", false, "InputChannel with id '%s' has no axis data 2D", inputChannel.GetInputChannelId().GetName());
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
const AZ::Vector2 outputValues = ApplyDeadZonesAndSensitivity(axisData2D->m_preDeadZoneValues,
|
||||
m_innerDeadZoneRadius,
|
||||
m_outerDeadZoneRadius,
|
||||
m_axisDeadZoneValue,
|
||||
m_sensitivityExponent);
|
||||
|
||||
// Ideally we would return both values here and allow each to be mapped to a different output
|
||||
// event, but that would require a greater re-factor of both the InputManagementFramework Gem
|
||||
// and the StartingPointInput Gem, and there is nothing preventing anyone from setting up one
|
||||
// ThumbstickInput component for each axis so it would only be a simplification/optimization.
|
||||
const float axisValueToReturn = (m_outputAxis == OutputAxis::X) ? outputValues.GetX() : outputValues.GetY();
|
||||
return axisValueToReturn;
|
||||
}
|
||||
|
||||
AZ::Vector2 ThumbstickInput::ApplyDeadZonesAndSensitivity(const AZ::Vector2& inputValues, float innerDeadZone, float outerDeadZone, float axisDeadZone, float sensitivityExponent)
|
||||
{
|
||||
static const AZ::Vector2 zeroVector = AZ::Vector2::CreateZero();
|
||||
const AZ::Vector2 rawAbsValues(fabsf(inputValues.GetX()), fabsf(inputValues.GetY()));
|
||||
const float rawLength = rawAbsValues.GetLength();
|
||||
if (rawLength == 0.0f)
|
||||
{
|
||||
return zeroVector;
|
||||
}
|
||||
|
||||
// Apply the circular dead zones
|
||||
const AZ::Vector2 normalizedValues = rawAbsValues / rawLength;
|
||||
const float postCircularDeadZoneLength = AZ::GetClamp((rawLength - innerDeadZone) / (outerDeadZone - innerDeadZone), 0.0f, 1.0f);
|
||||
AZ::Vector2 absValues = normalizedValues * postCircularDeadZoneLength;
|
||||
|
||||
// Apply the per-axis dead zone
|
||||
const AZ::Vector2 absAxisValues = zeroVector.GetMax(rawAbsValues - AZ::Vector2(axisDeadZone, axisDeadZone)) / (outerDeadZone - axisDeadZone);
|
||||
|
||||
// Merge the circular and per-axis dead zones. The resulting values are the smallest ones (dead zone takes priority). And restore the components sign.
|
||||
const AZ::Vector2 signValues(AZ::GetSign(inputValues.GetX()), AZ::GetSign(inputValues.GetY()));
|
||||
AZ::Vector2 values = absValues.GetMin(absAxisValues) * signValues;
|
||||
|
||||
// Rescale the vector using the post circular dead zone length, which is the real stick vector length,
|
||||
// to avoid any jump in values when the stick is fully pushed along an axis and slowly getting out of the axis dead zone
|
||||
// Additionally, apply the sensitivity curve to the final stick vector length
|
||||
const float postAxisDeadZoneLength = values.GetLength();
|
||||
if (postAxisDeadZoneLength > 0.0f)
|
||||
{
|
||||
values /= postAxisDeadZoneLength;
|
||||
|
||||
const float postSensitivityLength = powf(postCircularDeadZoneLength, sensitivityExponent);
|
||||
values *= postSensitivityLength;
|
||||
}
|
||||
|
||||
return values;
|
||||
}
|
||||
} // namespace Input
|
||||
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
#include <InputManagementFramework/InputSubComponent.h>
|
||||
#include <AzCore/RTTI/RTTI.h>
|
||||
#include <AzCore/Component/TickBus.h>
|
||||
#include <AzFramework/Input/Buses/Notifications/InputDeviceNotificationBus.h>
|
||||
#include <AzFramework/Input/Events/InputChannelEventListener.h>
|
||||
#include <InputNotificationBus.h>
|
||||
#include <InputRequestBus.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
class ReflectContext;
|
||||
}
|
||||
|
||||
namespace Input
|
||||
{
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
/// Input handles raw input from any source and outputs Pressed, Held, and Released input events
|
||||
class Input
|
||||
: public InputSubComponent
|
||||
, protected AzFramework::InputChannelEventListener
|
||||
, protected AZ::GlobalInputRecordRequestBus::Handler
|
||||
, protected AZ::InputRecordRequestBus::Handler
|
||||
{
|
||||
public:
|
||||
Input();
|
||||
~Input() override = default;
|
||||
AZ_RTTI(Input, "{546C9EBC-90EF-4F03-891A-0736BE2A487E}", InputSubComponent);
|
||||
static void Reflect(AZ::ReflectContext* reflection);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// InputSubComponent
|
||||
void Activate(const AZ::InputEventNotificationId& eventNotificationId) override;
|
||||
void Deactivate(const AZ::InputEventNotificationId& eventNotificationId) override;
|
||||
|
||||
protected:
|
||||
AZStd::string GetEditorText() const;
|
||||
virtual const AZStd::vector<AZStd::string> GetInputDeviceTypes() const;
|
||||
virtual const AZStd::vector<AZStd::string> GetInputNamesBySelectedDevice() const;
|
||||
|
||||
AZ::Crc32 OnDeviceSelected();
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// AZ::GlobalInputRecordRequests::Handler
|
||||
void GatherEditableInputRecords(AZ::EditableInputRecords& outResults) override;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// AZ::EditableInputRecord::Handler
|
||||
void SetInputRecord(const AZ::EditableInputRecord& newInputRecord) override;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// AzFramework::InputChannelEventListener
|
||||
bool OnInputChannelEventFiltered(const AzFramework::InputChannel& inputChannel) override;
|
||||
|
||||
using InputEventType = void(AZ::InputEventNotificationBus::Events::*)(float);
|
||||
virtual float CalculateEventValue(const AzFramework::InputChannel& inputChannel) const;
|
||||
void SendEventsInternal(float value, const AzFramework::LocalUserId& localUserIdOfEvent, const AZ::InputEventNotificationId busId, InputEventType eventType);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Non Reflected Data
|
||||
AZ::InputEventNotificationId m_outgoingBusId;
|
||||
bool m_wasPressed = false;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Reflected Data
|
||||
float m_eventValueMultiplier = 1.f;
|
||||
AZStd::string m_inputName = "";
|
||||
AZStd::string m_inputDeviceType = "";
|
||||
float m_deadZone = 0.0f;
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
/// ThumbstickInput handles raw input from thumbstick sources, applies any
|
||||
/// custom dead-zone or sensitivity curve calculations, and then outputs
|
||||
/// Pressed, Held, and Released input events for the specified axis
|
||||
class ThumbstickInput
|
||||
: public Input
|
||||
{
|
||||
public:
|
||||
ThumbstickInput();
|
||||
~ThumbstickInput() override = default;
|
||||
AZ_RTTI(ThumbstickInput, "{4881FA7C-0667-476C-8C77-4DBB6C69F646}", Input);
|
||||
static void Reflect(AZ::ReflectContext* reflection);
|
||||
|
||||
protected:
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// InputSubComponent
|
||||
AZStd::string GetEditorText() const;
|
||||
const AZStd::vector<AZStd::string> GetInputDeviceTypes() const override;
|
||||
const AZStd::vector<AZStd::string> GetInputNamesBySelectedDevice() const override;
|
||||
bool OnInputChannelEventFiltered(const AzFramework::InputChannel& inputChannel) override;
|
||||
float CalculateEventValue(const AzFramework::InputChannel& inputChannel) const override;
|
||||
|
||||
static AZ::Vector2 ApplyDeadZonesAndSensitivity(const AZ::Vector2& inputValues,
|
||||
float innerDeadZone,
|
||||
float outerDeadZone,
|
||||
float axisDeadZone,
|
||||
float sensitivityExponent);
|
||||
|
||||
enum class OutputAxis
|
||||
{
|
||||
X,
|
||||
Y
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Non Reflected Data
|
||||
const AzFramework::InputDeviceId* m_wasLastPressedByInputDeviceId = nullptr;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Reflected Data
|
||||
float m_innerDeadZoneRadius = 0.0f;
|
||||
float m_outerDeadZoneRadius = 1.0f;
|
||||
float m_axisDeadZoneValue = 0.0f;
|
||||
float m_sensitivityExponent = 1.0f;
|
||||
OutputAxis m_outputAxis = OutputAxis::X;
|
||||
};
|
||||
} // namespace Input
|
||||
@@ -0,0 +1,31 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<ScriptCanvas Include="Source/InputHandlerNodeable.h" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<Class Name="InputHandlerNodeable"
|
||||
Namespace="StartingPointInput"
|
||||
QualifiedName="StartingPointInput::InputHandlerNodeable"
|
||||
PreferredClassName="Input Handler"
|
||||
Base="ScriptCanvas::Nodeable"
|
||||
Icon="Editor/Icons/ScriptCanvas/Bus.png"
|
||||
EditAttributes="AZ::Edit::Attributes::Category@Gameplay/Input"
|
||||
GraphEntryPoint="True"
|
||||
GeneratePropertyFriend="True"
|
||||
Description="Handle processed input events found in input binding assets">
|
||||
|
||||
<Output Name="Pressed" Description="Signaled when the input event begins." >
|
||||
<Parameter Name="value" Type="float" Shared="true"/>
|
||||
</Output>
|
||||
<Output Name="Held" Description="Signaled while the input event is active." >
|
||||
<Parameter Name="value" Type="float" Shared="true"/>
|
||||
</Output>
|
||||
<Output Name="Released" Description="Signaled when the input event ends." >
|
||||
<Parameter Name="value" Type="float" Shared="true"/>
|
||||
</Output>
|
||||
|
||||
<Property Name="Event Name"
|
||||
Description="The input event name as defined in an inputbinding asset. Example 'Fireball'"
|
||||
Type="AZStd::string"
|
||||
IsInput="True"
|
||||
IsOutput="False" />
|
||||
</Class>
|
||||
</ScriptCanvas>
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
#include <StartingPointInput_precompiled.h>
|
||||
#include <InputHandlerNodeable.h>
|
||||
|
||||
#include <ScriptCanvas/Utils/SerializationUtils.h>
|
||||
|
||||
using namespace ScriptCanvas;
|
||||
|
||||
namespace StartingPointInput
|
||||
{
|
||||
InputHandlerNodeable::~InputHandlerNodeable()
|
||||
{
|
||||
InputEventNotificationBus::Handler::BusDisconnect();
|
||||
}
|
||||
|
||||
void InputHandlerNodeable::ConnectEvent(AZStd::string eventName)
|
||||
{
|
||||
InputEventNotificationBus::Handler::BusDisconnect();
|
||||
|
||||
InputEventNotificationBus::Handler::BusConnect(InputEventNotificationId(eventName.c_str()));
|
||||
}
|
||||
|
||||
void InputHandlerNodeable::OnDeactivate()
|
||||
{
|
||||
InputEventNotificationBus::Handler::BusDisconnect();
|
||||
}
|
||||
|
||||
void InputHandlerNodeable::OnPressed(float value)
|
||||
{
|
||||
SCRIPT_CANVAS_PERFORMANCE_SCOPE_LATENT(GetScriptCanvasId(), GetAssetId());
|
||||
CallPressed(value);
|
||||
}
|
||||
|
||||
void InputHandlerNodeable::OnHeld(float value)
|
||||
{
|
||||
SCRIPT_CANVAS_PERFORMANCE_SCOPE_LATENT(GetScriptCanvasId(), GetAssetId());
|
||||
CallHeld(value);
|
||||
}
|
||||
|
||||
void InputHandlerNodeable::OnReleased(float value)
|
||||
{
|
||||
SCRIPT_CANVAS_PERFORMANCE_SCOPE_LATENT(GetScriptCanvasId(), GetAssetId());
|
||||
CallReleased(value);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
// script canvas
|
||||
#include <ScriptCanvas/Core/Nodeable.h>
|
||||
#include <ScriptCanvas/Core/NodeableNode.h>
|
||||
#include <ScriptCanvas/CodeGen/NodeableCodegen.h>
|
||||
#include <StartingPointInput/InputEventNotificationBus.h>
|
||||
|
||||
#include <Source/InputHandlerNodeable.generated.h>
|
||||
|
||||
namespace StartingPointInput
|
||||
{
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
/// Input handles raw input from any source and outputs Pressed, Held, and Released input events
|
||||
class InputHandlerNodeable
|
||||
: public ScriptCanvas::Nodeable
|
||||
, protected InputEventNotificationBus::Handler
|
||||
{
|
||||
SCRIPTCANVAS_NODE(InputHandlerNodeable)
|
||||
|
||||
public:
|
||||
InputHandlerNodeable() = default;
|
||||
virtual ~InputHandlerNodeable();
|
||||
InputHandlerNodeable(const InputHandlerNodeable&) = default;
|
||||
InputHandlerNodeable& operator=(const InputHandlerNodeable&) = default;
|
||||
|
||||
void ConnectEvent(AZStd::string eventName);
|
||||
|
||||
protected:
|
||||
void OnDeactivate() override;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
/// InputEventNotificationBus::Handler
|
||||
void OnPressed(float value) override;
|
||||
void OnHeld(float value) override;
|
||||
void OnReleased(float value) override;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
// script canvas
|
||||
#include <ScriptCanvas/Libraries/Libraries.h>
|
||||
|
||||
#include <InputHandlerNodeable.h>
|
||||
#include <InputNode.h>
|
||||
|
||||
|
||||
@@ -44,13 +45,15 @@ namespace StartingPointInput
|
||||
|
||||
void InputLibrary::InitNodeRegistry(ScriptCanvas::NodeRegistry& nodeRegistry)
|
||||
{
|
||||
ScriptCanvas::Library::AddNodeToRegistry<InputLibrary, InputNode>(nodeRegistry);
|
||||
ScriptCanvas::Library::AddNodeToRegistry<InputLibrary, StartingPointInput::InputNode>(nodeRegistry);
|
||||
ScriptCanvas::Library::AddNodeToRegistry<InputLibrary, StartingPointInput::Nodes::InputHandlerNodeableNode>(nodeRegistry);
|
||||
}
|
||||
|
||||
AZStd::vector<AZ::ComponentDescriptor*> InputLibrary::GetComponentDescriptors()
|
||||
{
|
||||
return AZStd::vector<AZ::ComponentDescriptor*>({
|
||||
InputNode::CreateDescriptor(),
|
||||
StartingPointInput::InputNode::CreateDescriptor(),
|
||||
StartingPointInput::Nodes::InputHandlerNodeableNode::CreateDescriptor(),
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -19,19 +19,17 @@
|
||||
#include <AzCore/Component/TickBus.h>
|
||||
#include <AzCore/RTTI/BehaviorContext.h>
|
||||
|
||||
#include <ScriptCanvas/Libraries/Libraries.h>
|
||||
|
||||
namespace StartingPointInput
|
||||
{
|
||||
void InputNode::OnPostActivate()
|
||||
{
|
||||
if (GetExecutionType() == ScriptCanvas::ExecutionType::Runtime)
|
||||
{
|
||||
const ScriptCanvas::SlotId eventNameSlotId = InputNodeProperty::GetEventNameSlotId(this);
|
||||
//if (GetExecutionType() == ScriptCanvas::ExecutionType::Runtime)
|
||||
//{
|
||||
// const ScriptCanvas::SlotId eventNameSlotId = InputNodeProperty::GetEventNameSlotId(this);
|
||||
|
||||
m_eventName = (*(FindDatum(eventNameSlotId)->GetAs<ScriptCanvas::Data::StringType>()));
|
||||
InputEventNotificationBus::Handler::BusConnect(InputEventNotificationId(m_eventName.c_str()));
|
||||
}
|
||||
// m_eventName = (*(FindDatum(eventNameSlotId)->GetAs<ScriptCanvas::Data::StringType>()));
|
||||
// InputEventNotificationBus::Handler::BusConnect(InputEventNotificationId(m_eventName.c_str()));
|
||||
//}
|
||||
}
|
||||
|
||||
void InputNode::OnDeactivate()
|
||||
@@ -94,4 +92,4 @@ namespace StartingPointInput
|
||||
}
|
||||
SignalOutput(releasedSlotId);
|
||||
}
|
||||
} // namespace StartingPointInput
|
||||
}
|
||||
|
||||
@@ -10,13 +10,11 @@
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
// code gen
|
||||
|
||||
#include <Source/InputNode.generated.h>
|
||||
|
||||
// script canvas
|
||||
#include <ScriptCanvas/Core/Node.h>
|
||||
#include <ScriptCanvas/Core/Graph.h>
|
||||
#include <ScriptCanvas/CodeGen/CodeGen.h>
|
||||
|
||||
#include <StartingPointInput/InputEventNotificationBus.h>
|
||||
|
||||
@@ -30,30 +28,17 @@ namespace StartingPointInput
|
||||
: public ScriptCanvas::Node
|
||||
, protected InputEventNotificationBus::Handler
|
||||
{
|
||||
ScriptCanvas_Node(InputNode,
|
||||
ScriptCanvas_Node::Uuid("{0B0AC61B-4BBA-42BF-BDCD-DAF2D3CA41A8}")
|
||||
ScriptCanvas_Node::Name("Input Handler")
|
||||
ScriptCanvas_Node::Description("Handle processed input events found in input binding assets")
|
||||
ScriptCanvas_Node::Icon("Editor/Icons/Components/InputConfig.png")
|
||||
ScriptCanvas_Node::Category("Gameplay/Input")
|
||||
ScriptCanvas_Node::GraphEntryPoint(true)
|
||||
);
|
||||
|
||||
public:
|
||||
|
||||
SCRIPTCANVAS_NODE(InputNode);
|
||||
|
||||
InputNode() = default;
|
||||
~InputNode() override = default;
|
||||
InputNode(const InputNode&) = default;
|
||||
InputNode& operator=(const InputNode&) = default;
|
||||
|
||||
// Outputs
|
||||
ScriptCanvas_Out(ScriptCanvas_Out::Name("Pressed", "Signaled when the input event begins."));
|
||||
ScriptCanvas_Out(ScriptCanvas_Out::Name("Held", "Signaled while the input event is active."));
|
||||
ScriptCanvas_Out(ScriptCanvas_Out::Name("Released", "Signaled when the input event ends."));
|
||||
|
||||
// Data
|
||||
ScriptCanvas_Property(AZStd::string, ScriptCanvas_Property::Name("Event Name", "The input event name as defined in an inputbinding asset. Example 'Fireball'") ScriptCanvas_Property::Input);
|
||||
AZStd::string m_eventName;
|
||||
|
||||
ScriptCanvas_Property(float, ScriptCanvas_Property::Name("Value", "The current value from the input.") ScriptCanvas_Property::Visibility(true) ScriptCanvas_Property::Output ScriptCanvas_Property::OutputStorageSpec);
|
||||
float m_value;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
@@ -72,4 +57,4 @@ namespace StartingPointInput
|
||||
void OnHeld(float value) override;
|
||||
void OnReleased(float value) override;
|
||||
};
|
||||
} // namespace StartingPointInput
|
||||
}
|
||||
|
||||
@@ -0,0 +1,235 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <AzFramework/Input/Devices/Gamepad/InputDeviceGamepad.h>
|
||||
#include <AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard.h>
|
||||
#include <AzFramework/Input/Devices/Mouse/InputDeviceMouse.h>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace Input
|
||||
{
|
||||
using namespace AzFramework;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
inline AZStd::string ConvertInputDeviceName(AZStd::string inputDeviceName)
|
||||
{
|
||||
// Using std::unordered_map instead of AZStd to avoid allocator issues
|
||||
static const std::unordered_map<std::string, std::string> map =
|
||||
{
|
||||
{ "mouse", InputDeviceMouse::Id.GetName() },
|
||||
{ "keyboard", InputDeviceKeyboard::Id.GetName() },
|
||||
{ "gamepad", InputDeviceGamepad::Name },
|
||||
{ "game console controller", InputDeviceGamepad::Name },
|
||||
{ "other game console controller", InputDeviceGamepad::Name },
|
||||
{ "Oculus Touch Controller", "oculus_controllers" },
|
||||
{ "OpenVR Controller", "openvr_controllers" }
|
||||
};
|
||||
|
||||
const auto& it = map.find(inputDeviceName.c_str());
|
||||
return it != map.end() ? it->second.c_str() : inputDeviceName.c_str();
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
inline AZStd::string ConvertInputEventName(AZStd::string inputEventName)
|
||||
{
|
||||
// Using std::unordered_map instead of AZStd to avoid allocator issues
|
||||
static const std::unordered_map<std::string, std::string> map =
|
||||
{
|
||||
{ "mouse1", InputDeviceMouse::Button::Left.GetName() },
|
||||
{ "mouse2", InputDeviceMouse::Button::Right.GetName() },
|
||||
{ "mouse3", InputDeviceMouse::Button::Middle.GetName() },
|
||||
{ "mouse4", InputDeviceMouse::Button::Other1.GetName() },
|
||||
{ "mouse5", InputDeviceMouse::Button::Other2.GetName() },
|
||||
{ "maxis_x", InputDeviceMouse::Movement::X.GetName() },
|
||||
{ "maxis_y", InputDeviceMouse::Movement::Y.GetName() },
|
||||
{ "maxis_z", InputDeviceMouse::Movement::Z.GetName() },
|
||||
{ "mwheel_up", InputDeviceMouse::Movement::Z.GetName() },
|
||||
{ "mwheel_down", InputDeviceMouse::Movement::Z.GetName() },
|
||||
{ "mouse_pos", InputDeviceMouse::SystemCursorPosition.GetName() },
|
||||
|
||||
{ "escape", InputDeviceKeyboard::Key::Escape.GetName() },
|
||||
{ "1", InputDeviceKeyboard::Key::Alphanumeric1.GetName() },
|
||||
{ "2", InputDeviceKeyboard::Key::Alphanumeric2.GetName() },
|
||||
{ "3", InputDeviceKeyboard::Key::Alphanumeric3.GetName() },
|
||||
{ "4", InputDeviceKeyboard::Key::Alphanumeric4.GetName() },
|
||||
{ "5", InputDeviceKeyboard::Key::Alphanumeric5.GetName() },
|
||||
{ "6", InputDeviceKeyboard::Key::Alphanumeric6.GetName() },
|
||||
{ "7", InputDeviceKeyboard::Key::Alphanumeric7.GetName() },
|
||||
{ "8", InputDeviceKeyboard::Key::Alphanumeric8.GetName() },
|
||||
{ "9", InputDeviceKeyboard::Key::Alphanumeric9.GetName() },
|
||||
{ "0", InputDeviceKeyboard::Key::Alphanumeric0.GetName() },
|
||||
{ "minus", InputDeviceKeyboard::Key::PunctuationHyphen.GetName() },
|
||||
{ "equals", InputDeviceKeyboard::Key::PunctuationEquals.GetName() },
|
||||
{ "backspace", InputDeviceKeyboard::Key::EditBackspace.GetName() },
|
||||
{ "tab", InputDeviceKeyboard::Key::EditTab.GetName() },
|
||||
{ "q", InputDeviceKeyboard::Key::AlphanumericQ.GetName() },
|
||||
{ "w", InputDeviceKeyboard::Key::AlphanumericW.GetName() },
|
||||
{ "e", InputDeviceKeyboard::Key::AlphanumericE.GetName() },
|
||||
{ "r", InputDeviceKeyboard::Key::AlphanumericR.GetName() },
|
||||
{ "t", InputDeviceKeyboard::Key::AlphanumericT.GetName() },
|
||||
{ "y", InputDeviceKeyboard::Key::AlphanumericY.GetName() },
|
||||
{ "u", InputDeviceKeyboard::Key::AlphanumericU.GetName() },
|
||||
{ "i", InputDeviceKeyboard::Key::AlphanumericI.GetName() },
|
||||
{ "o", InputDeviceKeyboard::Key::AlphanumericO.GetName() },
|
||||
{ "p", InputDeviceKeyboard::Key::AlphanumericP.GetName() },
|
||||
{ "lbracket", InputDeviceKeyboard::Key::PunctuationBracketL.GetName() },
|
||||
{ "rbracket", InputDeviceKeyboard::Key::PunctuationBracketR.GetName() },
|
||||
{ "enter", InputDeviceKeyboard::Key::EditEnter.GetName() },
|
||||
{ "lctrl", InputDeviceKeyboard::Key::ModifierCtrlL.GetName() },
|
||||
{ "a", InputDeviceKeyboard::Key::AlphanumericA.GetName() },
|
||||
{ "s", InputDeviceKeyboard::Key::AlphanumericS.GetName() },
|
||||
{ "d", InputDeviceKeyboard::Key::AlphanumericD.GetName() },
|
||||
{ "f", InputDeviceKeyboard::Key::AlphanumericF.GetName() },
|
||||
{ "g", InputDeviceKeyboard::Key::AlphanumericG.GetName() },
|
||||
{ "h", InputDeviceKeyboard::Key::AlphanumericH.GetName() },
|
||||
{ "j", InputDeviceKeyboard::Key::AlphanumericJ.GetName() },
|
||||
{ "k", InputDeviceKeyboard::Key::AlphanumericK.GetName() },
|
||||
{ "l", InputDeviceKeyboard::Key::AlphanumericL.GetName() },
|
||||
{ "semicolon", InputDeviceKeyboard::Key::PunctuationSemicolon.GetName() },
|
||||
{ "apostrophe", InputDeviceKeyboard::Key::PunctuationApostrophe.GetName() },
|
||||
{ "tilde", InputDeviceKeyboard::Key::PunctuationTilde.GetName() },
|
||||
{ "lshift", InputDeviceKeyboard::Key::ModifierShiftL.GetName() },
|
||||
{ "backslash", InputDeviceKeyboard::Key::PunctuationBackslash.GetName() },
|
||||
{ "z", InputDeviceKeyboard::Key::AlphanumericZ.GetName() },
|
||||
{ "x", InputDeviceKeyboard::Key::AlphanumericX.GetName() },
|
||||
{ "c", InputDeviceKeyboard::Key::AlphanumericC.GetName() },
|
||||
{ "v", InputDeviceKeyboard::Key::AlphanumericV.GetName() },
|
||||
{ "b", InputDeviceKeyboard::Key::AlphanumericB.GetName() },
|
||||
{ "n", InputDeviceKeyboard::Key::AlphanumericN.GetName() },
|
||||
{ "m", InputDeviceKeyboard::Key::AlphanumericM.GetName() },
|
||||
{ "comma", InputDeviceKeyboard::Key::PunctuationComma.GetName() },
|
||||
{ "period", InputDeviceKeyboard::Key::PunctuationPeriod.GetName() },
|
||||
{ "slash", InputDeviceKeyboard::Key::PunctuationSlash.GetName() },
|
||||
{ "rshift", InputDeviceKeyboard::Key::ModifierShiftR.GetName() },
|
||||
{ "np_multiply", InputDeviceKeyboard::Key::NumPadMultiply.GetName() },
|
||||
{ "lalt", InputDeviceKeyboard::Key::ModifierAltL.GetName() },
|
||||
{ "space", InputDeviceKeyboard::Key::EditSpace.GetName() },
|
||||
{ "capslock", InputDeviceKeyboard::Key::EditCapsLock.GetName() },
|
||||
{ "f1", InputDeviceKeyboard::Key::Function01.GetName() },
|
||||
{ "f2", InputDeviceKeyboard::Key::Function02.GetName() },
|
||||
{ "f3", InputDeviceKeyboard::Key::Function03.GetName() },
|
||||
{ "f4", InputDeviceKeyboard::Key::Function04.GetName() },
|
||||
{ "f5", InputDeviceKeyboard::Key::Function05.GetName() },
|
||||
{ "f6", InputDeviceKeyboard::Key::Function06.GetName() },
|
||||
{ "f7", InputDeviceKeyboard::Key::Function07.GetName() },
|
||||
{ "f8", InputDeviceKeyboard::Key::Function08.GetName() },
|
||||
{ "f9", InputDeviceKeyboard::Key::Function09.GetName() },
|
||||
{ "f10", InputDeviceKeyboard::Key::Function10.GetName() },
|
||||
{ "numlock", InputDeviceKeyboard::Key::NumLock.GetName() },
|
||||
{ "scrolllock", InputDeviceKeyboard::Key::WindowsSystemScrollLock.GetName() },
|
||||
{ "np_7", InputDeviceKeyboard::Key::NumPad7.GetName() },
|
||||
{ "np_8", InputDeviceKeyboard::Key::NumPad8.GetName() },
|
||||
{ "np_9", InputDeviceKeyboard::Key::NumPad9.GetName() },
|
||||
{ "np_subtract", InputDeviceKeyboard::Key::NumPadSubtract.GetName() },
|
||||
{ "np_4", InputDeviceKeyboard::Key::NumPad4.GetName() },
|
||||
{ "np_5", InputDeviceKeyboard::Key::NumPad5.GetName() },
|
||||
{ "np_6", InputDeviceKeyboard::Key::NumPad6.GetName() },
|
||||
{ "np_add", InputDeviceKeyboard::Key::NumPadAdd.GetName() },
|
||||
{ "np_1", InputDeviceKeyboard::Key::NumPad1.GetName() },
|
||||
{ "np_2", InputDeviceKeyboard::Key::NumPad2.GetName() },
|
||||
{ "np_3", InputDeviceKeyboard::Key::NumPad3.GetName() },
|
||||
{ "np_0", InputDeviceKeyboard::Key::NumPad0.GetName() },
|
||||
{ "np_period", InputDeviceKeyboard::Key::NumPadDecimal.GetName() },
|
||||
{ "f11", InputDeviceKeyboard::Key::Function11.GetName() },
|
||||
{ "f12", InputDeviceKeyboard::Key::Function12.GetName() },
|
||||
{ "f13", InputDeviceKeyboard::Key::Function13.GetName() },
|
||||
{ "f14", InputDeviceKeyboard::Key::Function14.GetName() },
|
||||
{ "f15", InputDeviceKeyboard::Key::Function15.GetName() },
|
||||
{ "np_enter", InputDeviceKeyboard::Key::NumPadEnter.GetName() },
|
||||
{ "rctrl", InputDeviceKeyboard::Key::ModifierCtrlR.GetName() },
|
||||
{ "np_divide", InputDeviceKeyboard::Key::NumPadDivide.GetName() },
|
||||
{ "print", InputDeviceKeyboard::Key::WindowsSystemPrint.GetName() },
|
||||
{ "ralt", InputDeviceKeyboard::Key::ModifierAltR.GetName() },
|
||||
{ "pause", InputDeviceKeyboard::Key::WindowsSystemPause.GetName() },
|
||||
{ "home", InputDeviceKeyboard::Key::NavigationHome.GetName() },
|
||||
{ "up", InputDeviceKeyboard::Key::NavigationArrowUp.GetName() },
|
||||
{ "pgup", InputDeviceKeyboard::Key::NavigationPageUp.GetName() },
|
||||
{ "left", InputDeviceKeyboard::Key::NavigationArrowLeft.GetName() },
|
||||
{ "right", InputDeviceKeyboard::Key::NavigationArrowRight.GetName() },
|
||||
{ "end", InputDeviceKeyboard::Key::NavigationEnd.GetName() },
|
||||
{ "down", InputDeviceKeyboard::Key::NavigationArrowDown.GetName() },
|
||||
{ "pgdn", InputDeviceKeyboard::Key::NavigationPageDown.GetName() },
|
||||
{ "insert", InputDeviceKeyboard::Key::NavigationInsert.GetName() },
|
||||
{ "delete", InputDeviceKeyboard::Key::NavigationDelete.GetName() },
|
||||
{ "oem_102", InputDeviceKeyboard::Key::SupplementaryISO.GetName() },
|
||||
|
||||
{ "gamepad_a", InputDeviceGamepad::Button::A.GetName() },
|
||||
{ "gamepad_b", InputDeviceGamepad::Button::B.GetName() },
|
||||
{ "gamepad_x", InputDeviceGamepad::Button::X.GetName() },
|
||||
{ "gamepad_y", InputDeviceGamepad::Button::Y.GetName() },
|
||||
{ "gamepad_l1", InputDeviceGamepad::Button::L1.GetName() },
|
||||
{ "gamepad_r1", InputDeviceGamepad::Button::R1.GetName() },
|
||||
{ "gamepad_l2", InputDeviceGamepad::Trigger::L2.GetName() },
|
||||
{ "gamepad_r2", InputDeviceGamepad::Trigger::R2.GetName() },
|
||||
{ "gamepad_l3", InputDeviceGamepad::Button::L3.GetName() },
|
||||
{ "gamepad_r3", InputDeviceGamepad::Button::R3.GetName() },
|
||||
{ "gamepad_up", InputDeviceGamepad::Button::DU.GetName() },
|
||||
{ "gamepad_down", InputDeviceGamepad::Button::DD.GetName() },
|
||||
{ "gamepad_left", InputDeviceGamepad::Button::DL.GetName() },
|
||||
{ "gamepad_right", InputDeviceGamepad::Button::DR.GetName() },
|
||||
{ "gamepad_start", InputDeviceGamepad::Button::Start.GetName() },
|
||||
{ "gamepad_select", InputDeviceGamepad::Button::Select.GetName() },
|
||||
{ "gamepad_sticklx", InputDeviceGamepad::ThumbStickAxis1D::LX.GetName() },
|
||||
{ "gamepad_stickly", InputDeviceGamepad::ThumbStickAxis1D::LY.GetName() },
|
||||
{ "gamepad_stickrx", InputDeviceGamepad::ThumbStickAxis1D::RX.GetName() },
|
||||
{ "gamepad_stickry", InputDeviceGamepad::ThumbStickAxis1D::RY.GetName() },
|
||||
|
||||
// Additional platform device configurations may be added here
|
||||
|
||||
{ "OculusTouch_A", "oculus_button_a" },
|
||||
{ "OculusTouch_B", "oculus_button_b" },
|
||||
{ "OculusTouch_X", "oculus_button_x" },
|
||||
{ "OculusTouch_Y", "oculus_button_y" },
|
||||
{ "OculusTouch_LeftThumbstickButton", "oculus_button_l3" },
|
||||
{ "OculusTouch_RightThumbstickButton", "oculus_button_r3" },
|
||||
{ "OculusTouch_LeftTrigger", "oculus_trigger_l1" },
|
||||
{ "OculusTouch_RightTrigger", "oculus_trigger_r1" },
|
||||
{ "OculusTouch_LeftHandTrigger", "oculus_trigger_l2" },
|
||||
{ "OculusTouch_RightHandTrigger", "oculus_trigger_r2" },
|
||||
{ "OculusTouch_LeftThumbstickX", "oculus_thumbstick_l_x" },
|
||||
{ "OculusTouch_LeftThumbstickY", "oculus_thumbstick_l_y" },
|
||||
{ "OculusTouch_RightThumbstickX", "oculus_thumbstick_r_x" },
|
||||
{ "OculusTouch_RightThumbstickY", "oculus_thumbstick_r_y" },
|
||||
|
||||
{ "OpenVR_A_0", "openvr_button_a_l" },
|
||||
{ "OpenVR_A_1", "openvr_button_a_r" },
|
||||
{ "OpenVR_DPadUp_0", "openvr_button_d_up_l" },
|
||||
{ "OpenVR_DPadDown_0", "openvr_button_d_down_l" },
|
||||
{ "OpenVR_DPadLeft_0", "openvr_button_d_left_l" },
|
||||
{ "OpenVR_DPadRight_0", "openvr_button_d_right_l" },
|
||||
{ "OpenVR_DPadUp_1", "openvr_button_d_up_r" },
|
||||
{ "OpenVR_DPadDown_1", "openvr_button_d_down_r" },
|
||||
{ "OpenVR_DPadLeft_1", "openvr_button_d_left_r" },
|
||||
{ "OpenVR_DPadRight_1", "openvr_button_d_right_r" },
|
||||
{ "OpenVR_Grip_0", "openvr_button_grip_l" },
|
||||
{ "OpenVR_Grip_1", "openvr_button_grip_r" },
|
||||
{ "OpenVR_Application_0", "openvr_button_start_l" },
|
||||
{ "OpenVR_Application_1", "openvr_button_start_r" },
|
||||
{ "OpenVR_System_0", "openvr_button_select_l" },
|
||||
{ "OpenVR_System_1", "openvr_button_select_r" },
|
||||
{ "OpenVR_TriggerButton_0", "openvr_button_trigger_l" },
|
||||
{ "OpenVR_TriggerButton_1", "openvr_button_trigger_r" },
|
||||
{ "OpenVR_TouchpadButton_0", "openvr_button_touchpad_l" },
|
||||
{ "OpenVR_TouchpadButton_1", "openvr_button_touchpad_r" },
|
||||
{ "OpenVR_Trigger_0", "openvr_trigger_l1" },
|
||||
{ "OpenVR_Trigger_1", "openvr_trigger_r1" },
|
||||
{ "OpenVR_TouchpadX_0", "openvr_touchpad_l_x" },
|
||||
{ "OpenVR_TouchpadY_0", "openvr_touchpad_l_y" },
|
||||
{ "OpenVR_TouchpadX_1", "openvr_touchpad_r_x" },
|
||||
{ "OpenVR_TouchpadY_1", "openvr_touchpad_r_y" }
|
||||
};
|
||||
|
||||
const auto& it = map.find(inputEventName.c_str());
|
||||
return it != map.end() ? it->second.c_str() : inputEventName.c_str();
|
||||
}
|
||||
} // namespace Input
|
||||
@@ -16,7 +16,6 @@
|
||||
#include "InputEventBindings.h"
|
||||
#include "InputEventMap.h"
|
||||
#include "InputLibrary.h"
|
||||
#include "InputNode.h"
|
||||
|
||||
#include <AzCore/Module/Module.h>
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
@@ -24,6 +23,8 @@
|
||||
#include <AzCore/Module/Environment.h>
|
||||
#include <AzCore/Component/Component.h>
|
||||
|
||||
#include <AzFramework/Asset/GenericAssetHandler.h>
|
||||
|
||||
namespace StartingPointInput
|
||||
{
|
||||
static bool ConvertToInputEventMap(AZ::SerializeContext& context, AZ::SerializeContext::DataElementNode& classElement)
|
||||
|
||||
@@ -12,4 +12,6 @@
|
||||
set(FILES
|
||||
${LY_ROOT_FOLDER}/Gems/ScriptCanvas/Code/Include/ScriptCanvas/AutoGen/ScriptCanvasNode_Header.jinja
|
||||
${LY_ROOT_FOLDER}/Gems/ScriptCanvas/Code/Include/ScriptCanvas/AutoGen/ScriptCanvasNode_Source.jinja
|
||||
${LY_ROOT_FOLDER}/Gems/ScriptCanvas/Code/Include/ScriptCanvas/AutoGen/ScriptCanvasNodeable_Header.jinja
|
||||
${LY_ROOT_FOLDER}/Gems/ScriptCanvas/Code/Include/ScriptCanvas/AutoGen/ScriptCanvasNodeable_Source.jinja
|
||||
)
|
||||
|
||||
@@ -22,6 +22,9 @@ set(FILES
|
||||
Source/InputLibrary.cpp
|
||||
Source/InputNode.h
|
||||
Source/InputNode.cpp
|
||||
Source/InputHandlerNodeable.h
|
||||
Source/InputHandlerNodeable.cpp
|
||||
Source/InputHandlerNodeable.ScriptCanvasNodeable.xml
|
||||
Source/InputNode.ScriptCanvasNode.xml
|
||||
Source/StartingPointInput_precompiled.h
|
||||
Source/StartingPointInput_precompiled.cpp
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{
|
||||
"gem_name": "StartingPointInput",
|
||||
"Dependencies": [
|
||||
{
|
||||
"Uuid" : "869a0d0ec11a45c299917d45c81555e6",
|
||||
|
||||
Reference in New Issue
Block a user