Version:

第19章 使用鼠标输入转向

第19章 使用鼠标输入转向

介绍

在第 18 章 角色移动 中,我们使用 W 和 S 实现角色移动,用于向前和向后移动。在本章中,我将介绍如何使用鼠标运动来转动角色实体,这也会转动摄像机。

注意:

自定义输入事件组

在第 18 章 角色移动 中,我们使用了 O3DE 安装的输入绑定文件,网址为 C:\O3DE\21.11.2\Gems\StartingPointInput\Assets\thirdpersonmovement.inputbindings。最好复制它,使其成为我们自己的,并将其置于我们的源代码控制之下。

  1. 将 thirdpersonmovement.inputbindings 从 C:\O3DE\21.11.2\Gems \StartingPointInput\Assets\ 复制到 C:\git\book\Gems\MyGem\Assets\ 作为 mythirdpersonmovement.inputbindings。
  2. 将 Input (输入) 组件更改为指向 mythirdpersonmovement.inputbindings。
  3. 打开 mythirdpersonmovement.inputbindings 的 Asset Editor。
  4. 删除除 move forward、move right 和 rotate yaw 之外的所有绑定

鼠标输入

Input Event Groups 允许您为每个事件指定设备和设备输入。在这里,我们分配了鼠标设备和mouse_delta_x输入来旋转偏航事件。这意味着鼠标的横向运动将被发送到分配的事件。以下是捕获此鼠标输入并应用它的方法。

图 19.1.使用鼠标转动

  1. 创建一个变量来存储 mouse_delta_x 的当前状态。
class ChickenControllerComponent
{
...
float m_yaw = 0.f;
};
  1. 为 “rotate yaw” 创建 InputEventNotificationId。
const StartingPointInput::InputEventNotificationId RotateYawEventId("rotate yaw");
  1. 使用 InputEventNotificationBus 注册活动。
void ChickenControllerComponent::Activate()
{
  ...
  InputEventNotificationBus::MultiHandler::BusConnect(
  RotateYawEventId);
}
注意:
此步骤至关重要。否则,您将根本不会收到 “rotate yaw” 的鼠标事件。
  1. 重写虚拟方法 OnHeld 以捕获鼠标输入。
void ChickenControllerComponent::OnHeld(float value)
{
  const InputEventNotificationId* inputId =
  InputEventNotificationBus::GetCurrentBusId();
  if (inputId == nullptr)
  {
    return;
  }
  if (*inputId == RotateYawEventId)
  {
    m_yaw = value;
  }
}
  1. 更新 CreateInput 以复制m_yaw值进行处理。
ChickenInput ChickenControllerComponent::CreateInput()
{
  ChickenInput input;
  ...
  input.m_viewYaw = m_yaw;
  return input;
}
  1. 更新 ProcessInput 以更新鸡的旋转。
void ChickenControllerComponent::UpdateRotation(
const ChickenInput& input)
{
  AZ::TransformInterface* t = GetEntity()->GetTransform();
  float currentHeading = t->GetWorldRotationQuaternion().
  GetEulerRadians().GetZ();
  currentHeading += input.m_viewYaw * m_turnSpeed;
  AZ::Quaternion q =
  AZ::Quaternion::CreateRotationZ(currentHeading);
  t->SetWorldRotationQuaternion(q);
}

通过这些更改,鸡将根据鼠标的左右移动而转动。相机将始终保持在鸡的后面,因为它被锁定到该方向

源代码

注意:

作为奖励,此源代码还实现了 strafing。它的实现与前进和向后移动非常相似。

例 19.1.ChickenControllerComponent.h

 #pragma once
 #include <AzCore/Component/Component.h>
 #include <AzCore/Component/TickBus.h>
 #include <AzCore/Math/Vector3.h>
 #include <StartingPointInput/InputEventNotificationBus.h>
 namespace MyGem
 {
 const StartingPointInput::InputEventNotificationId
        MoveFwdEventId("move forward");
 const StartingPointInput::InputEventNotificationId
        MoveRightEventId("move right");
 const StartingPointInput::InputEventNotificationId
        RotateYawEventId("rotate yaw");
 class ChickenInput
    {
 public:
 float m_forwardAxis = 0;
 float m_strafeAxis = 0;
 float m_viewYaw = 0;
    };
 class ChickenControllerComponent
        : 
public AZ::Component
        , 
        , 
public AZ::TickBus::Handler
 public StartingPointInput::
            InputEventNotificationBus::MultiHandler
    {
 public:
        AZ_COMPONENT(ChickenControllerComponent,
 "{fe639d60-75c0-4e16-aa1a-0d44dbe6d339}");
 static void Reflect(AZ::ReflectContext* rc);
 // AZ::Component interface implementation
 void Activate() override;
 void Deactivate() override;
  // AZ::InputEventNotificationBus interface
 void OnPressed(float value) override;
 void OnReleased(float value) override;
 void OnHeld(float value) override;
 // TickBus interface
 void OnTick(float deltaTime, AZ::ScriptTimePoint) override;
 private:
        ChickenInput CreateInput();
 void ProcessInput(const ChickenInput& input);
 void UpdateRotation(const ChickenInput& input);
 void UpdateVelocity(const ChickenInput& input);
        AZ::Vector3 m_velocity = AZ::Vector3::CreateZero();
 float m_speed = 10.f;
 float m_turnSpeed = 1.f;
 float m_forward = 0.f;
 float m_strafe = 0.f;
 float m_yaw = 0.f;
    };
 } // namespace MyGem

