UE4 component可视化

最新4.23更新一下 源码中VisualTarget.cpp中

需要额外再添加一个头文件#include "Editor/UnrealEdEngine.h"

否则会报GUnrealED为定义错误

最终效果 可以通过component中添加target点 动态调整点的位置 也可以在点上右键 进行复制 创建

下面的代码可能有省略 以最终链接中的工程为准

1.首先 创建一个editor module 这里不具体描述了创建过程了 参考上一篇教程

额外要注意的是 我们需要添加一些系统模块

		PrivateDependencyModuleNames.AddRange(new string[] { "Slate", "SlateCore", "EditorStyle", "UnrealEd" });

2.定义HTargetProxy 类 用来保存target点的信息  这里只保存了点的index

struct HTargetingVisProxy : public HComponentVisProxy
{
	DECLARE_HIT_PROXY();
	HTargetingVisProxy(const UActorComponent* InComponent)
		:HComponentVisProxy(InComponent, HPP_Wireframe)
	{}


};

struct HTargetProxy :public HTargetingVisProxy
{
	DECLARE_HIT_PROXY();

	HTargetProxy(const UActorComponent* Incomponent, int32 index)
		:HTargetingVisProxy(Incomponent), TargetIndex(index)
	{

	}
	int32 TargetIndex;
};

3.定义菜单命令

.h文件

class FTargetingVisualizerCommands :public TCommands<FTargetingVisualizerCommands>
{
public:
	FTargetingVisualizerCommands():TCommands<FTargetingVisualizerCommands>("TargetingComponent", FText::FromString(FString(TEXT("Visualizer"))), NAME_None, FEditorStyle::GetStyleSetName())
	{

	}

	virtual void RegisterCommands() override;

	TSharedPtr<FUICommandInfo> Duplicate;

};

.cpp

void FTargetingVisualizerCommands::RegisterCommands()
{
	UI_COMMAND(Duplicate, "Duplicate Target", "Duplicate the CurrentTarget", EUserInterfaceActionType::Button, FInputGesture());
}

4.定义component类

.h文件

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "Components/ActorComponent.h"
#include "TargetComponent.generated.h"


UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) )
class VISUALTARGET_API UTargetComponent : public USceneComponent
{
	GENERATED_BODY()

public:	
	// Sets default values for this component's properties
	UTargetComponent();

protected:
	// Called when the game starts
	virtual void BeginPlay() override;

public:	
	// Called every frame
	virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;
	void DeleteTarget(int32 deleteIndex); //用于执行删除target点操作

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Target)
		TArray<FVector> Targets; //用于保存target点
	
		

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Target)
		FLinearColor EditorSelectedColor; //选中点的颜色

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Target)
		FLinearColor UnSelectedColor;  //没有选中点的颜色
};


.cpp文件

// Fill out your copyright notice in the Description page of Project Settings.


#include "TargetComponent.h"

// Sets default values for this component's properties
UTargetComponent::UTargetComponent()
{
	// Set this component to be initialized when the game starts, and to be ticked every frame.  You can turn these features
	// off to improve performance if you don't need them.
	PrimaryComponentTick.bCanEverTick = true;

	// ...
	EditorSelectedColor = FLinearColor(1.0f, 0.0f, 0.0f, 1.0f);
	UnSelectedColor = FLinearColor(0.0f, 0.0f, 0.0f, 1.0f);
}


// Called when the game starts
void UTargetComponent::BeginPlay()
{
	Super::BeginPlay();

	// ...
	
}


// Called every frame
void UTargetComponent::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
{
	Super::TickComponent(DeltaTime, TickType, ThisTickFunction);

	// ...
}

void UTargetComponent::DeleteTarget(int32 deleteIndex)
{
	Targets.RemoveAt(deleteIndex);
}



5.定义ComponentVisualizer  注释写在代码里了

.h文件
// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved.

#pragma once

#include "CoreMinimal.h"
#include "InputCoreTypes.h"
#include "Components/ActorComponent.h"
#include "HitProxies.h"

class AActor;
class FCanvas;
class FEditorViewportClient;
class FPrimitiveDrawInterface;
class FSceneView;
class FViewport;
class SWidget;
struct FViewportClick;

struct HComponentVisProxy : public HHitProxy
{
	DECLARE_HIT_PROXY(UNREALED_API);

	HComponentVisProxy(const UActorComponent* InComponent, EHitProxyPriority InPriority = HPP_Wireframe) 
	: HHitProxy(InPriority)
	, Component(InComponent)
	{}

	virtual EMouseCursor::Type GetMouseCursor() override
	{
		return EMouseCursor::Crosshairs;
	}

