Paso 3: Escribir un nuevo Modo de componente. - LumberyardGuía de usuario de

Las traducciones son generadas a través de traducción automática. En caso de conflicto entre la traducción y la version original de inglés, prevalecerá la version en inglés.

Paso 3: Escribir un nuevo Modo de componente.

Ahora que ha actualizado el encabezado y el archivo de implementación, este procedimiento le muestra cómo escribir un nuevo Modo de componente. Para ello, debe crear un archivo EditorPointLightComponentMode.h, que es la interfaz para Modo de componente.

Para escribir un nuevo Modo de componente

  1. Vaya al directorio lumberyard_version\dev\Gems\LmbrCentral\Code\Source\Rendering.

  2. En un editor de texto, cree un archivo y asígnele el nombre EditorPointLightComponentMode.h.

  3. Introduzca el código siguiente. Esto incluye el archivo EditorBaseComponentMode.h para que puede heredar del EditorBaseComponentMode.

    #include <AzToolsFramework/ComponentMode/EditorBaseComponentMode.h>
  4. Asegúrese de que todos los Modos de componente deben heredar de EditorBaseComponentMode.

    : public AzToolsFramework::ComponentModeFramework::EditorBaseComponentMode
  5. Para EditorBaseComponentMode, introduzca el siguiente código para anular la función Refresh.

    // EditorBaseComponentMode void Refresh() override;

    Se llama a la función Refresh después de alguna acción deshacer o rehacer para garantizar que se actualice un Modo de componente para reflejar el estado actual del componente. Por ejemplo, las posiciones del manipulador deben actualizarse después de deshacer o rehacer una acción.

    nota

    La interfaz EditorBaseComponentMode está diseñada para ser lo más ligera posible. Registre solo las partes que necesita.

  6. Declare el manipulador para modificar una propiedad específica en el componente. En el ejemplo se utiliza LinearManipulator, de forma que puede ajustar un punto a lo largo de un eje determinado.

    AZStd::shared_ptr<AzToolsFramework::LinearManipulator> m_pointMaxDistanceManipulator; ///< Manipulator for point max distance property.
    nota
    • Debe utilizar un AZStd::shared_ptr para administrar la vida útil de la propiedad LinearManipulator, que requiere ManipulatorManager.

    • En este procedimiento, se utiliza solo la propiedad LinearManipulator, pero hay otras propiedades disponibles. El PlanarManipulator permite dos grados de libertad para editar un valor y AngularManipulator puede rotar un valor. También puede utilizar manipuladores de agregación como TranslationManipulators y RotationManipulators. También puede crear y ampliar sus propios manipuladores heredándolos de BaseManipulator. Sin embargo, este es un tema avanzado y no se recomienda, ya que puede conseguir la mayor parte de la funcionalidad personalizando el comportamiento en las devoluciones de llamada de manipulador existentes.

  7. Guarde el archivo .

    ejemplo EditorPointLightComponentMode.h

    El código debería tener el siguiente aspecto. Tenga en cuenta la interfaz ligera.

    #pragma once #include <AzToolsFramework/ComponentMode/EditorBaseComponentMode.h> namespace AzToolsFramework { class LinearManipulator; } namespace LmbrCentral { class EditorPointLightComponentMode : public AzToolsFramework::ComponentModeFramework::EditorBaseComponentMode { public: EditorPointLightComponentMode( const AZ::EntityComponentIdPair& entityComponentIdPair, AZ::Uuid componentType); ~EditorPointLightComponentMode(); // EditorBaseComponentMode void Refresh() override; private: AZStd::shared_ptr<AzToolsFramework::LinearManipulator> m_pointMaxDistanceManipulator; /// Manipulator for point max distance property. }; } // namespace LmbrCentral

Implemente Modo de componente

Ahora que ha escrito la parte de interfaz de un Modo de componente, cree un archivo EditorPointLightComponentMode.cpp. Este archivo implementa un Modo de componente para el componente Point Light (Luz puntual).

En este procedimiento, realice los siguientes cambios en el archivo:

