InputConfigurationComponent.cpp 8.3 KB

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