FollowingCameraComponent.cpp 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  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 "FollowingCameraComponent.h"
  9. #include <AzCore/Serialization/EditContext.h>
  10. #include <AzCore/Serialization/SerializeContext.h>
  11. #include <AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard.h>
  12. #include <MathConversion.h>
  13. namespace AppleKraken
  14. {
  15. void FollowingCameraComponent::Reflect(AZ::ReflectContext* reflection)
  16. {
  17. AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(reflection);
  18. if (serializeContext)
  19. {
  20. serializeContext->Class<FollowingCameraComponent, AZ::Component>()
  21. ->Version(1)
  22. ->Field("Is active", &FollowingCameraComponent::m_isActive)
  23. ->Field("Target", &FollowingCameraComponent::m_target)
  24. ->Field("SmoothingLength", &FollowingCameraComponent::m_smoothingBuffer)
  25. ->Field("ZoomSpeed", &FollowingCameraComponent::m_zoomSpeed)
  26. ->Field("RotationSpeed", &FollowingCameraComponent::m_rotationSpeed);
  27. AZ::EditContext* editContext = serializeContext->GetEditContext();
  28. if (editContext)
  29. {
  30. editContext->Class<FollowingCameraComponent>("Following Camera", "Camera following kraken")
  31. ->ClassElement(AZ::Edit::ClassElements::EditorData, "")
  32. ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game"))
  33. ->Attribute(AZ::Edit::Attributes::Category, "AppleKraken")
  34. ->DataElement(AZ::Edit::UIHandlers::CheckBox, &FollowingCameraComponent::m_isActive, "Active", "")
  35. ->DataElement(
  36. AZ::Edit::UIHandlers::EntityId, &FollowingCameraComponent::m_target, "Target", "Entity of the followed object")
  37. ->DataElement(
  38. AZ::Edit::UIHandlers::Default,
  39. &FollowingCameraComponent::m_smoothingBuffer,
  40. "SmoothingLength",
  41. "Number of past transform used to smooth")
  42. ->DataElement(AZ::Edit::UIHandlers::Default, &FollowingCameraComponent::m_zoomSpeed, "Zoom Speed", "Zoom speed")
  43. ->DataElement(
  44. AZ::Edit::UIHandlers::Default, &FollowingCameraComponent::m_rotationSpeed, "Rotation Speed", "Rotation Speed");
  45. }
  46. }
  47. }
  48. void FollowingCameraComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required)
  49. {
  50. required.push_back(AZ_CRC("TransformService"));
  51. }
  52. void FollowingCameraComponent::Init()
  53. {
  54. }
  55. void FollowingCameraComponent::Activate()
  56. {
  57. InputChannelEventListener::Connect();
  58. AZ::TickBus::Handler::BusConnect();
  59. EBUS_EVENT_ID_RESULT(m_initialPose, GetEntityId(), AZ::TransformBus, GetLocalTM);
  60. }
  61. void FollowingCameraComponent::Deactivate()
  62. {
  63. AZ::TickBus::Handler::BusDisconnect();
  64. InputChannelEventListener::Disconnect();
  65. }
  66. void FollowingCameraComponent::OnTick(float deltaTime, AZ::ScriptTimePoint /*time*/)
  67. {
  68. if (!m_target.IsValid())
  69. {
  70. AZ_Warning("FollowingCameraComponent", false, "m_target is empty!");
  71. }
  72. if (!m_isActive)
  73. {
  74. return;
  75. }
  76. AZ::Transform target_world_transform;
  77. EBUS_EVENT_ID_RESULT(target_world_transform, m_target, AZ::TransformBus, GetWorldTM);
  78. m_lastTransforms.push_back(AZStd::make_pair(target_world_transform.GetTranslation(), deltaTime));
  79. m_lastRotations.push_back(AZStd::make_pair(target_world_transform.GetRotation(), deltaTime));
  80. if (m_lastTransforms.size() > m_smoothingBuffer)
  81. {
  82. m_lastTransforms.pop_front();
  83. m_lastRotations.pop_front();
  84. }
  85. AZ::Vector3 translation = SmoothTranslation();
  86. AZ::Quaternion quat = SmoothRotation();
  87. AZ::Transform filtered_transform = { translation,
  88. AZ::Quaternion::CreateRotationZ(quat.GetEulerRadians().GetZ()),
  89. target_world_transform.GetUniformScale() };
  90. auto modified_transform = m_initialPose.GetInverse();
  91. AZ::Transform rotation_transform = {
  92. { 0.0, 0.0, 0.0 }, AZ::Quaternion::CreateRotationY(m_rotationChange2) * AZ::Quaternion::CreateRotationZ(m_rotationChange), 1.0
  93. };
  94. modified_transform *= rotation_transform;
  95. modified_transform.Invert();
  96. AZ::Vector3 translation_modifier = modified_transform.GetBasisY() * m_zoomChange;
  97. auto modified_translation = modified_transform.GetTranslation() + translation_modifier;
  98. modified_transform.SetTranslation(modified_translation);
  99. EBUS_EVENT_ID(GetEntityId(), AZ::TransformBus, SetWorldTM, filtered_transform * modified_transform);
  100. }
  101. AZ::Vector3 FollowingCameraComponent::SmoothTranslation() const
  102. {
  103. AZ::Vector3 sum{ 0 };
  104. float normalization{ 0 };
  105. for (const auto& p : m_lastTransforms)
  106. {
  107. sum += p.first * p.second;
  108. normalization += p.second;
  109. }
  110. return sum / normalization;
  111. }
  112. AZ::Quaternion FollowingCameraComponent::SmoothRotation() const
  113. {
  114. // Smoothing is done by taking an exponential map. Note that the exponential map is the same thing as 'ScaledAxisAngle'
  115. // for 3D rotations. This map can be treated as linear space locally.
  116. // In that linear space the average is computed. It should give a pleasant experience for slow-moving objects.
  117. float normalization{ 0 };
  118. AZ::Vector3 sum{ 0 };
  119. for (const auto& p : m_lastRotations)
  120. {
  121. sum += p.second * (p.first.ConvertToScaledAxisAngle());
  122. normalization += p.second;
  123. }
  124. return AZ::Quaternion::CreateFromScaledAxisAngle(sum / normalization);
  125. }
  126. bool FollowingCameraComponent::OnInputChannelEventFiltered(const AzFramework::InputChannel& inputChannel)
  127. {
  128. if (!m_isActive)
  129. {
  130. return false;
  131. }
  132. const AzFramework::InputDeviceId& deviceId = inputChannel.GetInputDevice().GetInputDeviceId();
  133. if (AzFramework::InputDeviceKeyboard::IsKeyboardDevice(deviceId))
  134. {
  135. OnKeyboardEvent(inputChannel);
  136. }
  137. return false;
  138. }
  139. void FollowingCameraComponent::OnKeyboardEvent(const AzFramework::InputChannel& inputChannel)
  140. {
  141. if (!m_isActive)
  142. {
  143. return;
  144. }
  145. const AzFramework::InputChannelId& channelId = inputChannel.GetInputChannelId();
  146. // TODO take into account the refresh rate on key pressed for smooth experience
  147. if (channelId == AzFramework::InputDeviceKeyboard::Key::AlphanumericW)
  148. {
  149. m_zoomChange = AZStd::min(m_zoomMax, m_zoomChange + m_zoomSpeed);
  150. }
  151. if (channelId == AzFramework::InputDeviceKeyboard::Key::AlphanumericA)
  152. {
  153. m_rotationChange += m_rotationSpeed;
  154. }
  155. if (channelId == AzFramework::InputDeviceKeyboard::Key::AlphanumericQ)
  156. {
  157. m_rotationChange2 += m_rotationSpeed;
  158. }
  159. if (channelId == AzFramework::InputDeviceKeyboard::Key::AlphanumericS)
  160. {
  161. m_zoomChange = AZStd::max(m_zoomMin, m_zoomChange - m_zoomSpeed);
  162. }
  163. if (channelId == AzFramework::InputDeviceKeyboard::Key::AlphanumericD)
  164. {
  165. m_rotationChange -= m_rotationSpeed;
  166. }
  167. if (channelId == AzFramework::InputDeviceKeyboard::Key::AlphanumericE)
  168. {
  169. m_rotationChange2 -= m_rotationSpeed;
  170. }
  171. }
  172. } // namespace AppleKraken