【UE】动画蓝图参数屏幕调试工具

动画蓝图参数屏幕调试工具

这里用到的动画蓝图基类为自定义的UBaseAnimInstance

引脚 类型 默认值 / 用法
Key FName 手动填写参数名称,不要加冒号
Value String 变量、转换结果、拼接结果或字符串常量均可
Key Color Linear Color 白色,包含自动添加的冒号
Value Color Linear Color 红色
Location Offset Vector (0,-200,100)

使用效果

动画蓝图调用:

1789017342449

角色蓝图调用:

1789017311268

效果(人物左侧显示)):

1789017386936

迁移到自己的项目

将下面的声明及实现复制到目标 UAnimInstance 派生类中,替换类名和自身头文件名。前置声明放在类外,头文件 region 放在类内,generated.h 保持为最后一个 include。若已有生命周期函数,合并清理调用,不要重复定义。

BaseAnimInstance.h

class UCanvas;
class APlayerController;
#pragma region DrawDebugMessages
public:
	virtual void NativeUninitializeAnimation() override;
	virtual void BeginDestroy() override;

	// 手动输入 Key 和 Value;独立颜色、共享位置偏移和自动分行。
	UFUNCTION(BlueprintCallable, Category = "DebugDraw")
	void DrawDebugKeyValueMessages(
		FName Key,
		const FString& Value,
		FLinearColor KeyColor = FLinearColor::White,
		FLinearColor ValueColor = FLinearColor::Red,
		FVector LocationOffset = FVector(0, -200, 100));

private:
	struct FDebugKeyValueMessage
	{
		FString KeyText;
		FString ValueText;
		FLinearColor KeyColor;
		FLinearColor ValueColor;
		FVector WorldLocation;
	};

	TArray<FDebugKeyValueMessage> DebugKeyValueMessages;
	uint64 DebugKeyValueFrame = MAX_uint64;
	FDelegateHandle DebugKeyValueDrawHandle;
	void DrawDebugKeyValueMessages(UCanvas* Canvas, APlayerController* PlayerController);
	void ClearDebugKeyValueMessages();
#pragma endregion DrawDebugMessages

BaseAnimInstance.cpp

#include "BaseAnimInstance.h"
#include "CanvasItem.h"
#include "Debug/DebugDrawService.h"
#include "Engine/Canvas.h"
#include "Engine/Engine.h"
#include "GameFramework/PlayerController.h"
#include "SceneInterface.h"
#include "SceneView.h"
#include "Components/SkeletalMeshComponent.h"
#include "GameFramework/Pawn.h"
#pragma region DrawDebugMessages

void UBaseAnimInstance::DrawDebugKeyValueMessages(
	const FName Key,
	const FString& Value,
	const FLinearColor KeyColor,
	const FLinearColor ValueColor,
	const FVector LocationOffset)
{
#if ENABLE_ANIM_DRAW_DEBUG
	if (!ensureMsgf(IsInGameThread(), TEXT("Draw Debug Key Value must run on the game thread.")))
	{
		return;
	}
	const USkeletalMeshComponent* Mesh = GetSkelMeshComponent();
	const AActor* Owner = Mesh ? Mesh->GetOwner() : nullptr;
	if (!Owner)
	{
		return;
	}

	FVector DrawLocationOffset = LocationOffset;
	if (const APawn* PawnOwner = TryGetPawnOwner())
	{
		if (const APlayerController* PlayerController =
			Cast<APlayerController>(PawnOwner->GetController()))
		{
			FVector ViewLocation;
			FRotator ViewRotation;
			PlayerController->GetPlayerViewPoint(ViewLocation, ViewRotation);

			// XY 跟随完整相机旋转:X 沿视线前后,Y 沿相机左右。
			// Z 单独叠加,继续保持世界向上。
			DrawLocationOffset = ViewRotation.RotateVector(
				FVector(LocationOffset.X, LocationOffset.Y, 0.0f))
				+ FVector(0.0f, 0.0f, LocationOffset.Z);
		}
	}

	if (DebugKeyValueFrame != GFrameCounter)
	{
		DebugKeyValueMessages.Reset();
		DebugKeyValueFrame = GFrameCounter;
	}
	DebugKeyValueMessages.Add({Key.ToString() + TEXT(":"), Value,
		KeyColor, ValueColor, Owner->GetActorLocation() + DrawLocationOffset});
	if (!DebugKeyValueDrawHandle.IsValid())
	{
		DebugKeyValueDrawHandle = UDebugDrawService::Register(TEXT("Game"),
			FDebugDrawDelegate::CreateUObject(this, &UBaseAnimInstance::DrawDebugKeyValueMessages));
	}
#endif
}