ejemplo EditorPointLightComponentMode.cpp

Después de completar este procedimiento, el archivo EditorPointLightComponentMode.cpp tiene un aspecto similar al siguiente.

#include "LmbrCentral_precompiled.h" #include "EditorPointLightComponentMode.h" #include <AzCore/Component/TransformBus.h> #include <AzToolsFramework/Manipulators/LinearManipulator.h> #include <AzToolsFramework/Manipulators/ManipulatorManager.h> #include <LmbrCentral/Rendering/EditorLightComponentBus.h> namespace LmbrCentral { EditorPointLightComponentMode::EditorPointLightComponentMode( const AZ::EntityComponentIdPair& entityComponentIdPair, AZ::Uuid componentType) : EditorBaseComponentMode(entityComponentIdPair, componentType) { AZ::Transform worldFromLocal = AZ::Transform::CreateIdentity(); AZ::TransformBus::EventResult( worldFromLocal, GetEntityId(), &AZ::TransformInterface::GetWorldTM); m_pointMaxDistanceManipulator = AzToolsFramework::LinearManipulator::MakeShared>(worldFromLocal); m_pointMaxDistanceManipulator->AddEntityId(GetEntityId()); m_pointMaxDistanceManipulator->SetAxis(AZ::Vector3::CreateAxisX()); Refresh(); const AZ::Color manipulatorColor(0.3f, 0.3f, 0.3f, 1.0f); const float manipulatorSize = 0.05f; AzToolsFramework::ManipulatorViews views; views.emplace_back(AzToolsFramework::CreateManipulatorViewQuadBillboard(manipulatorColor, manipulatorSize)); m_pointMaxDistanceManipulator->SetViews(AZStd::move(views)); struct SharedState { float m_startingPointMaxDistance = 0.0f; }; auto sharedState = AZStd::make_shared<SharedState>(); m_pointMaxDistanceManipulator->InstallLeftMouseDownCallback( [this, sharedState] (const AzToolsFramework::LinearManipulator::Action& /*action*/) mutable { float currentMaxDistance = 0.0f; EditorLightComponentRequestBus::EventResult( currentMaxDistance, GetEntityId(), &EditorLightComponentRequests::GetPointMaxDistance); sharedState->m_startingPointMaxDistance = currentMaxDistance; }); m_pointMaxDistanceManipulator->InstallMouseMoveCallback( [this, sharedState](const AzToolsFramework::LinearManipulator::Action& action) { const AZ::VectorFloat axisDisplacement = action.LocalPositionOffset().Dot(action.m_fixed.m_axis); EditorLightComponentRequestBus::Event( GetEntityId(), &EditorLightComponentRequests::SetPointMaxDistance, (sharedState->m_startingPointMaxDistance + axisDisplacement).GetMax(AZ::VectorFloat(0.1f))); const AZ::Vector3 localPosition = action.LocalPosition().GetMax(AZ::Vector3(0.1f, 0.0f, 0.0f)); m_pointMaxDistanceManipulator->SetLocalTransform(AZ::Transform::CreateTranslation(localPosition)); m_pointMaxDistanceManipulator->SetBoundsDirty(); // ensure property grid values are refreshed AzToolsFramework::ToolsApplicationNotificationBus::Broadcast( &AzToolsFramework::ToolsApplicationNotificationBus::Events::InvalidatePropertyDisplay, AzToolsFramework::Refresh_Values); }); m_pointMaxDistanceManipulator->Register(AzToolsFramework::g_mainManipulatorManagerId); } EditorPointLightComponentMode::~EditorPointLightComponentMode() { m_pointMaxDistanceManipulator->Unregister(); } void EditorPointLightComponentMode::Refresh() { float currentMaxDistance = 0.0f; EditorLightComponentRequestBus::EventResult( currentMaxDistance, GetEntityId(), &EditorLightComponentRequests::GetPointMaxDistance); m_pointMaxDistanceManipulator->SetLocalTransform( AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisX() * currentMaxDistance)); } } // namespace LmbrCentral
nota

