UE4 繪制Gizmo


Unity的Gizmos可以很方便的在編輯器下進行調試,Unreal中也有一些辦法可以達到效果。

本文主要參考:https://zhuanlan.zhihu.com/p/363625037,進行了一些簡化。並在Unreal 4.27中實現。

 

具體流程如下:

  1. 需要繪制Gizmo的Actor掛載繼承UPrimitiveComponent的組件;該組件重寫了CreateSceneProxy方法,這個方法里可以拿到PDI繪制
  2. 然后進行這個組件的編寫(繼承UPrimitiveComponent實際上也繼承了USceneComponent),手動掛載到Actor上就可以繪制Gizmo了
  3. 再在Actor的構造函數中編寫自動掛載該組件的邏輯,方便使用

 

先來編寫繪制Gizmo的組件,這里命名為UMyPrimitiveComponent:

MyPrimitiveComponent.h (MYPROJECT_API注意替換)

//MyPrimitiveComponent.h

#pragma once

#include "CoreMinimal.h"
#include "Components/PrimitiveComponent.h"
#include "PrimitiveSceneProxy.h"
#include "MyPrimitiveComponent.generated.h"

UCLASS(ClassGroup = (Custom), meta = (BlueprintSpawnableComponent))
class MYPROJECT_API UMyPrimitiveComponent : public UPrimitiveComponent
{
    GENERATED_BODY()
    
public:
    //繪制邏輯主要在這里
    virtual FPrimitiveSceneProxy* CreateSceneProxy() override;

    //如果要在非選中情況下始終繪制的話,需要有Bounds信息,所以要重寫該函數
    virtual FBoxSphereBounds CalcBounds(const FTransform& LocalToWorld) const;
};

 

 

MyPrimitiveComponent.cpp

//MyPrimitiveComponent.cpp
#include "MyPrimitiveComponent.h"

FPrimitiveSceneProxy* UMyPrimitiveComponent::CreateSceneProxy()
{
    class FMySceneProxy : public FPrimitiveSceneProxy
    {
    public:

        SIZE_T GetTypeHash() const override
        {
            static size_t UniquePointer;
            return reinterpret_cast<size_t>(&UniquePointer);
        }

        FMySceneProxy(const UPrimitiveComponent* InComponent) : FPrimitiveSceneProxy(InComponent)
        {
            CacheInComponent = InComponent;
            bWillEverBeLit = false;
        }

        virtual void GetDynamicMeshElements(const TArray<const FSceneView*>& Views, const FSceneViewFamily& ViewFamily, uint32 VisibilityMap, FMeshElementCollector& Collector) const override
        {
            QUICK_SCOPE_CYCLE_COUNTER(STAT_Draw3DAgentSceneProxy_GetDynamicMeshElements);

            for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
            {
                if (VisibilityMap & (1 << ViewIndex))
                {
                    const FSceneView* View = Views[ViewIndex];
                    FPrimitiveDrawInterface* PDI = Collector.GetPDI(ViewIndex);

                    //拿到Actor的矩陣繪制,而不是Component自己的
                    const FMatrix& LocalToWorld = CacheInComponent->GetTypedOuter<AActor>()->GetTransform().ToMatrixWithScale();

                    //繪制函數可以去看下PrimitiveDrawingUtils.cpp
                    DrawOrientedWireBox(PDI
                        , LocalToWorld.TransformPosition(FVector::ZeroVector)
                        , LocalToWorld.GetScaledAxis(EAxis::X)
                        , LocalToWorld.GetScaledAxis(EAxis::Y)
                        , LocalToWorld.GetScaledAxis(EAxis::Z)
                        , FVector(100.0, 100.0, 100.0)
                        , FLinearColor(1.0, 0.0, 0.0, 1.0)
                        , SDPG_World
                        , 1.0);
                }
            }
        }
        virtual FPrimitiveViewRelevance GetViewRelevance(const FSceneView* View) const override
        {
            /*const bool bVisibleForSelection = IsSelected();
            const bool bShowForCollision = View->Family->EngineShowFlags.Collision && IsCollisionEnabled();

            FPrimitiveViewRelevance Result;
            Result.bDrawRelevance = (IsShown(View) && bVisibleForSelection) || bShowForCollision;
            Result.bDynamicRelevance = true;
            Result.bShadowRelevance = IsShadowCast(View);
            Result.bEditorPrimitiveRelevance = true;
            Result.bEditorNoDepthTestPrimitiveRelevance = true; */
            //上面這段表示選中繪制Gizmo

            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* CacheInComponent;
    };

    return new FMySceneProxy(this);
}

FBoxSphereBounds UMyPrimitiveComponent::CalcBounds(const FTransform& LocalToWorld) const
{
    return FBoxSphereBounds(FBox(FVector(-50, -50, -50), FVector(50, 50, 50))).TransformBy(LocalToWorld);
    //因為縮放是1,這里填的就是實際尺寸,也可以遍歷所有組件取最大Bounds.
}

 

 

 

然后手動再掛載一下組件,就有效果了:

 

 

如果需要像Unity那樣;直接就有Gizmo,可以在Actor構造函數中編寫邏輯自動掛載組件:

AMyTestActor::AMyTestActor()
{
    UBBoxActorGizmoComponent* Gizmo = CreateDefaultSubobject<UBBoxActorGizmoComponent>(TEXT("Gizmo"));
    if (Gizmo)
    {
        Gizmo->SetupAttachment(GetRootComponent());
    }
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM