InputConfigurationComponent.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237
  1. /*
  2. * Copyright (c) Contributors to the Open 3D Engine Project.
  3. * For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. *
  5. * SPDX-License-Identifier: Apache-2.0 OR MIT
  6. *
  7. */
  8. #include "InputConfigurationComponent.h"
  9. #include <InputEventGroup.h>
  10. #include <AzCore/Asset/AssetSerializer.h>
  11. #include <AzCore/Component/ComponentApplicationBus.h>
  12. #include <AzCore/IO/ByteContainerStream.h>
  13. #include <AzCore/IO/SystemFile.h>
  14. #include <AzCore/Serialization/DataPatch.h>
  15. #include <AzCore/Serialization/ObjectStream.h>
  16. #include <AzCore/Serialization/Utils.h>
  17. #include <InputHandlerNodeable.h>
  18. namespace AZ
  19. {
  20. // Added definition of type info and rtti for the DataPatchTypeUpgrade class
  21. // to this Unit Test file to allow rtti functions to be accessible from the SerializeContext::TypeChange
  22. // call
  23. AZ_TYPE_INFO_TEMPLATE_WITH_NAME_IMPL(SerializeContext::DataPatchTypeUpgrade, \
  24. "DataPatchTypeUpgrade", "{E5A2F519-261C-4B81-925F-3730D363AB9C}", AZ_TYPE_INFO_CLASS, AZ_TYPE_INFO_CLASS);
  25. AZ_RTTI_NO_TYPE_INFO_IMPL((SerializeContext::DataPatchTypeUpgrade, \
  26. AZ_TYPE_INFO_CLASS, AZ_TYPE_INFO_CLASS), DataPatchUpgrade);
  27. }
  28. namespace StartingPointInput
  29. {
  30. void InputEventGroup::Reflect(AZ::ReflectContext* reflection)
  31. {
  32. if (AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(reflection))
  33. {
  34. serializeContext->Class<InputEventGroup>()
  35. ->Version(1)
  36. ->Field("Event Name", &InputEventGroup::m_eventName)
  37. ->Field("Event Generators", &InputEventGroup::m_inputHandlers)
  38. ->Field("Exclude From Release", &InputEventGroup::m_excludeFromRelease);
  39. if (AZ::EditContext* editContext = serializeContext->GetEditContext())
  40. {
  41. editContext->Class<InputEventGroup>("InputEventGroup", "Groups input bindings by the event they generate")
  42. ->ClassElement(AZ::Edit::ClassElements::EditorData, "")
  43. ->Attribute(AZ::Edit::Attributes::NameLabelOverride, &InputEventGroup::GetEditorText)
  44. ->Attribute(AZ::Edit::Attributes::AutoExpand, true)
  45. ->DataElement(AZ::Edit::UIHandlers::Default, &InputEventGroup::m_eventName, "Event Name", "The event generated by the collection of Input Bindings")
  46. ->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ_CRC("RefreshAttributesAndValues"))
  47. ->DataElement(AZ::Edit::UIHandlers::Default, &InputEventGroup::m_inputHandlers, "Event Generators", "Handlers that generate named events")
  48. ->DataElement(AZ::Edit::UIHandlers::CheckBox, &InputEventGroup::m_excludeFromRelease, "Exclude from release", "This input binding will not activate in release builds");
  49. }
  50. }
  51. }
  52. }
  53. namespace StartingPointInput
  54. {
  55. static AZ::s32 Uint32ToInt32(const AZ::u32& value)
  56. {
  57. return static_cast<AZ::s32>(value);
  58. };
  59. InputConfigurationComponent::InputConfigurationComponent()
  60. : m_uuid{ AZ::Uuid::CreateRandom() }
  61. {
  62. }
  63. InputConfigurationComponent::~InputConfigurationComponent()
  64. {
  65. m_inputEventBindings.Cleanup();
  66. }
  67. void InputConfigurationComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
  68. {
  69. provided.push_back(AZ_CRC("InputConfigurationService"));
  70. }
  71. void InputConfigurationComponent::Reflect(AZ::ReflectContext* reflection)
  72. {
  73. AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(reflection);
  74. if (serializeContext)
  75. {
  76. serializeContext->Class<InputConfigurationComponent, AZ::Component>()
  77. ->Version(4)
  78. ->Field("Input Event Bindings", &InputConfigurationComponent::m_inputEventBindingsAsset)
  79. ->Field("Local Player Index", &InputConfigurationComponent::m_localPlayerIndex)
  80. ->NameChange(2, 3, "Local User Id", "Local Player Index")
  81. ->TypeChange("Local Player Index", 3, 4, AZStd::function<AZ::s32(const AZ::u32&)>(&Uint32ToInt32))
  82. ;
  83. AZ::EditContext* editContext = serializeContext->GetEditContext();
  84. if (editContext)
  85. {
  86. editContext->Class<InputConfigurationComponent>("Input",
  87. "The Input component allows an entity to bind a set of inputs to an event by referencing a .inputbindings file")
  88. ->ClassElement(AZ::Edit::ClassElements::EditorData, "")
  89. ->Attribute(AZ::Edit::Attributes::Category, "Gameplay")
  90. ->Attribute(AZ::Edit::Attributes::Icon, "Editor/Icons/Components/InputConfig.svg")
  91. ->Attribute(AZ::Edit::Attributes::ViewportIcon, "Editor/Icons/Components/Viewport/InputConfig.svg")
  92. ->Attribute(AZ::Edit::Attributes::PrimaryAssetType, AZ::AzTypeInfo<InputEventBindingsAsset>::Uuid())
  93. ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game"))
  94. ->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components/reference/gameplay/input/")
  95. ->DataElement(AZ::Edit::UIHandlers::Default, &InputConfigurationComponent::m_inputEventBindingsAsset, "Input to event bindings",
  96. "Asset containing input to event binding information.")
  97. ->Attribute(AZ::Edit::Attributes::AutoExpand, true)
  98. ->Attribute(AZ::Edit::Attributes::ContainerCanBeModified, true)
  99. ->Attribute("BrowseIcon", ":/stylesheet/img/UI20/browse-edit-select-files.svg")
  100. ->Attribute("EditButton", "")
  101. ->Attribute("EditDescription", "Open in Asset Editor")
  102. ->Attribute("ComponentIdentifier", &InputConfigurationComponent::m_uuid)
  103. ->DataElement(AZ::Edit::UIHandlers::SpinBox, &InputConfigurationComponent::m_localPlayerIndex, "Local player index",
  104. "The player index that this component will receive input from (0 based, -1 means all controllers).\n"
  105. "Will only work on platforms such as PC where the local user id corresponds to the local player index.\n"
  106. "For other platforms, SetLocalUserId must be called at runtime with the id of a logged in user.")
  107. ->Attribute(AZ::Edit::Attributes::Min, -1)
  108. ->Attribute(AZ::Edit::Attributes::Max, 3)
  109. ;
  110. }
  111. }
  112. }
  113. void InputConfigurationComponent::Init()
  114. {
  115. // The player index that this component will receive input from (0 based, -1 means all controllers)
  116. // can be set from data, but will only work on platforms such as PC where the local user id corresponds
  117. // to the local player index. For other platforms, SetLocalUserId must be called at runtime with the id
  118. // of a logged in user, which will overwrite anything set here from data.
  119. if (m_localPlayerIndex == -1)
  120. {
  121. m_localUserId = AzFramework::LocalUserIdAny;
  122. }
  123. else
  124. {
  125. // we have to cast to u32 here even if LocalUserId is not a u32 type because some platforms use
  126. // an aggregate type for m_localUserId and only have the pertinent constructors/operators for u32
  127. m_localUserId = aznumeric_cast<AZ::u32>(m_localPlayerIndex);
  128. }
  129. }
  130. void InputConfigurationComponent::Activate()
  131. {
  132. InputConfigurationComponentRequestBus::Handler::BusConnect(GetEntityId());
  133. AZ::Data::AssetBus::Handler::BusConnect(m_inputEventBindingsAsset.GetId());
  134. }
  135. void InputConfigurationComponent::Deactivate()
  136. {
  137. InputConfigurationComponentRequestBus::Handler::BusDisconnect();
  138. AZ::Data::AssetBus::Handler::BusDisconnect();
  139. if (m_localUserId != AzFramework::LocalUserIdNone)
  140. {
  141. m_inputEventBindings.Deactivate(m_localUserId);
  142. }
  143. }
  144. void InputConfigurationComponent::SetLocalUserId(AzFramework::LocalUserId localUserId)
  145. {
  146. if (m_localUserId != localUserId)
  147. {
  148. if (m_localUserId != AzFramework::LocalUserIdNone)
  149. {
  150. m_inputEventBindings.Deactivate(m_localUserId);
  151. }
  152. m_localUserId = localUserId;
  153. if (m_localUserId != AzFramework::LocalUserIdNone)
  154. {
  155. m_inputEventBindings.Activate(m_localUserId);
  156. }
  157. }
  158. }
  159. void InputConfigurationComponent::OnAssetReloaded(AZ::Data::Asset<AZ::Data::AssetData> asset)
  160. {
  161. if (asset.GetId() == m_inputEventBindingsAsset.GetId())
  162. {
  163. // before we reload and reapply, disable any existing old ones, or else they'd double up
  164. // and you'd end up with both being active.
  165. if (m_localUserId != AzFramework::LocalUserIdNone)
  166. {
  167. m_inputEventBindings.Deactivate(m_localUserId);
  168. }
  169. m_inputEventBindingsAsset = asset;
  170. if (asset.IsReady())
  171. {
  172. OnAssetReady(asset);
  173. }
  174. }
  175. }
  176. void InputConfigurationComponent::OnAssetReady(AZ::Data::Asset<AZ::Data::AssetData> asset)
  177. {
  178. if (InputEventBindingsAsset* inputAsset = asset.GetAs<InputEventBindingsAsset>())
  179. {
  180. // the input asset actually requires us to do additional cloning and copying of the data
  181. // mainly because we retrieve the player profile data and apply it as a bindings patch on top of the data.
  182. AZ::SerializeContext* serializeContext = nullptr;
  183. AZ::ComponentApplicationBus::BroadcastResult(serializeContext, &AZ::ComponentApplicationRequests::GetSerializeContext);
  184. if (serializeContext)
  185. {
  186. // we swap with a fresh empty one here just to make sure that if this happens repeatedly, we don't have anything left over.
  187. InputEventBindings freshBindings;
  188. serializeContext->CloneObjectInplace<InputEventBindings>(freshBindings, &inputAsset->m_bindings);
  189. m_inputEventBindings.Cleanup();
  190. m_inputEventBindings.Swap(&freshBindings);
  191. }
  192. m_isAssetPrepared = true;
  193. ActivateBindingsIfAppropriate();
  194. }
  195. else
  196. {
  197. AZ_Error("Input Configuration", false, "Input bindings asset is not the correct type.");
  198. }
  199. }
  200. void InputConfigurationComponent::ActivateBindingsIfAppropriate()
  201. {
  202. if (m_isAssetPrepared)
  203. {
  204. if (m_localUserId != AzFramework::LocalUserIdNone)
  205. {
  206. m_inputEventBindings.Activate(m_localUserId);
  207. }
  208. }
  209. }
  210. void InputConfigurationComponent::EditorSetPrimaryAsset(const AZ::Data::AssetId& assetId)
  211. {
  212. m_inputEventBindingsAsset.Create(assetId);
  213. }
  214. }