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
-
Vaya al directorio
.lumberyard_version
\dev\Gems\LmbrCentral\Code\Source\Rendering -
En un editor de texto, cree un archivo y asígnele el nombre
EditorPointLightComponentMode.h
. -
Introduzca el código siguiente. Esto incluye el archivo
EditorBaseComponentMode.h
para que puede heredar delEditorBaseComponentMode
.#include <AzToolsFramework/ComponentMode/EditorBaseComponentMode.h>
-
Asegúrese de que todos los Modos de componente deben heredar de
EditorBaseComponentMode
.: public AzToolsFramework::ComponentModeFramework::EditorBaseComponentMode
-
Para
EditorBaseComponentMode
, introduzca el siguiente código para anular la funciónRefresh
.// 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. -
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 propiedadLinearManipulator
, que requiereManipulatorManager
. -
En este procedimiento, se utiliza solo la propiedad
LinearManipulator
, pero hay otras propiedades disponibles. ElPlanarManipulator
permite dos grados de libertad para editar un valor yAngularManipulator
puede rotar un valor. También puede utilizar manipuladores de agregación comoTranslationManipulators
yRotationManipulators
. También puede crear y ampliar sus propios manipuladores heredándolos deBaseManipulator
. 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.
-
-
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
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
-
Vaya al directorio
.lumberyard_version
\dev\Gems\LmbrCentral\Code\Source\Rendering -
En un editor de texto, cree un archivo y asígnele el nombre
EditorPointLightComponentMode.cpp
. -
Llame al constructor
EditorBaseComponentMode
y especifique elEntity
yComponentId
, junto con elcomponentType
.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
-
En el archivo
EditorPointLightComponentMode.cpp
, identifiqueLinearManipulator
en el código. -
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ónworldFromLocal
, la multiplicación deworldFromLocal
*localPosition
tiene la salida correcta, ya que los identificadoreslocal
están uno junto al otro. Esto transforma lalocalPosition
a su posición en el espacio del mundo. -
(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 nuevoUndoCommand
que se obtiene deURSequencePoint
.m_pointMaxDistanceManipulator->AddEntityId(GetEntityId());
-
Para la función
SetAxis
, especifique un vector en el espacio local de la entidad. Esto define el vector queLinearManipulator
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());
-
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
yEditorPhysXColliderComponent.h
incluyen elBoxManipulatorRequestBus
. Esto proporciona una interfaz para obtener la transformación de forma o colisionador y obtener o establecer sus dimensiones. De esta manera, puede aplicar elBoxComponentMode
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 unEntityId
, la administración de la vida útil sería más compleja. Esto significa que elEditorComponent
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.
-
-
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 valorPointMaxDistance
actual y desplace el controlador del manipulador esa distancia en el eje X.En el siguiente ejemplo,
ManipulatorView
no tiene una orientación. Puede especificarCreateTranslation
en la claseTransform
.// From void EditorPointLightComponentMode::Refresh() m_pointMaxDistanceManipulator->SetLocalTransform( AZ::Transform::CreateTranslation(AZ::Vector3::CreateAxisX() * currentMaxDistance));
-
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 elTranslationManipulator
clásico. Puede dibujar una línea y un cono (una flecha) para representar elLinearManipulator
que corresponde a cada eje. -
Cree un
AZStd::vector
de vistas y unaQuadBillboardView
que especifica el color y dimensiones de la forma. -
Añada las nuevas
views
al propio manipulador conSetViews
.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));
-
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
-
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. -
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; });
-
Haga referencia a los datos contenidos en
EditorPointLightComponentMode
. Esto proporciona acceso a laEntityId
para que pueda capturar el punterothis
.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 denominadaAction
. Esta estructura contiene información sobre el estado actual del manipulador. -
Para determinar cuánto puede desplazar en el eje, añada un
.Dot
entreLocalPositionOffset
y el eje fijo del manipulador. Esto le ofrece una proyección delLocalPositionOffset
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
.
-
-
Después de calcular el
axisDisplacement
, especifique dicho valor para establecer laPointMaxDistance
actual, que actualiza el estado del componente. Debe actualizar laLocalTransform
del manipulador. Si no lo hace, la representación visual del manipulador no cambia. Para ello, lea laLocalPosition
de la acción y establezca laLocalTransform
del manipulador.const AZ::Vector3 localPosition = action.LocalPosition().GetMax(AZ::Vector3(0.1f, 0.0f, 0.0f)); m_pointMaxDistanceManipulator->SetLocalTransform(AZ::Transform::CreateTranslation(localPosition));
-
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();
-
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);
-
Registre el manipulador con el
ManipulatorManager
principal. Esto garantiza que este manipulador esté asociado a la ventanilla principal.m_pointMaxDistanceManipulator->Register(AzToolsFramework::g_mainManipulatorManagerId);
-
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
yInstallMouseMoveCallback
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); });
-
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);
-
Guarde el archivo .