Dans Unreal Engine 4, il est possible de visualiser des informations de débogage via des Gizmos dans l'éditeur, une fonctionnalité similaire aux Gizmos d'Unity. Cet article décrit une méthode simplifiée pour implémenter cela en utilisant des composants personnalisés, testée sur Unreal Engine 4.27.
Le processus implique la création d'un composant héritant de UPrimitiveComponent, qui redéfinit la méthode CreateSceneProxy pour effectuer le dessin via l'interface PDI. Ensuite, ce compsoant est attaché manuellement à un Actor, et une logique d'attachement automatique peut être ajoutée dans le constructeur de l'Actor pour une utilisation pratique.
Voici les étapes détaillées :
Premièrement, créer un composant nommé ici UVisualDebugComponent qui hérite de UPrimitiveComponent. Ce composant doit redéfinit CreateSceneProxy pour accéder à l'interface de dessin et CalcBounds pour définir les limites de rendu.
Fichier d'en-tête (DebugVisualComponent.h) :
// DebugVisualComponent.h
#pragma once
#include "CoreMinimal.h"
#include "Components/PrimitiveComponent.h"
#include "DebugVisualComponent.generated.h"
UCLASS(ClassGroup=(Custom), meta=(BlueprintSpawnableComponent))
class MONPROJET_API UVisualDebugComponent : public UPrimitiveComponent
{
GENERATED_BODY()
public:
virtual FPrimitiveSceneProxy* CreateSceneProxy() override;
virtual FBoxSphereBounds CalcBounds(const FTransform& LocalToWorld) const override;
};
Fichier d'implémentation (DebugVisualComponent.cpp) :
// DebugVisualComponent.cpp
#include "DebugVisualComponent.h"
#include "PrimitiveDrawingUtils.h"
FPrimitiveSceneProxy* UVisualDebugComponent::CreateSceneProxy()
{
class FDebugSceneProxy : public FPrimitiveSceneProxy
{
public:
SIZE_T GetTypeHash() const override
{
static size_t UniqueIdentifier;
return reinterpret_cast<size_t>(&UniqueIdentifier);
}
FDebugSceneProxy(const UPrimitiveComponent* InOwnerComponent) : FPrimitiveSceneProxy(InOwnerComponent)
{
CachedOwner = InOwnerComponent;
bWillEverBeLit = false;
}
virtual void GetDynamicMeshElements(const TArray<const FSceneView*>& Views, const FSceneViewFamily& ViewFamily, uint32 VisibilityMap, FMeshElementCollector& Collector) const override
{
QUICK_SCOPE_CYCLE_COUNTER(STAT_DebugVisualProxy_GetDynamicMeshElements);
for (int32 Index = 0; Index < Views.Num(); ++Index)
{
if (VisibilityMap & (1 << Index))
{
const FSceneView* CurrentView = Views[Index];
FPrimitiveDrawInterface* DrawInterface = Collector.GetPDI(Index);
// Obtenir la matrice de transformation de l'Actor parent pour le dessin
const AActor* ParentActor = CachedOwner->GetTypedOuter<AActor>();
if (ParentActor)
{
const FMatrix& ActorToWorld = ParentActor->GetTransform().ToMatrixWithScale();
// Dessiner une boîte filaire orientée
DrawOrientedWireBox(DrawInterface,
ActorToWorld.TransformPosition(FVector::ZeroVector),
ActorToWorld.GetScaledAxis(EAxis::X),
ActorToWorld.GetScaledAxis(EAxis::Y),
ActorToWorld.GetScaledAxis(EAxis::Z),
FVector(100.0f, 100.0f, 100.0f),
FLinearColor(0.0f, 1.0f, 0.0f, 1.0f),
SDPG_World,
2.0f);
}
}
}
}
virtual FPrimitiveViewRelevance GetViewRelevance(const FSceneView* View) const override
{
FPrimitiveViewRelevance Result;
Result.bDrawRelevance = true;
Result.bDynamicRelevance = true;
Result.bShadowRelevance = false;
Result.bEditorPrimitiveRelevance = true;
Result.bEditorNoDepthTestPrimitiveRelevance = true;
return Result;
}
virtual uint32 GetMemoryFootprint(void) const override { return sizeof(*this) + GetAllocatedSize(); }
uint32 GetAllocatedSize(void) const { return FPrimitiveSceneProxy::GetAllocatedSize(); }
private:
const UPrimitiveComponent* CachedOwner;
};
return new FDebugSceneProxy(this);
}
FBoxSphereBounds UVisualDebugComponent::CalcBounds(const FTransform& LocalToWorld) const
{
// Définir une boîte de délimitation fixe pour le rendu continu
return FBoxSphereBounds(FBox(FVector(-100, -100, -100), FVector(100, 100, 100))).TransformBy(LocalToWorld);
}
Pour que le composant soit utilisé, il doit être attaché à un Actor. On peut le faire manuellement dans l'éditeur ou automatiquement via le constructeur de l'Actor.
Exemple d'attachement automatique dans le constrcuteur d'un Actor (par exemple, ADebugActor) :
// Dans le fichier DebugActor.cpp
ADebugActor::ADebugActor()
{
UVisualDebugComponent* DebugGizmo = CreateDefaultSubobject<UVisualDebugComponent>(TEXT("DebugGizmo"));
if (DebugGizmo)
{
DebugGizmo->SetupAttachment(GetRootComponent());
}
}
Une fois le composant attaché, le Gizmo sera affiché dans la fenêtre de l'éditeur, permettant une visualisation directe pour le débogage.