Version:

本文内容

第7章 AZ::Event是什么

第7章 AZ::Event是什么

介绍

Note:
AZ::Event 的官方参考可以在这里找到: https://www.o3de.org/docs/user-guide/programming/az-event/

AZ::Event 是在组件之间实现通知系统的绝佳工具,其中一个组件充当事件的发布者,而其他组件充当订阅者。同一发布者上可以有许多订阅者。AZ::Event 不适合用作 getter 接口,但非常适合通知调用,例如通知任何侦听组件世界转换已发生更改的 Transform 组件。

以下是用于创建 AZ::Event 的 C++ 构建基块。

图 7.1.设置 AZ::Event

  1. 使用AZ::Event<>定义事件
   AZ::Event<int> myEvent;
  1. 使用带有回调的 AZ::Event<>::Handler 定义处理程序。
   AZ::Event<int>::Handler myHandler([](int value)
   {
      /*process*/
   });
  1. 将处理程序连接到事件。
   myHandler.Connect(myEvent);
  1. 从事件发送通知。
   myEvent.Signal(42);
Tip:
您可以在 AZ::Event 的模板参数中定义任何参数或多个参数。处理程序必须与这些类型匹配。Signal 也必须使用相同类型的参数。

例如,每当 TransformComponent 的实体移动或更改其旋转或缩放时,都会发出通知。它通过在 C:\git\o3de\Code\Framework\AzCore\AzCore\Component\TransformBus.h 中定义以下事件来实现此目的:

using TransformChangedEvent = AZ::Event<const Transform&, const Transform&>;

必须有一种方法可以将处理程序连接到此事件,这是通过调用 TransformComponent::BindTransformChangedEventHandler 来完成的:

void TransformComponent::BindTransformChangedEventHandler(AZ::TransformChangedEvent::Handler& handler)
{
    handler.Connect(m_transformChangedEvent);
}

如果你查看 TransformComponent.cpp 的代码,你会发现它在适当的时间向事件发出信号:

m_transformChangedEvent.Signal(m_localTM, m_worldTM);

了解了介绍性细节后,我们可以构建一个新组件,每当实体移动时,该组件都会打印调试消息。这里有四个步骤。

  1. 声明正确的处理程序类型,并选择性地声明通知的回调方法。
   AZ::TransformChangedEvent::Handler m_movementHandler;
   void OnWorldTransformChanged(const AZ::Transform& world);
  1. 在组件的构造函数中,定义调用回调方法 OnWorldTransformChanged 的 lambda。
MyEventComponent::MyEventComponent()
    : m_movementHandler(
        [this](
            const AZ::Transform& /*local*/,
            const AZ::Transform& world)
        {
            OnWorldTransformChanged(world);
        })
{
}

图 7.2. 使用 AZ::Event的TransformComponent通知

  1. 实现 callback 方法以打印消息。
void MyEventComponent::OnWorldTransformChanged(const AZ::Transform& world)
{
    AZ_Printf("MyEvent", "now at %f %f %f",
        world.GetTranslation().GetX(),
        world.GetTranslation().GetY(),
        world.GetTranslation().GetZ());
}
Note:
也可以只在 lambda 中完成所有工作。
  1. 最后,处理程序需要通过 BindTransformChangedEventHandler 连接到事件。
void MyEventComponent::Activate()
{
    AZ::Entity* e = GetEntity();

    using namespace AzFramework;
    TransformComponent* tc = e->FindComponent<TransformComponent>();
    // track the movement of the entity
    tc->BindTransformChangedEventHandler(m_movementHandler);
}
Tip:
不同的组件可能会提供不同的方法来将处理程序连接到其事件,但方法名称通常具有相似的名称架构:BindSomethingEventHandler。

小结

Note:
您可以在 GitHub 上找到本章的代码和项目更改: https://github.com/AMZN-Olex/O3DEBookCode2111/tree/ch06_az_interface

以下是新组件 MyEventComponent 的完整源代码。

例 7.1. MyEventComponent.h

#pragma once
#include <AzCore/Component/Component.h>
#include <AzCore/Component/TransformBus.h>

namespace MyProject
{
    // An example of listening to movement events
    // of TransformComponent using an AZ::Event
    class MyEventComponent : public AZ::Component
    {
    public:
        AZ_COMPONENT(MyEventComponent,
            "{934BF061-204B-4695-944A-23A1BA7433CB}");

        static void GetRequiredServices(
            AZ::ComponentDescriptor::DependencyArrayType& required)
        {
            required.push_back(AZ_CRC_CE("TransformService"));
        }

        MyEventComponent();

        // AZ::Component overrides
        void Activate() override;
        void Deactivate() override {}

        // Provide runtime reflection, if any
        static void Reflect(AZ::ReflectContext* rc);

    private:
        AZ::TransformChangedEvent::Handler m_movementHandler;
        void OnWorldTransformChanged(const AZ::Transform& world);
    };
}

例 7.2. MyEventComponent.cpp

#include "MyEventComponent.h"
#include <AzCore/Component/Entity.h>
#include <AzCore/Serialization/EditContext.h>
#include <AzFramework/Components/TransformComponent.h>

using namespace MyProject;

MyEventComponent::MyEventComponent()
    : m_movementHandler(
        [this](
            const AZ::Transform& /*local*/,
            const AZ::Transform& world)
        {
            OnWorldTransformChanged(world);
        })
{
}

void MyEventComponent::OnWorldTransformChanged(const AZ::Transform& world)
{
    AZ_Printf("MyEvent", "now at %f %f %f",
        world.GetTranslation().GetX(),
        world.GetTranslation().GetY(),
        world.GetTranslation().GetZ());
}

void MyEventComponent::Activate()
{
    AZ::Entity* e = GetEntity();

    using namespace AzFramework;
    TransformComponent* tc = e->FindComponent<TransformComponent>();
    // track the movement of the entity
    tc->BindTransformChangedEventHandler(m_movementHandler);
}

void MyEventComponent::Reflect(AZ::ReflectContext* rc)
{
    auto sc = azrtti_cast<AZ::SerializeContext*>(rc);
    if (!sc) return;

    sc->Class<MyEventComponent, Component>()
        ->Version(1);

    AZ::EditContext* ec = sc->GetEditContext();
    if (!ec) return;

    using namespace AZ::Edit::Attributes;
    // reflection of this component for O3DE Editor
    ec->Class<MyEventComponent>("My Event Component Example",
        "[Communicates using AZ::Event]")
      ->ClassElement(AZ::Edit::ClassElements::EditorData, "")
        ->Attribute(AppearsInAddComponentMenu, AZ_CRC("Game"))
        ->Attribute(Category, "My Project");
}