	TWeakObjectPtr<const UActorComponent> Component;
};


/** Base class for a component visualizer, that draw editor information for a particular component class */
class UNREALED_API FComponentVisualizer : public TSharedFromThis<FComponentVisualizer>
{
public:
	FComponentVisualizer() {}
	virtual ~FComponentVisualizer() {}

	/** */
	virtual void OnRegister() {}
	/** Draw visualization for the supplied component */
	virtual void DrawVisualization(const UActorComponent* Component, const FSceneView* View, FPrimitiveDrawInterface* PDI) {}
	/** Draw HUD on viewport for the supplied component */
	virtual void DrawVisualizationHUD(const UActorComponent* Component, const FViewport* Viewport, const FSceneView* View, FCanvas* Canvas) {}
	/** */
	virtual bool VisProxyHandleClick(FEditorViewportClient* InViewportClient, HComponentVisProxy* VisProxy, const FViewportClick& Click) { return false; }
	/** */
	virtual void EndEditing() {}
	/** */
	virtual bool GetWidgetLocation(const FEditorViewportClient* ViewportClient, FVector& OutLocation) const { return false; }
	/** */
	virtual bool GetCustomInputCoordinateSystem(const FEditorViewportClient* ViewportClient, FMatrix& OutMatrix) const { return false; }
	/** */
	virtual bool HandleInputDelta(FEditorViewportClient* ViewportClient, FViewport* Viewport, FVector& DeltaTranslate, FRotator& DeltalRotate, FVector& DeltaScale) { return false; }
	/** */
	virtual bool HandleInputKey(FEditorViewportClient* ViewportClient,FViewport* Viewport,FKey Key,EInputEvent Event) { return false; }
	/** */
	virtual TSharedPtr<SWidget> GenerateContextMenu() const { return TSharedPtr<SWidget>(); }
	/** */
	virtual bool IsVisualizingArchetype() const { return false; }

	struct FPropertyNameAndIndex
	{
		FPropertyNameAndIndex()
			: Name(NAME_None)
			, Index(INDEX_NONE)
		{}

		explicit FPropertyNameAndIndex(FName InName, int32 InIndex = 0)
			: Name(InName)
			, Index(InIndex)
		{}

		bool IsValid() const { return Name != NAME_None && Index != INDEX_NONE; }

		void Clear()
		{
			Name = NAME_None;
			Index = INDEX_NONE;
		}

		FName Name;
		int32 Index;
	};

	/** Find the name of the property that points to this component */
	static FPropertyNameAndIndex GetComponentPropertyName(const UActorComponent* Component);

	/** Get a component pointer from the property name */
	static UActorComponent* GetComponentFromPropertyName(const AActor* CompOwner, const FPropertyNameAndIndex& Property);

	/** Notify that a component property has been modified */
	static void NotifyPropertyModified(UActorComponent* Component, UProperty* Property);

	/** Notify that many component properties have been modified */
	static void NotifyPropertiesModified(UActorComponent* Component, const TArray<UProperty*>& Properties);
};

struct FCachedComponentVisualizer
{
	TWeakObjectPtr<UActorComponent> Component;
	TSharedPtr<FComponentVisualizer> Visualizer;
	
	FCachedComponentVisualizer(UActorComponent* InComponent, TSharedPtr<FComponentVisualizer>& InVisualizer)
		: Component(InComponent)
		, Visualizer(InVisualizer)
	{}
};
// Fill out your copyright notice in the Description page of Project Settings.


#include "TargetingComponentVisualizer.h"

IMPLEMENT_HIT_PROXY(HTargetingVisProxy, HComponentVisProxy);
IMPLEMENT_HIT_PROXY(HTargetProxy, HTargetingVisProxy);

#define LOCTEXT_NAMESPACE "ComponentVisualizer"

void FTargetingComponentVisualizer::OnRegister()
{
	FTargetingVisualizerCommands::Register(); //创建菜单instance
	TargetingComponentVisualizerActions = MakeShareable(new FUICommandList());

	const auto& Commands = FTargetingVisualizerCommands::Get();
	TargetingComponentVisualizerActions->MapAction(Commands.Duplicate, FExecuteAction::CreateSP(this, &FTargetingComponentVisualizer::OnDuplicateTarget)); //绑定菜单项
}