例 19.2. ChickenControllerComponent.cpp

 #include <ChickenControllerComponent.h>
 #include <AzCore/Component/Entity.h>
 #include <AzCore/Component/TransformBus.h>
 #include <AzCore/Serialization/EditContext.h>
 #include <AzFramework/Physics/CharacterBus.h>
 namespace MyGem
 {
 using namespace StartingPointInput;
 void ChickenControllerComponent::Reflect(AZ::ReflectContext* rc)
    {
 if (auto sc = azrtti_cast<AZ::SerializeContext*>(rc))
        {
            sc->Class<ChickenControllerComponent, AZ::Component>()
              ->Field("Speed", &ChickenControllerComponent::m_speed)
              ->Field("Turn Speed",
                    &ChickenControllerComponent::m_turnSpeed)
              ->Version(2);
 if (AZ::EditContext* ec = sc->GetEditContext())
            {
 using namespace AZ::Edit;
                ec->Class<ChickenControllerComponent>(
 "Chicken Controller",
 "[Player controlled chicken]")
 ->ClassElement(ClassElements::EditorData, "")
                    ->Attribute(
                        Attributes::AppearsInAddComponentMenu,
                            AZ_CRC_CE("Game"))
                    ->DataElement(nullptr,
                        &ChickenControllerComponent::m_turnSpeed,
 "Turn Speed", "Chicken's turning speed")
                    ->DataElement(nullptr,
                        &ChickenControllerComponent::m_speed,
 "Speed", "Chicken's speed");
            }
        }
    }
 void ChickenControllerComponent::Activate()
    {
        InputEventNotificationBus::MultiHandler::BusConnect(
            MoveFwdEventId);
        InputEventNotificationBus::MultiHandler::BusConnect(
            MoveRightEventId);
        InputEventNotificationBus::MultiHandler::BusConnect(
            RotateYawEventId);
        AZ::TickBus::Handler::BusConnect();
    }
 void ChickenControllerComponent::Deactivate()
    {
        AZ::TickBus::Handler::BusDisconnect();
        InputEventNotificationBus::MultiHandler::BusDisconnect();
    }
 void ChickenControllerComponent::OnPressed(float value)
    {
 const InputEventNotificationId* inputId =
            InputEventNotificationBus::GetCurrentBusId();
 if (inputId == nullptr)
        {
 return;
        }
 if (*inputId == MoveFwdEventId)
        {
            m_forward = value;
        }
 else if (*inputId == MoveRightEventId)
        {
            m_strafe = value;
        }
    }
 void ChickenControllerComponent::OnReleased(float value)
    {
 const InputEventNotificationId* inputId =
            InputEventNotificationBus::GetCurrentBusId();
             if (inputId == nullptr)
        {
 return;
        }
 if (*inputId == MoveFwdEventId)
        {
            m_forward = value;
        }
 else if (*inputId == MoveRightEventId)
        {
            m_strafe = value;
        }
    }
 void ChickenControllerComponent::OnHeld(float value)
    {
 const InputEventNotificationId* inputId =
            InputEventNotificationBus::GetCurrentBusId();
 if (inputId == nullptr)
        {
 return;
        }
 if (*inputId == RotateYawEventId)
        {
            m_yaw = value;
        }
    }
 void ChickenControllerComponent::OnTick(float,
                                            AZ::ScriptTimePoint)
    {
 const ChickenInput input = CreateInput();
        ProcessInput(input);
    }
    ChickenInput ChickenControllerComponent::CreateInput()
    {
        ChickenInput input;
        input.m_forwardAxis = m_forward;
        input.m_strafeAxis = m_strafe;
        input.m_viewYaw = m_yaw;
 return input;
    }
 void ChickenControllerComponent::UpdateRotation(
 const ChickenInput& input)
    {
        AZ::TransformInterface* t = GetEntity()->GetTransform();
 float currentHeading = t->GetWorldRotationQuaternion().
            GetEulerRadians().GetZ();
            currentHeading += input.m_viewYaw * m_turnSpeed;
        AZ::Quaternion q =
            AZ::Quaternion::CreateRotationZ(currentHeading);
        t->SetWorldRotationQuaternion(q);
    }
 void ChickenControllerComponent::UpdateVelocity(
 const ChickenInput& input)
    {
 const float currentHeading = GetEntity()->GetTransform()->
            GetWorldRotationQuaternion().GetEulerRadians().GetZ();
 const AZ::Vector3 fwd = AZ::Vector3::CreateAxisY(
            input.m_forwardAxis);
 const AZ::Vector3 strafe = AZ::Vector3::CreateAxisX(
            input.m_strafeAxis);
 const AZ::Vector3 combined = (fwd + strafe).GetNormalized();
        m_velocity = AZ::Quaternion::CreateRotationZ(currentHeading).
            TransformVector(combined) * m_speed;
    }
 void ChickenControllerComponent::ProcessInput(
 const ChickenInput& input)
    {
        UpdateRotation(input);
        UpdateVelocity(input);
        Physics::CharacterRequestBus::Event(GetEntityId(),
            &Physics::CharacterRequestBus::Events::AddVelocity,
                m_velocity);
    }
 } // namespace MyGem