La mayor parte del código en el archivo está relacionada con el manipulador. Los manipuladores son de bajo nivel pero ofrecen un elevado grado de control.

Construction

El constructor de Modo de componente contiene la mayor parte de la lógica.

Para construir un Modo de componente

  1. Vaya al directorio lumberyard_version\dev\Gems\LmbrCentral\Code\Source\Rendering.

  2. En un editor de texto, cree un archivo y asígnele el nombre EditorPointLightComponentMode.cpp.

  3. Llame al constructor EditorBaseComponentMode y especifique el Entity y ComponentId, junto con el componentType.

    EditorPointLightComponentMode::EditorPointLightComponentMode( const AZ::EntityComponentIdPair& entityComponentIdPair, AZ::Uuid componentType) : EditorBaseComponentMode(entityComponentIdPair, componentType) // IMPORTANT

Configuración de manipulador

A continuación, configure el manipulador para el componente.

Para configurar el manipulador

  1. En el archivo EditorPointLightComponentMode.cpp, identifique LinearManipulator en el código.

  2. Solicite la transformación del mundo de la entidad a la que está asociada el componente y transfiera dicho valor al constructor del manipulador. Debe definir el espacio donde va a operar el manipulador. Con los componentes, este valor suele ser la transformación de entidad. Si desea que el manipulador opere en el espacio del mundo, puede transferir aquí la transformación de identidad.

    AZ::Transform worldFromLocal = AZ::Transform::CreateIdentity(); AZ::TransformBus::EventResult( worldFromLocal, GetEntityId(), &AZ::TransformInterface::GetWorldTM); m_pointMaxDistanceManipulator = AzToolsFramework::LinearManipulator::MakeShared>(worldFromLocal);
    nota

    La nomenclatura worldFromLocal se elige para indicar cómo esta transformación está modificando una posición. Por ejemplo, si tiene una posición en el espacio local de la entidad, esta transformación la toma de espacio del mundo local. El estilo de nomenclatura ayuda a depurar la orden de multiplicación de las transformaciones y vectores. Lumberyard utiliza un orden principal en columnas, que es una multiplicación de matriz que se produce de derecha a izquierda.

    Por ejemplo, si tiene el vector localPosition y la transformación worldFromLocal, la multiplicación de worldFromLocal * localPosition tiene la salida correcta, ya que los identificadores local están uno junto al otro. Esto transforma la localPosition a su posición en el espacio del mundo.

  3. (Opcional) Añada EntityId al manipulador. Esto resulta útil para realizar un seguimiento de las operaciones de hacer y deshacer del manipulador en las entidades.

    Durante cada movimiento del ratón, los EntityIds añadidos se marcan como sucios. Cuando una acción de manipulador finaliza, Lumberyard compara la entidad y estado serializado del componente antes y después del evento. Si la entidad ha cambiado, Lumberyard registra un paso deshacer. En caso contrario, Lumberyard descarta la acción deshacer posible. Es importante tener en cuenta que esto realiza un seguimiento del cambio que el manipulador provocó en el estado de entidad serializado. Si tiene otras operaciones personalizadas que desea deshacer, cree un nuevo UndoCommand que se obtiene de URSequencePoint.

    m_pointMaxDistanceManipulator->AddEntityId(GetEntityId());
  4. Para la función SetAxis, especifique un vector en el espacio local de la entidad. Esto define el vector que LinearManipulator desplaza en el espacio local. En el siguiente ejemplo se utiliza el eje X, pero puede especificar otro vector.

    m_pointMaxDistanceManipulator->SetAxis(AZ::Vector3::CreateAxisX());
  5. Para establecer la posición del manipulador, consulte el EditorLightComponent. No tiene una referencia directa (puntero) al componente o entidad. Todas las comunicaciones se realizan mediante EBuses.

    // From void EditorPointLightComponentMode::Refresh() float currentMaxDistance = 0.0f; EditorLightComponentRequestBus::EventResult( currentMaxDistance, GetEntityId(), &EditorLightComponentRequests::GetPointMaxDistance);
    nota

    El uso de EBuses y EntityIds ofrece las siguientes ventajas:

    • No es necesario acoplar un Modo de componente a un componente específico. Por ejemplo, los componentes Box Shape (Forma de cubo) y PhysX Collider (Colisionador) necesitan capacidades de edición similares, como la capacidad de cambiar las dimensiones de un cuadro delimitador orientado en la ventanilla. Los archivos EditorBoxShapeComponent.h y EditorPhysXColliderComponent.h incluyen el BoxManipulatorRequestBus. Esto proporciona una interfaz para obtener la transformación de forma o colisionador y obtener o establecer sus dimensiones. De esta manera, puede aplicar el BoxComponentMode a ambos archivos.

    • Puede evitar problemas con las entidades que se destruyen y vuelven a crear con cada acción deshacer y rehacer. Si una entidad cambia mientras se registra una acción deshacer, el acto de deshacer la acción destruye la entidad actual y vuelve a crearla devolviendo la entidad a su estado guardado anterior. Si Modo de componente tiene una referencia directa al EditorComponent y no solo un EntityId, la administración de la vida útil sería más compleja. Esto significa que el EditorComponent que desea editar debe exponer las acciones GET y SET que necesita en su bus de solicitud. De lo contrario, Modo de componente no puede leer o escribir las acciones.

  6. La llamada a SetLocalTransform establece la transformación del manipulador. De forma predeterminada, este valor es el mismo espacio local de la entidad. Para calcular este valor, consulte el valor PointMaxDistance actual y desplace el controlador del manipulador esa distancia en el eje X.

    En el siguiente ejemplo, ManipulatorView no tiene una orientación. Puede especificar CreateTranslation en la clase Transform.

    // From void EditorPointLightComponentMode::Refresh() m_pointMaxDistanceManipulator->SetLocalTransform( AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisX() * currentMaxDistance));
  7. Configure la ManipulatorView.

    El comportamiento de un manipulador se desacopla de la forma en la que aparece en la ventanilla. Esto significa que un LinearManipulator puede tener el aspecto de una línea, cono, cubo o cuadrángulo alineado en pantalla. LinearManipulator admite varias vistas, lo que resulta útil con el TranslationManipulator clásico. Puede dibujar una línea y un cono (una flecha) para representar el LinearManipulator que corresponde a cada eje.

  8. Cree un AZStd::vector de vistas y una QuadBillboardView que especifica el color y dimensiones de la forma.

  9. Añada las nuevas views al propio manipulador con SetViews.

    ejemplo

    const AZ::Color manipulatorColor(0.3f, 0.3f, 0.3f, 1.0f); const float manipulatorSize = 0.05f; AzToolsFramework::ManipulatorViews views; views.emplace_back(AzToolsFramework::CreateManipulatorViewQuadBillboard(manipulatorColor, manipulatorSize)); m_pointMaxDistanceManipulator->SetViews(AZStd::move(views));
  10. Guarde el archivo.

    ejemplo

    El código debe tener hasta el momento un aspecto similar al siguiente.

    // EditorPointLightComponentMode::EditorPointLightComponentMode() AZ::Transform worldFromLocal = AZ::Transform::CreateIdentity(); AZ::TransformBus::EventResult( worldFromLocal, GetEntityId(), &AZ::TransformInterface::GetWorldTM); m_pointMaxDistanceManipulator = AzToolsFramework::LinearManipulator::MakeShared>(worldFromLocal); m_pointMaxDistanceManipulator->AddEntityId(GetEntityId()); m_pointMaxDistanceManipulator->SetAxis(AZ::Vector3::CreateAxisX()); // Refresh(); inlined/expanded float currentMaxDistance = 0.0f; EditorLightComponentRequestBus::EventResult( currentMaxDistance, GetEntityId(), &EditorLightComponentRequests::GetPointMaxDistance); m_pointMaxDistanceManipulator->SetLocalTransform( AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisX() * currentMaxDistance)); const AZ::Color manipulatorColor(0.3f, 0.3f, 0.3f, 1.0f); const float manipulatorSize = 0.05f; AzToolsFramework::ManipulatorViews views; views.emplace_back(AzToolsFramework::CreateManipulatorViewQuadBillboard(manipulatorColor, manipulatorSize)); m_pointMaxDistanceManipulator->SetViews(AZStd::move(views));

Devoluciones de llamadas de manipulador

A continuación, configure la forma en que debería responder el manipulador al interactuar con él en la ventanilla.

Para configurar devoluciones de llamada de manipulador

  1. En el archivo EditorPointLightComponentMode.cpp, introduzca el siguiente código para crear un fragmento de estado compartido que pueda utilizar cada devolución de llamada.

    struct SharedState { float m_startingPointMaxDistance = 0.0f; }; auto sharedState = AZStd::make_shared<SharedState>();
    nota

    Puede añadir un miembro al EditorPointLightComponentMode y hacer referencia a ello en cada expresión lambda. Sin embargo, dado que solo las expresiones lambda se preocupan de este estado, mantenga su ámbito tan limitado como sea posible.

  2. Utilice AZStd::shared_ptr para asegurarse de que las expresiones lambda capture en puntero por valor. De este modo se garantiza que las expresiones lambda posean el estado compartido y lo cierren de forma eficaz. Esto es similar a un cierre en JavaScript.

    m_pointMaxDistanceManipulator->InstallLeftMouseDownCallback( [this, sharedState] (const AzToolsFramework::LinearManipulator::Action& /*action*/) mutable { float currentMaxDistance = 0.0f; EditorLightComponentRequestBus::EventResult( currentMaxDistance, GetEntityId(), &EditorLightComponentRequests::GetPointMaxDistance); sharedState->m_startingPointMaxDistance = currentMaxDistance; });
  3. Haga referencia a los datos contenidos en EditorPointLightComponentMode. Esto proporciona acceso a la EntityId para que pueda capturar el puntero this.

    m_pointMaxDistanceManipulator->InstallMouseMoveCallback( [this, sharedState](const AzToolsFramework::LinearManipulator::Action& action) { const AZ::VectorFloat axisDisplacement = action.LocalPositionOffset().Dot(action.m_fixed.m_axis); EditorLightComponentRequestBus::Event( GetEntityId(), &EditorLightComponentRequests::SetPointMaxDistance, (sharedState->m_startingPointMaxDistance + axisDisplacement).GetMax(AZ::VectorFloat(0.1f))); const AZ::Vector3 localPosition = action.LocalPosition().GetMax(AZ::Vector3(0.1f, 0.0f, 0.0f)); m_pointMaxDistanceManipulator->SetLocalTransform(AZ::Transform::CreateTranslation(localPosition)); m_pointMaxDistanceManipulator->SetBoundsDirty(); // ensure property grid values are refreshed AzToolsFramework::ToolsApplicationNotificationBus::Broadcast( &AzToolsFramework::ToolsApplicationNotificationBus::Events::InvalidatePropertyDisplay, AzToolsFramework::Refresh_Values); });

    La devolución de llamada que obtiene de LinearManipulator pasa una estructura denominada Action. Esta estructura contiene información sobre el estado actual del manipulador.

  4. Para determinar cuánto puede desplazar en el eje, añada un .Dot entre LocalPositionOffset y el eje fijo del manipulador. Esto le ofrece una proyección del LocalPositionOffset en el eje y la distancia desplazada.

    const AZ::VectorFloat axisDisplacement = action.LocalPositionOffset().Dot(action.m_fixed.m_axis);

    La acción del manipulador tiene tres partes. Puede especificar estas propiedades para controlar cómo deben modificar el componente.

    • Fixed – Contiene datos asociados con el manipulador, que se establece en el momento de su creación. A menudo, este es el eje o plano de movimiento.

    • Start: estado del manipulador en –.MouseDown

    • Current: – estado actual del manipulador durante un MouseMove.

  5. Después de calcular el axisDisplacement, especifique dicho valor para establecer la PointMaxDistance actual, que actualiza el estado del componente. Debe actualizar la LocalTransform del manipulador. Si no lo hace, la representación visual del manipulador no cambia. Para ello, lea la LocalPosition de la acción y establezca la LocalTransform del manipulador.

    const AZ::Vector3 localPosition = action.LocalPosition().GetMax(AZ::Vector3(0.1f, 0.0f, 0.0f)); m_pointMaxDistanceManipulator->SetLocalTransform(AZ::Transform::CreateTranslation(localPosition));
  6. Después de actualizar la LocalTransform del manipulador, debe marcar sus límites como sucios de modo que se puedan volver a calcular para pruebas de intersección.

    m_pointMaxDistanceManipulator->SetBoundsDirty();
  7. Introduzca el siguiente código para notificar al editor que ha modificado una propiedad que se tiene que actualizar en el Entity Inspector. Si no realiza esta actualización, las propiedades del componente en el Entity Inspector no coinciden con el modo en que el componente aparece en la ventanilla.

    AzToolsFramework::ToolsApplicationNotificationBus::Broadcast( &AzToolsFramework::ToolsApplicationNotificationBus::Events::InvalidatePropertyDisplay, AzToolsFramework::Refresh_Values);
  8. Registre el manipulador con el ManipulatorManager principal. Esto garantiza que este manipulador esté asociado a la ventanilla principal.

    m_pointMaxDistanceManipulator->Register(AzToolsFramework::g_mainManipulatorManagerId);
  9. Debe anular el registro del manipulador del ManipulatorManager cuando se destruye. Para ello, agregue esta función a su destructor de Modo de componente.

    EditorPointLightComponentMode::~EditorPointLightComponentMode() { m_pointMaxDistanceManipulator->Unregister(); }

    ejemplo

    En el siguiente código, anule las devoluciones de llamada InstallLeftMouseDownCallback y InstallMouseMoveCallback para conseguir el comportamiento preferido. Este código muestra la lógica de la devolución de llamada.

    struct SharedState { float m_startingPointMaxDistance = 0.0f; }; auto sharedState = AZStd::make_shared<SharedState>(); m_pointMaxDistanceManipulator->InstallLeftMouseDownCallback( [this, sharedState] (const AzToolsFramework::LinearManipulator::Action& /*action*/) mutable { float currentMaxDistance = 0.0f; EditorLightComponentRequestBus::EventResult( currentMaxDistance, GetEntityId(), &EditorLightComponentRequests::GetPointMaxDistance); sharedState->m_startingPointMaxDistance = currentMaxDistance; }); m_pointMaxDistanceManipulator->InstallMouseMoveCallback( [this, sharedState](const AzToolsFramework::LinearManipulator::Action& action) { const AZ::VectorFloat axisDisplacement = action.LocalPositionOffset().Dot(action.m_fixed.m_axis); EditorLightComponentRequestBus::Event( GetEntityId(), &EditorLightComponentRequests::SetPointMaxDistance, (sharedState->m_startingPointMaxDistance + axisDisplacement).GetMax(AZ::VectorFloat(0.1f))); const AZ::Vector3 localPosition = action.LocalPosition().GetMax(AZ::Vector3(AZ::VectorFloat(0.1f))); m_pointMaxDistanceManipulator->SetLocalTransform(AZ::Transform::CreateTranslation(localPosition)); m_pointMaxDistanceManipulator->SetBoundsDirty(); // ensure property grid values are refreshed AzToolsFramework::ToolsApplicationNotificationBus::Broadcast( &AzToolsFramework::ToolsApplicationNotificationBus::Events::InvalidatePropertyDisplay, AzToolsFramework::Refresh_Values); });
  10. Para ver un Modo de componente en acción, vuelva a EditorPointLightComponent.h y elimine las líneas de comentario que ha añadió a su código. Consulte EditorPointLightComponent.cpp.

    #include "EditorPointLightComponentMode" ... m_componentModeDelegate.ConnectWithSingleComponentMode< EditorPointLightComponent, EditorPointLightComponentMode>( AZ::EntityComponentIdPair(GetEntityId(), GetId()), nullptr);
  11. Guarde el archivo .