//实时绘制target点 线
void FTargetingComponentVisualizer::DrawVisualization(const UActorComponent* Component, const FSceneView* View, FPrimitiveDrawInterface* PDI)
{
	if (const UTargetComponent* TargetingComponent = Cast<const UTargetComponent>(Component))
	{
		CurrentTargetComponent = const_cast<UTargetComponent*>(TargetingComponent);
		const FLinearColor SelectedColor = TargetingComponent->EditorSelectedColor;
		const FLinearColor UnSelectedColor = TargetingComponent->UnSelectedColor;

		const FVector Location = TargetingComponent->GetComponentLocation();

		for (int i = 0; i < TargetingComponent->Targets.Num(); i++)
		{
			FLinearColor Color = (i == SelectedTargetIndex) ? SelectedColor : UnSelectedColor;

			PDI->SetHitProxy(new HTargetProxy(Component, i));
			PDI->DrawLine(Location, TargetingComponent->Targets[i], Color, SDPG_Foreground);
			PDI->DrawPoint(TargetingComponent->Targets[i], Color, 20.0f, SDPG_Foreground);
			PDI->SetHitProxy(NULL);
		}
	}
}

//处理选中target点
bool FTargetingComponentVisualizer::VisProxyHandleClick(FEditorViewportClient* InViewportClient, HComponentVisProxy* VisProxy, const FViewportClick& Click)
{
	bool bEditing = false;
	if (VisProxy && VisProxy->Component.IsValid())
	{
		bEditing = true;
		if (VisProxy->IsA(HTargetProxy::StaticGetType()))
		{
			HTargetProxy* Proxy = (HTargetProxy*)VisProxy;
			SelectedTargetIndex = Proxy->TargetIndex;
		}
	}
	else
	{
		SelectedTargetIndex = INDEX_NONE;
	}
	
	return bEditing;
}

//实时获得选中的target位置
bool FTargetingComponentVisualizer::GetWidgetLocation(const FEditorViewportClient* ViewportClient, FVector& OutLocation) const
{
	if (IsValid(GetEditedTargetingComponent()) && SelectedTargetIndex != INDEX_NONE)
	{
		OutLocation = GetEditedTargetingComponent()->Targets[SelectedTargetIndex];
		return true;
	}
	return false;
}

//处理在editor中 旋转 缩放 等操作
bool FTargetingComponentVisualizer::HandleInputDelta(FEditorViewportClient* ViewportClient, FViewport* Viewport, FVector& DeltaTranslate, FRotator& DeltalRotate, FVector& DeltaScale)
{
	bool bHandled = false;
	if (IsValid(GetEditedTargetingComponent()) && SelectedTargetIndex != INDEX_NONE)
	{
		GetEditedTargetingComponent()->Targets[SelectedTargetIndex] += DeltaTranslate;
		bHandled = true;
	}
	return bHandled;
}

//处理在editor中快捷键操作(这里只添加了删除键的响应)
bool FTargetingComponentVisualizer::HandleInputKey(FEditorViewportClient* ViewportClient, FViewport* Viewport, FKey Key, EInputEvent Event)
{
	bool bHandled = false;
	if (Key == EKeys::Delete)
	{
		if (IsValid(GetEditedTargetingComponent()) && SelectedTargetIndex != INDEX_NONE)
		{
			GetEditedTargetingComponent()->DeleteTarget(SelectedTargetIndex);
			SelectedTargetIndex--;
			SelectedTargetIndex = FMath::Max<int32>(SelectedTargetIndex, 0);
			bHandled = true;
		}
	}
	
	return bHandled;
}

//右键创建菜单
TSharedPtr<SWidget> FTargetingComponentVisualizer::GenerateContextMenu() const
{
	FMenuBuilder MenuBuilder(true, TargetingComponentVisualizerActions);
	{
		MenuBuilder.BeginSection("Target Actions");
		{
			MenuBuilder.AddMenuEntry(FTargetingVisualizerCommands::Get().Duplicate);
		}
		MenuBuilder.EndSection();
	}

	TSharedPtr<SWidget> MenuBuilderWidget = MenuBuilder.MakeWidget();
	return MenuBuilderWidget;
}

UTargetComponent* FTargetingComponentVisualizer::GetEditedTargetingComponent() const
{
	
	return CurrentTargetComponent;
}

//菜单项复制功能回调
void FTargetingComponentVisualizer::OnDuplicateTarget()
{
	GetEditedTargetingComponent()->Targets.Add(GetEditedTargetingComponent()->Targets[SelectedTargetIndex]);
}

void FTargetingVisualizerCommands::RegisterCommands()
{
	UI_COMMAND(Duplicate, "Duplicate Target", "Duplicate the CurrentTarget", EUserInterfaceActionType::Button, FInputGesture());
}

#undef LOCTEXT_NAMESPACE 
发布了144 篇原创文章 · 获赞 15 · 访问量 9万+

猜你喜欢

转载自blog.csdn.net/maxiaosheng521/article/details/99857530