void UBaseAnimInstance::DrawDebugKeyValueMessages(UCanvas* Canvas, APlayerController* PlayerController)
{
#if ENABLE_ANIM_DRAW_DEBUG
	// Debug draw can be called for other editor/PIE worlds. Never leak text into those views.
	if (!Canvas || !Canvas->Canvas || !Canvas->SceneView || !GEngine
		|| !Canvas->SceneView->Family || !Canvas->SceneView->Family->Scene
		|| Canvas->SceneView->Family->Scene->GetWorld() != GetWorld()
		|| DebugKeyValueFrame == MAX_uint64 || GFrameCounter - DebugKeyValueFrame > 1)
	{
		return;
	}
	UFont* Font = GEngine->GetSmallFont();
	if (!Font)
	{
		return;
	}
	// Reset for each viewport draw; stack this instance's messages in submission order.
	float LineOffsetY = 0.0f;
	constexpr float LineSpacing = 4.0f;
	for (const FDebugKeyValueMessage& Message : DebugKeyValueMessages)
	{
		if (Canvas->SceneView->WorldToScreen(Message.WorldLocation).W <= 0.0f)
		{
			continue;
		}
		const FVector ScreenLocation = Canvas->Project(Message.WorldLocation);
		const FVector2D TextPosition(ScreenLocation.X, ScreenLocation.Y + LineOffsetY);
		float KeyWidth = 0.0f;
		float KeyHeight = 0.0f;
		Canvas->StrLen(Font, Message.KeyText, KeyWidth, KeyHeight);
		FCanvasTextItem KeyItem(TextPosition, FText::FromString(Message.KeyText), Font, Message.KeyColor);
		Canvas->DrawItem(KeyItem);
		// Canvas does not reliably expand tabs; use an explicit screen-space gap.
		constexpr float KeyValueGap = 24.0f;
		FCanvasTextItem ValueItem(TextPosition + FVector2D(KeyWidth + KeyValueGap, 0.0f),
			FText::FromString(Message.ValueText), Font, Message.ValueColor);
		Canvas->DrawItem(ValueItem);
		LineOffsetY += KeyHeight + LineSpacing;
	}
#endif
}

void UBaseAnimInstance::ClearDebugKeyValueMessages()
{
	if (DebugKeyValueDrawHandle.IsValid())
	{
		UDebugDrawService::Unregister(DebugKeyValueDrawHandle);
		DebugKeyValueDrawHandle.Reset();
	}
	DebugKeyValueMessages.Reset();
	DebugKeyValueFrame = MAX_uint64;
}

void UBaseAnimInstance::BeginDestroy()
{
	ClearDebugKeyValueMessages();
	Super::BeginDestroy();
}

void UBaseAnimInstance::NativeUninitializeAnimation()
{
	ClearDebugKeyValueMessages();
	Super::NativeUninitializeAnimation();
}

#pragma endregion DrawDebugMessages

编译与使用

目标运行时模块 Build.cs 确保依赖 Core、CoreUObject、Engine。

关闭编辑器,完整编译 Development Editor,再重启并刷新蓝图节点。动画蓝图父类应为目标动画实例类;不要在线程安全动画更新中调用此函数。

多参数自动分行

注意:要让多条参数自动排列成同一列,各个节点的 LocationOffset(X、Y、Z)必须保持一致。不要为每一行单独修改偏移,行距由工具自动累加;偏移不一致会导致文字错位或重叠。

同一帧连续调用多个节点,并使用相同的 LocationOffset。消息按调用顺序向屏幕下方排列,不需要逐条修改世界 Z。

每绘制一条消息,累计屏幕偏移 LineOffsetY += KeyHeight + LineSpacingLineSpacing 默认为 4 个画布像素,修改此常量可以调整额外行距;Key 与 Value 之间仍保持额外 24 像素间距。

行偏移在每次视口绘制开始时重置,不会跨帧累计。镜头后方被跳过的消息不占行。同一个动画实例的消息共享这一行偏移;如果传入不同的 LocationOffset,还会在各自锚点上叠加累计行偏移。

这是多条单行消息的自动分行,不是按屏幕宽度自动折行;Key 和 Value 建议均使用单行文本。需要持续显示时,每帧调用这些节点。

角色蓝图调用时

通过目标动画实例引用调用:

目标 Skeletal Mesh Component
    → Get Anim Instance
    → Cast To BaseAnimInstance
    → Draw Debug Key Value Messages(Target 接转换结果)

检查引用和 Cast 是否有效。文字跟随目标动画实例网格所属 Actor,而非另一个发起调用的 Actor。

posted @ 2026-09-10 13:18  EanoJiang  阅读(85)  评论(0)    收藏  举报