游戏性的调试
发表于2016-07-29
游戏调试工具对于在运行时(甚至在使用复制的联网游戏中的客户端上)查看实时数据,都非常有用。在编辑器中进行游戏、在编辑器中进行模拟以及在单独的游戏会话中,都可使用游戏调试工具,且所有数据都重叠显示在游戏视区中。该系统提供了一个框架,该框架可扩展,以便对游戏特定数据进行调试。
引擎实现了以下这些可显示的功能:
Pawn 的基本数据
AIController 的基本数据
行为树和黑板数据的相关信息
执行环境查询 (EQS) 的相关信息
感知系统的信息
玩家周围的导航网格或带有所有细节(如链接或区域)的选定 Pawn
通常数据量较大,因此 GDT 使用类别来限制屏幕上的信息量。系统只会复制已启用类别的数据,该数据可保存复制通道的带宽。共有以下 5 种默认类别和 5 种项目中使用的类别:

导航网格
基本数据
行为树
EQS
感知
以及 5 种项目中使用的类别
也可对现有类别进行扩展,以显示更多游戏特定数据。
以下是我们在客户端上获取的屏幕截屏,该客户端上只启用了几个类别:基本数据、EQS、导航网格和行为树:

使用 '(单引号)键(默认设置)或 EnableGDT 作弊工具可启用游戏调试工具。键盘按键设置在文件中设置,可方便地更改该设置。 若要选定要调试的敌人,请在屏幕上正对着该敌人时按 ' 键。数字键盘用于在可见类别之间进行切换。 必须将游戏调试模块添加到项目的依赖关系模块,才能启用和使用游戏调试模块。
一、编辑器-与游戏调试工具组合使用
在编辑器中工作时,可在 PIE 或模拟模式中使用 GDT。已设置的键或 EnableGDT 作弊工具可用于在 PIE 模式中启用 GDT。 模拟模式与 PIE 模式稍有不同,所以启用本调试工具需要启用 “Debug AI” 显示标志。软件还提供了一个选项,可在模拟模式中更改可见的类别。 GameplayDebuggingReplicator actor 就用于该选项。该 actor 位于场景大纲视图中,其属性可用于控制 GDT:

二、基本扩展
游戏调试工具只能使用 C++ 代码进行扩展。在蓝图项目中,游戏调试程序只能按原样使用,即显示当前的基本调试信息。对游戏调试程序进行扩展是一件容易的事情, 收集并显示游戏特定数据即可。为此,需要使用继承自 UGameplayDebuggingComponent 类和AGameplayDebuggingHUDComponent 类的定制类。 第一个类用于收集数据,并最终复制数据,而第二个类用于在屏幕上显示所有已收集的数据。
以下是一种用于收集游戏特定数据的简单类:
GDTComponent.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | // Copyright 1998-2016 Epic Games, Inc. All Rights Reserved. #pragma once #include "GameplayDebuggingComponent.h" #include "GDTComponent.generated.h" UCLASS() class UGDTComponent : public UGameplayDebuggingComponent { public : GENERATED_UCLASS_BODY() virtual void CollectBasicData() override ; UPROPERTY(Replicated) float TestData; //custom data replicated to clients }; |
GDTComponent.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | // Copyright 1998-2016 Epic Games, Inc. All Rights Reserved. #include "MyGameProject.h" #include "GameplayDebuggingComponent.h" #include "GDTComponent.h" UGDTComponent::UGDTComponent( const class FPostConstructInitializeProperties& PCIP) :Super(PCIP) { } void UGDTComponent::GetLifetimeReplicatedProps(TArray const { Super::GetLifetimeReplicatedProps( OutLifetimeProps ); #if !(UE_BUILD_SHIPPING || UE_BUILD_TEST) DOREPLIFETIME( UGDTComponent, TestData); #endif } void UGDTComponent::CollectBasicData() { Super::CollectBasicData(); #if !(UE_BUILD_SHIPPING || UE_BUILD_TEST) TestData= FMath::RandRange(2.75, 8.25); //collect data and store it #endif } |
下个类用于在屏幕上显示新数据:
GDTHUDComponent.h
1 2 3 4 5 6 7 8 9 10 11 12 13 | // Copyright 1998-2016 Epic Games, Inc. All Rights Reserved. #pragma once #include "GameplayDebuggingHUDComponent.h" #include "GDTHUDComponent.generated.h" UCLASS(notplaceable) class AGDTHUDComponent: public AGameplayDebuggingHUDComponent { GENERATED_UCLASS_BODY() protected : virtual void DrawBasicData(APlayerController* PC, class UGameplayDebuggingComponent *DebugComponent) override ; }; |
GDTHUDComponent.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | // Copyright 1998-2016 Epic Games, Inc. All Rights Reserved. #include "MyGameProject.h" #include "GDTComponent.h" #include "GDTHUDComponent.h" AGDTHUDComponent::AGDTHUDComponent( const class FPostConstructInitializeProperties& PCIP) :Super(PCIP) { } void AGDTHUDComponent::DrawBasicData(APlayerController* PC, class UGameplayDebuggingComponent *DebugComponent) { Super::DrawBasicData(PC, DebugComponent); #if !(UE_BUILD_SHIPPING || UE_BUILD_TEST) const UGDTComponent* MyComp = Cast if (MyComp) { PrintString(DefaultContext, FString::Printf(TEXT( "{white}Test data: {red}%fn" ), MyComp->TestData)); } #endif } |
游戏调试工具需要理解新的类,且该信息可在 DefaultEngine.ini 配置文件中设置:
DefaultEngine.ini
1 2 3 | [/Script/GameplayDebugger.GameplayDebuggingReplicator] DebugComponentClassName= "/Script/MyGameProject.GDTComponent" DebugComponentHUDClassName= "/Script/MyGameProject.GDTHUDComponent" | |
三、定制类别
需几步即可在游戏调试程序中添加项目特定类别。
让我们扩展 GDTComponent 类:
GDTComponent.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | // Copyright 1998-2016 Epic Games, Inc. All Rights Reserved. #pragma once #include "GameplayDebuggingComponent.h" #include "GDTComponent.generated.h" UCLASS() class UGDTComponent : public UGameplayDebuggingComponent { public : GENERATED_UCLASS_BODY() protected : virtual void CollectDataToReplicate( bool bCollectExtendedData) override ; void CollectCustomData(); public : UPROPERTY(Replicated) float TestData; //custom data replicated to clients }; |
GDTComponent.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | // Copyright 1998-2016 Epic Games, Inc. All Rights Reserved. #include "MyGameProject.h" #include "GameplayDebuggingComponent.h" #include "GDTComponent.h" UGDTComponent::UGDTComponent( const class FPostConstructInitializeProperties& PCIP) :Super(PCIP) { } void UGDTComponent::GetLifetimeReplicatedProps(TArray const { Super::GetLifetimeReplicatedProps( OutLifetimeProps ); #if !(UE_BUILD_SHIPPING || UE_BUILD_TEST) DOREPLIFETIME( UGDTComponent, TestData); #endif } void UGDTComponent::CollectCustomData() { Super::CollectBasicData(); #if !(UE_BUILD_SHIPPING || UE_BUILD_TEST) TestData= FMath::RandRange(2.75, 8.25); //collect data and store it #endif } void UGDTComponent::CollectDataToReplicate( bool bCollectExtendedData) { Super::CollectDataToReplicate(bCollectExtendedData); #if !(UE_BUILD_SHIPPING || UE_BUILD_TEST) if (ShouldReplicateData(EAIDebugDrawDataView::GameView1)) { CollectCustomData(); if (bCollectExtendedData) { // collect additional data for selected Pawn/AIController } } #endif } |
HUD 组件已被扩展,以便在新视图中显示数据:
GDTHUDComponent.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | // Copyright 1998-2016 Epic Games, Inc. All Rights Reserved. #pragma once #include "GameplayDebuggingHUDComponent.h" #include "GDTHUDComponent.generated.h" UCLASS(notplaceable) class AGDTHUDComponent: public AGameplayDebuggingHUDComponent { GENERATED_UCLASS_BODY() protected : virtual void DrawGameSpecificView(APlayerController* PC, class UGameplayDebuggingComponent *DebugComponent) override ; virtual void GetKeyboardDesc(TArray override ; void DrawCustomData(APlayerController* PC, class UGameplayDebuggingComponent *DebugComponent); }; |
GDTHUDComponent.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | // Copyright 1998-2016 Epic Games, Inc. All Rights Reserved. #include "MyGameProject.h" #include "GDTComponent.h" #include "GDTHUDComponent.h" AGDTHUDComponent::AGDTHUDComponent( const class FPostConstructInitializeProperties& PCIP) :Super(PCIP) { } void AGDTHUDComponent::DrawCustomData(APlayerController* PC, class UGameplayDebuggingComponent *DebugComponent) { #if !(UE_BUILD_SHIPPING || UE_BUILD_TEST) const UGDTComponent* MyComp = Cast if (MyComp) { PrintString(DefaultContext, FString::Printf(TEXT( "{white}Test data: {red}%fn" ), MyComp->TestData)); } #endif } void AGDTHUDComponent::GetKeyboardDesc(TArray { Super::GetKeyboardDesc(Categories); #if !(UE_BUILD_SHIPPING || UE_BUILD_TEST) Categories.Add(FDebugCategoryView(EAIDebugDrawDataView::GameView1, TEXT( "MyData" ))); #endif } void AGDTHUDComponent::DrawGameSpecificView(APlayerController* PC, class UGameplayDebuggingComponent *InDebugComponent) { Super::DrawGameSpecificView(PC, InDebugComponent); #if !(UE_BUILD_SHIPPING || UE_BUILD_TEST) if (InDebugComponent && GameplayDebuggerSettings(GetDebuggingReplicator()).CheckFlag(EAIDebugDrawDataView::GameView1)) { PrintString(DefaultContext, FColor::Green, TEXT( "nMY GAME DATAn" )); DrawCustomData(PC, InDebugComponent); } #endif } |
新类别已准备就绪,可用于对项目特定数据进行调试:

若要用颜色标出调试信息,PrintString 函数可在字符串中使用标记来改变当前使用的颜色。使用不同颜色描绘字符串变得更加方便:
1 2 3 4 | void PrintString(FPrintContext& Context, const FString& InString ); void PrintString(FPrintContext& Context, const FColor& InColor, const FString& InString ); PrintString(DefaultContext, FColor::Green, TEXT( "Whole text in green" )); PrintString(DefaultContext, TEXT( "String {green}in green, {red}in red {white}or {R=0,G=0,B=255,A=255}in blue" )); |
最后的 PrintString 函数可生成具有 4 种不同颜色的字符串:
