You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
==========================
MAJOR FEATURES + CHANGES
==========================
Change 2799478 on 2015/12/11 by Robert.Manuszewski@Robert.Manuszewski_NCL_Stream1
Added Dev Stream custom versions.
- Each stream now has its own custom version
- Developers working in a stream should only modify their respective version
Change 2789867 on 2015/12/04 by Robert.Manuszewski@Robert.Manuszewski_NCL_Stream1
UnrealHeaderTool plugins no longer need to be a separate plugin and don't require UnrealHeaderTool source code changes to work.
- Merged ScriptGeneratorPlugin with ScriptPlugin
- Introduced the concept of UHT plugin support for plugins so that UHT's source files don't need to be modified to make it work with external plugins
- Added RuntimeNoProgram module type (module that can be used at runtime but not by program targets).
- Fixed logic with project file path setting in the engine. It will no longer try to crate a full path from an already rooted path.
Change 2796114 on 2015/12/09 by Steve.Robb@Dev-Core
TEnumRange - enabled ranged-based for iteration over enum values.
Change 2789843 on 2015/12/04 by Robert.Manuszewski@Robert.Manuszewski_NCL_Stream2
More thorough way of verifying GC cluster assumptions.
Change 2794221 on 2015/12/08 by Robert.Manuszewski@Robert.Manuszewski_NCL_Stream2
Don't merge GC clusters by default
Change 2797824 on 2015/12/10 by Robert.Manuszewski@Robert.Manuszewski_NCL_Stream1
Added the option to load all symbols for stack walking in non-monolithic builds.
Change 2790539 on 2015/12/04 by Jaroslaw.Surowiec@Stream.1.JarekSurowiec
Stats/Profiler - Better handling for live connection, using the same path as file profiles, added FStatsLoadedState to separate runtime stats state from the loaded one
Change 2794183 on 2015/12/08 by Robert.Manuszewski@Robert.Manuszewski_NCL_Stream1
Always reset events when returning them to pool.
Change 2794406 on 2015/12/08 by Robert.Manuszewski@Robert.Manuszewski_NCL_Stream2
Fixing -unversioned flag being completely ignored when cooking
Change 2794563 on 2015/12/08 by Robert.Manuszewski@Robert.Manuszewski_NCL_Stream2
Making sure string referenced assets don't get cooked if referenced by editor-only properties.
Change 2795124 on 2015/12/08 by Jaroslaw.Surowiec@Stream.1.JarekSurowiec
Profiler - Fixed bad data in min/max/avg inclusive times, added min/max/avg num calls, fixed event graph tooltip not displaying correct values
Change 2796208 on 2015/12/09 by Jaroslaw.Surowiec@Stream.1.JarekSurowiec
Profiler - Remove support for multiple instances in the profiler (game thread is always visible, a few crash fixes, cleaned code a bit)
Change 2797658 on 2015/12/10 by Robert.Manuszewski@Robert.Manuszewski_NCL_Stream1
Allocation verification helpers. Helps with tracking down memory stomps, freeing the same pointers multiple times.
Change 2797699 on 2015/12/10 by Robert.Manuszewski@Robert.Manuszewski_NCL_Stream1
Fix incorrect asset loading in Cooked game data (by bozaro)
PR #1844
Change 2798173 on 2015/12/10 by Steve.Robb@Dev-Core
Migration of Fortnite to use engine's TEnumRange.
Change 2798217 on 2015/12/10 by Jaroslaw.Surowiec@Stream.1.JarekSurowiec
PR #1331
[Core] Added a stomp allocator that allows finding memory overruns, underruns, and read/write after free. (Contributed by Pablo Zurita)
Change 2799605 on 2015/12/11 by Robert.Manuszewski@Robert.Manuszewski_NCL_Stream1
Fixing a crash when cancelling async loading caused by detaching linker from objects that had RF_NeedLoad flag set.
Change 2799849 on 2015/12/11 by Steve.Robb@Dev-Core
Migration of Ocean to use engine's TEnumRange.
Change 2803144 on 2015/12/15 by Robert.Manuszewski@Robert.Manuszewski_NCL_Stream1
Changed export tagging archive to also serialize class default objects using the normal Serialize path so that it can collect all custom versions used by exports.
Change 2803206 on 2015/12/15 by Jaroslaw.Surowiec@Stream.1.JarekSurowiec
#jira UE-24177
Audit ôshippingö defines in engine
Change 2804868 on 2015/12/16 by Steve.Robb@Dev-Core
Removal of stats from MallocBinned2, to be readded later.
#lockdown Nick.Penwarden
[CL 2805158 by Robert Manuszewski in Main branch]
1621 lines
50 KiB
C++
1621 lines
50 KiB
C++
// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
|
|
#include "GameplayDebuggerPrivate.h"
|
|
#include "Net/UnrealNetwork.h"
|
|
#include "DebugRenderSceneProxy.h"
|
|
#include "AI/Navigation/RecastNavMeshGenerator.h"
|
|
#include "NavMeshRenderingHelpers.h"
|
|
#include "AI/Navigation/NavigationSystem.h"
|
|
#include "EnvironmentQuery/EnvQueryTypes.h"
|
|
#include "EnvironmentQuery/EnvQueryManager.h"
|
|
#include "EnvironmentQuery/EQSRenderingComponent.h"
|
|
#include "EnvironmentQuery/EnvQueryTest.h"
|
|
#include "BehaviorTree/BlackboardComponent.h"
|
|
#include "BehaviorTree/BehaviorTreeComponent.h"
|
|
#include "AbilitySystemComponent.h"
|
|
#include "AIController.h"
|
|
#include "BrainComponent.h"
|
|
#include "BehaviorTreeDelegates.h"
|
|
#include "GameFramework/PlayerState.h"
|
|
#include "GameFramework/Character.h"
|
|
#include "GameFramework/CharacterMovementComponent.h"
|
|
#include "Engine/Channel.h"
|
|
#include "Animation/AnimMontage.h"
|
|
#include "GameplayAbilitiesModule.h"
|
|
#include "Perception/AIPerceptionComponent.h"
|
|
|
|
#if WITH_EDITOR
|
|
#include "Editor/EditorEngine.h"
|
|
#include "GeomTools.h"
|
|
#endif // WITH_EDITOR
|
|
|
|
#include "GameplayDebuggingComponent.h"
|
|
|
|
//----------------------------------------------------------------------//
|
|
// Composite Scene proxy
|
|
//----------------------------------------------------------------------//
|
|
class FDebugRenderSceneCompositeProxy : public FDebugRenderSceneProxy
|
|
{
|
|
public:
|
|
FDebugRenderSceneCompositeProxy(const UPrimitiveComponent* InComponent)
|
|
: FDebugRenderSceneProxy(InComponent)
|
|
{
|
|
}
|
|
|
|
virtual ~FDebugRenderSceneCompositeProxy()
|
|
{
|
|
for (int32 Index = 0; Index < ChildProxies.Num(); ++Index)
|
|
{
|
|
delete ChildProxies[Index];
|
|
}
|
|
}
|
|
|
|
virtual void DrawStaticElements(FStaticPrimitiveDrawInterface* PDI) override
|
|
{
|
|
for (int32 Index = 0; Index < ChildProxies.Num(); ++Index)
|
|
{
|
|
ChildProxies[Index]->DrawStaticElements(PDI);
|
|
}
|
|
}
|
|
|
|
virtual void GetDynamicMeshElements(const TArray<const FSceneView*>& Views, const FSceneViewFamily& ViewFamily, uint32 VisibilityMap, FMeshElementCollector& Collector) const override
|
|
{
|
|
for (int32 Index = 0; Index < ChildProxies.Num(); ++Index)
|
|
{
|
|
ChildProxies[Index]->GetDynamicMeshElements(Views, ViewFamily, VisibilityMap, Collector);
|
|
}
|
|
}
|
|
|
|
virtual void RegisterDebugDrawDelgate() override
|
|
{
|
|
for (int32 Index = 0; Index < ChildProxies.Num(); ++Index)
|
|
{
|
|
ChildProxies[Index]->RegisterDebugDrawDelgate();
|
|
}
|
|
}
|
|
|
|
virtual void UnregisterDebugDrawDelgate() override
|
|
{
|
|
for (int32 Index = 0; Index < ChildProxies.Num(); ++Index)
|
|
{
|
|
ChildProxies[Index]->UnregisterDebugDrawDelgate();
|
|
}
|
|
}
|
|
|
|
virtual FPrimitiveViewRelevance GetViewRelevance(const FSceneView* View) const override
|
|
{
|
|
FPrimitiveViewRelevance Result;
|
|
for (int32 Index = 0; Index < ChildProxies.Num(); ++Index)
|
|
{
|
|
Result |= ChildProxies[Index]->GetViewRelevance(View);
|
|
}
|
|
return Result;
|
|
}
|
|
|
|
virtual uint32 GetMemoryFootprint( void ) const override
|
|
{
|
|
return sizeof( *this ) + GetAllocatedSize();
|
|
}
|
|
|
|
uint32 GetAllocatedSize( void ) const
|
|
{
|
|
uint32 Size = 0;
|
|
for (int32 Index = 0; Index < ChildProxies.Num(); ++Index)
|
|
{
|
|
ChildProxies[Index]->GetMemoryFootprint();
|
|
}
|
|
|
|
return Size + ChildProxies.GetAllocatedSize();
|
|
}
|
|
|
|
void AddChild(FDebugRenderSceneProxy* NewChild)
|
|
{
|
|
ChildProxies.AddUnique(NewChild);
|
|
}
|
|
|
|
protected:
|
|
TArray<FDebugRenderSceneProxy*> ChildProxies;
|
|
};
|
|
|
|
//----------------------------------------------------------------------//
|
|
// UGameplayDebuggingComponent
|
|
//----------------------------------------------------------------------//
|
|
|
|
FName UGameplayDebuggingComponent::DefaultComponentName = TEXT("GameplayDebuggingComponent");
|
|
FOnDebuggingTargetChanged UGameplayDebuggingComponent::OnDebuggingTargetChangedDelegate;
|
|
|
|
UGameplayDebuggingComponent::UGameplayDebuggingComponent(const FObjectInitializer& ObjectInitializer)
|
|
: Super(ObjectInitializer)
|
|
, bDrawEQSLabels(true)
|
|
, bDrawEQSFailedItems(true)
|
|
{
|
|
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
|
|
DebugComponentClassName = TEXT("/Script/GameplayDebugger.GameplayDebuggingComponent");
|
|
|
|
PrimaryComponentTick.bCanEverTick = true;
|
|
bWantsInitializeComponent = true;
|
|
bAutoActivate = false;
|
|
PrimaryComponentTick.bStartWithTickEnabled = false;
|
|
|
|
LastStoredPathTimeStamp = -1.f;
|
|
|
|
ShowExtendedInformatiomCounter = 0;
|
|
#if WITH_EDITOR
|
|
bWasSelectedInEditor = false;
|
|
#endif
|
|
|
|
bHiddenInGame = false;
|
|
bEnabledTargetSelection = false;
|
|
|
|
bEnableClientEQSSceneProxy = false;
|
|
NextTargrtSelectionTime = 0;
|
|
ReplicateViewDataCounters.Init(0, EAIDebugDrawDataView::MAX);
|
|
|
|
AGameplayDebuggingReplicator* Replicator = Cast<AGameplayDebuggingReplicator>(GetOwner());
|
|
if (Replicator)
|
|
{
|
|
Replicator->OnCycleDetailsView.AddUObject(this, &UGameplayDebuggingComponent::OnCycleDetailsView);
|
|
FGameplayDebuggerSettings Settings = GameplayDebuggerSettings(Replicator);
|
|
for (EAIDebugDrawDataView::Type View : TEnumRange<EAIDebugDrawDataView::Type>())
|
|
{
|
|
ReplicateViewDataCounters[View] = Settings.CheckFlag(View);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void UGameplayDebuggingComponent::Activate(bool bReset)
|
|
{
|
|
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
|
|
Super::Activate(bReset);
|
|
SetComponentTickEnabled(true);
|
|
SetIsReplicated(true);
|
|
ActivationCounter++;
|
|
#else
|
|
Super::Activate(bReset);
|
|
#endif
|
|
}
|
|
|
|
void UGameplayDebuggingComponent::Deactivate()
|
|
{
|
|
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
|
|
Super::Deactivate();
|
|
SetComponentTickEnabled(false);
|
|
SetIsReplicated(false);
|
|
#else
|
|
Super::Deactivate();
|
|
#endif
|
|
}
|
|
|
|
void UGameplayDebuggingComponent::OnRep_ActivationCounter()
|
|
{
|
|
// make sure that tick function is running, can't select actor to debug without it
|
|
// if first activation (UActorComponent) comes before tick function is registered, state will be overridden by bStartWithTickEnabled (false)
|
|
SetComponentTickEnabled(true);
|
|
}
|
|
|
|
void UGameplayDebuggingComponent::GetLifetimeReplicatedProps( TArray< FLifetimeProperty > & OutLifetimeProps ) const
|
|
{
|
|
Super::GetLifetimeReplicatedProps( OutLifetimeProps );
|
|
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
|
|
DOREPLIFETIME(UGameplayDebuggingComponent, ReplicateViewDataCounters);
|
|
DOREPLIFETIME(UGameplayDebuggingComponent, ShowExtendedInformatiomCounter);
|
|
DOREPLIFETIME(UGameplayDebuggingComponent, ControllerName)
|
|
DOREPLIFETIME(UGameplayDebuggingComponent, PawnName);
|
|
DOREPLIFETIME(UGameplayDebuggingComponent, DebugIcon);
|
|
DOREPLIFETIME(UGameplayDebuggingComponent, PawnClass);
|
|
DOREPLIFETIME(UGameplayDebuggingComponent, ActivationCounter);
|
|
|
|
DOREPLIFETIME(UGameplayDebuggingComponent, bIsUsingCharacter);
|
|
DOREPLIFETIME(UGameplayDebuggingComponent, MovementBaseInfo);
|
|
DOREPLIFETIME(UGameplayDebuggingComponent, MovementModeInfo);
|
|
DOREPLIFETIME(UGameplayDebuggingComponent, MontageInfo);
|
|
|
|
DOREPLIFETIME(UGameplayDebuggingComponent, bIsUsingPathFollowing);
|
|
DOREPLIFETIME(UGameplayDebuggingComponent, PathFollowingInfo);
|
|
DOREPLIFETIME(UGameplayDebuggingComponent, NavDataInfo);
|
|
DOREPLIFETIME(UGameplayDebuggingComponent, PathPoints);
|
|
DOREPLIFETIME(UGameplayDebuggingComponent, PathCorridorData);
|
|
DOREPLIFETIME(UGameplayDebuggingComponent, NextPathPointIndex);
|
|
|
|
DOREPLIFETIME(UGameplayDebuggingComponent, bIsUsingBehaviorTree);
|
|
DOREPLIFETIME(UGameplayDebuggingComponent, CurrentAITask);
|
|
DOREPLIFETIME(UGameplayDebuggingComponent, CurrentAIState);
|
|
DOREPLIFETIME(UGameplayDebuggingComponent, CurrentAIAssets);
|
|
|
|
DOREPLIFETIME(UGameplayDebuggingComponent, GameplayTasksState);
|
|
|
|
DOREPLIFETIME(UGameplayDebuggingComponent, bIsUsingAbilities);
|
|
DOREPLIFETIME(UGameplayDebuggingComponent, AbilityInfo);
|
|
|
|
DOREPLIFETIME(UGameplayDebuggingComponent, BrainComponentName);
|
|
DOREPLIFETIME(UGameplayDebuggingComponent, BrainComponentString);
|
|
DOREPLIFETIME(UGameplayDebuggingComponent, BlackboardRepData);
|
|
|
|
DOREPLIFETIME(UGameplayDebuggingComponent, NavmeshRepData);
|
|
DOREPLIFETIME(UGameplayDebuggingComponent, TargetActor);
|
|
DOREPLIFETIME(UGameplayDebuggingComponent, EQSRepData);
|
|
|
|
DOREPLIFETIME(UGameplayDebuggingComponent, SensingComponentLocation);
|
|
|
|
DOREPLIFETIME(UGameplayDebuggingComponent, PerceptionLegend)
|
|
DOREPLIFETIME(UGameplayDebuggingComponent, DistanceFromPlayer);
|
|
DOREPLIFETIME(UGameplayDebuggingComponent, DistanceFromSensor);
|
|
DOREPLIFETIME(UGameplayDebuggingComponent, SensingComponentLocation);
|
|
DOREPLIFETIME(UGameplayDebuggingComponent, PerceptionShapeElements);
|
|
|
|
#endif //!(UE_BUILD_SHIPPING || UE_BUILD_TEST)
|
|
}
|
|
|
|
bool UGameplayDebuggingComponent::ClientEnableTargetSelection_Validate(bool bEnable)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
void UGameplayDebuggingComponent::ClientEnableTargetSelection_Implementation(bool bEnable)
|
|
{
|
|
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
|
|
bEnabledTargetSelection = bEnable;
|
|
if (bEnabledTargetSelection && World && World->GetNetMode() != NM_DedicatedServer)
|
|
{
|
|
NextTargrtSelectionTime = 0;
|
|
}
|
|
#endif //!(UE_BUILD_SHIPPING || UE_BUILD_TEST)
|
|
}
|
|
|
|
void UGameplayDebuggingComponent::SetActorToDebug(AActor* Actor)
|
|
{
|
|
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
|
|
if (TargetActor != Actor)
|
|
{
|
|
MarkRenderStateDirty();
|
|
}
|
|
TargetActor = Actor;
|
|
EQSLocalData.Reset();
|
|
#endif //!(UE_BUILD_SHIPPING || UE_BUILD_TEST)
|
|
}
|
|
|
|
void UGameplayDebuggingComponent::TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction *ThisTickFunction)
|
|
{
|
|
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
|
|
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
|
|
|
|
if (World && World->GetNetMode() != NM_DedicatedServer)
|
|
{
|
|
if (bEnabledTargetSelection)
|
|
{
|
|
AGameplayDebuggingReplicator* Replicator = Cast<AGameplayDebuggingReplicator>(GetOwner());
|
|
if (Replicator)
|
|
{
|
|
if (Replicator->GetLocalPlayerOwner())
|
|
{
|
|
SelectTargetToDebug();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (World && World->GetNetMode() < NM_Client)
|
|
{
|
|
CollectDataToReplicate(true);
|
|
}
|
|
|
|
#endif //!(UE_BUILD_SHIPPING || UE_BUILD_TEST)
|
|
}
|
|
|
|
void UGameplayDebuggingComponent::SelectTargetToDebug()
|
|
{
|
|
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
|
|
AGameplayDebuggingReplicator* Replicator = Cast<AGameplayDebuggingReplicator>(GetOwner());
|
|
|
|
UGameplayDebuggingControllerComponent* GDC = Replicator->FindComponentByClass<UGameplayDebuggingControllerComponent>();
|
|
APlayerController* MyPC = GDC && GDC->GetDebugCameraController().IsValid() ? GDC->GetDebugCameraController().Get() : Replicator->GetLocalPlayerOwner();
|
|
|
|
if (MyPC )
|
|
{
|
|
float bestAim = 0;
|
|
FVector CamLocation;
|
|
FRotator CamRotation;
|
|
check(MyPC->PlayerCameraManager != NULL);
|
|
MyPC->PlayerCameraManager->GetCameraViewPoint(CamLocation, CamRotation);
|
|
FVector FireDir = CamRotation.Vector();
|
|
UWorld* World = MyPC->GetWorld();
|
|
check( World );
|
|
|
|
APawn* PossibleTarget = NULL;
|
|
for (FConstPawnIterator Iterator = World->GetPawnIterator(); Iterator; ++Iterator )
|
|
{
|
|
APawn* NewTarget = *Iterator;
|
|
if (NewTarget == NULL || NewTarget == MyPC->GetPawn()
|
|
|| (NewTarget->PlayerState != NULL && NewTarget->PlayerState->bIsABot == false)
|
|
|| NewTarget->GetActorEnableCollision() == false)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (NewTarget != MyPC->GetPawn())
|
|
{
|
|
// look for best controlled pawn target
|
|
const FVector AimDir = NewTarget->GetActorLocation() - CamLocation;
|
|
float FireDist = AimDir.SizeSquared();
|
|
// only find targets which are < 25000 units away
|
|
if (FireDist < 625000000.f)
|
|
{
|
|
FireDist = FMath::Sqrt(FireDist);
|
|
float newAim = FVector::DotProduct(FireDir, AimDir);
|
|
newAim = newAim / FireDist;
|
|
if (newAim > bestAim)
|
|
{
|
|
PossibleTarget = NewTarget;
|
|
bestAim = newAim;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (PossibleTarget != NULL && PossibleTarget != GetSelectedActor())
|
|
{
|
|
if (AGameplayDebuggingReplicator* DebuggingReplicator = Cast<AGameplayDebuggingReplicator>(GetOwner()))
|
|
{
|
|
DebuggingReplicator->ServerSetActorToDebug(Cast<AActor>(PossibleTarget));
|
|
MarkRenderStateDirty();
|
|
}
|
|
|
|
ServerReplicateData(EDebugComponentMessage::ActivateReplication, EAIDebugDrawDataView::Empty);
|
|
}
|
|
}
|
|
#endif //!(UE_BUILD_SHIPPING || UE_BUILD_TEST)
|
|
}
|
|
|
|
void UGameplayDebuggingComponent::CollectDataToReplicate(bool bCollectExtendedData)
|
|
{
|
|
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
|
|
const AActor *SelectedActor = GetSelectedActor();
|
|
if (!SelectedActor || SelectedActor->IsPendingKill())
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (ShouldReplicateData(EAIDebugDrawDataView::Basic) || ShouldReplicateData(EAIDebugDrawDataView::OverHead))
|
|
{
|
|
CollectBasicData();
|
|
}
|
|
|
|
AGameplayDebuggingReplicator* Replicator = Cast<AGameplayDebuggingReplicator>(GetOwner());
|
|
const bool bDrawFullData = Replicator->GetSelectedActorToDebug() == GetSelectedActor();
|
|
if (bDrawFullData && ShouldReplicateData(EAIDebugDrawDataView::Basic))
|
|
{
|
|
CollectPathData();
|
|
}
|
|
|
|
if (bCollectExtendedData && bDrawFullData)
|
|
{
|
|
if (ShouldReplicateData(EAIDebugDrawDataView::BehaviorTree))
|
|
{
|
|
CollectBehaviorTreeData();
|
|
}
|
|
|
|
#if WITH_EQS
|
|
if (ShouldReplicateData(EAIDebugDrawDataView::EQS))
|
|
{
|
|
bool bEnabledEnvironmentQueryEd = true;
|
|
if (GConfig)
|
|
{
|
|
GConfig->GetBool(TEXT("EnvironmentQueryEd"), TEXT("EnableEnvironmentQueryEd"), bEnabledEnvironmentQueryEd, GEngineIni);
|
|
}
|
|
|
|
if (bEnabledEnvironmentQueryEd)
|
|
{
|
|
CollectEQSData();
|
|
}
|
|
}
|
|
#endif // WITH_EQS
|
|
CollectPerceptionData();
|
|
}
|
|
#endif //!(UE_BUILD_SHIPPING || UE_BUILD_TEST)
|
|
}
|
|
|
|
void UGameplayDebuggingComponent::CollectBasicData()
|
|
{
|
|
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
|
|
APawn* MyPawn = Cast<APawn>(GetSelectedActor());
|
|
PawnName = MyPawn->GetHumanReadableName();
|
|
PawnClass = MyPawn->GetClass()->GetName();
|
|
AAIController* MyController = Cast<AAIController>(MyPawn->Controller);
|
|
|
|
bIsUsingCharacter = MyPawn->IsA(ACharacter::StaticClass());
|
|
|
|
if (MyController)
|
|
{
|
|
if (MyController->IsPendingKill() == false)
|
|
{
|
|
ControllerName = MyController->GetName();
|
|
DebugIcon = MyController->GetDebugIcon();
|
|
|
|
CollectBasicMovementData(MyPawn);
|
|
CollectBasicPathData(MyPawn);
|
|
CollectBasicBehaviorData(MyPawn);
|
|
CollectBasicAbilityData(MyPawn);
|
|
CollectBasicAnimationData(MyPawn);
|
|
}
|
|
else
|
|
{
|
|
ControllerName = TEXT("Controller PENDING KILL");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ControllerName = TEXT("No Controller");
|
|
}
|
|
#endif //!(UE_BUILD_SHIPPING || UE_BUILD_TEST)
|
|
}
|
|
|
|
void UGameplayDebuggingComponent::CollectBasicMovementData(APawn* MyPawn)
|
|
{
|
|
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
|
|
UCharacterMovementComponent* CharMovement = Cast<UCharacterMovementComponent>(MyPawn->GetMovementComponent());
|
|
if (CharMovement)
|
|
{
|
|
UPrimitiveComponent* FloorComponent = MyPawn->GetMovementBase();
|
|
AActor* FloorActor = FloorComponent ? FloorComponent->GetOwner() : nullptr;
|
|
MovementBaseInfo = FloorComponent ? FString::Printf(TEXT("%s.%s"), *GetNameSafe(FloorActor), *FloorComponent->GetName()) : TEXT("None");
|
|
|
|
MovementModeInfo = CharMovement->GetMovementName();
|
|
}
|
|
else
|
|
{
|
|
MovementBaseInfo = TEXT("");
|
|
MovementModeInfo = TEXT("");
|
|
}
|
|
#endif //!(UE_BUILD_SHIPPING || UE_BUILD_TEST)
|
|
}
|
|
|
|
void UGameplayDebuggingComponent::CollectBasicPathData(APawn* MyPawn)
|
|
{
|
|
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
|
|
UNavigationSystem* NavSys = UNavigationSystem::GetCurrent(MyPawn->GetWorld());
|
|
AAIController* MyAIController = Cast<AAIController>(MyPawn->GetController());
|
|
|
|
const ANavigationData* NavData = NavSys ? NavSys->GetNavDataForProps(MyAIController->GetNavAgentPropertiesRef()) : nullptr;
|
|
if (NavData)
|
|
{
|
|
NavDataInfo = NavData->GetConfig().Name.ToString();
|
|
}
|
|
|
|
UPathFollowingComponent* PFC = MyAIController->GetPathFollowingComponent();
|
|
bIsUsingPathFollowing = (PFC != nullptr);
|
|
|
|
if (PFC)
|
|
{
|
|
TArray<FString> Tokens;
|
|
TArray<EPathFollowingDebugTokens::Type> Flags;
|
|
if (PFC)
|
|
{
|
|
PFC->GetDebugStringTokens(Tokens, Flags);
|
|
}
|
|
|
|
PathFollowingInfo = FString();
|
|
for (int32 Idx = 0; Idx < Tokens.Num(); Idx++)
|
|
{
|
|
switch (Flags[Idx])
|
|
{
|
|
case EPathFollowingDebugTokens::Description:
|
|
PathFollowingInfo += Tokens[Idx];
|
|
break;
|
|
|
|
case EPathFollowingDebugTokens::ParamName:
|
|
PathFollowingInfo += TEXT(", {yellow}");
|
|
PathFollowingInfo += Tokens[Idx];
|
|
PathFollowingInfo += TEXT(":");
|
|
break;
|
|
|
|
case EPathFollowingDebugTokens::PassedValue:
|
|
PathFollowingInfo += TEXT("{yellow}");
|
|
PathFollowingInfo += Tokens[Idx];
|
|
break;
|
|
|
|
case EPathFollowingDebugTokens::FailedValue:
|
|
PathFollowingInfo += TEXT("{orange}");
|
|
PathFollowingInfo += Tokens[Idx];
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
PathFollowingInfo = TEXT("");
|
|
}
|
|
#endif //!(UE_BUILD_SHIPPING || UE_BUILD_TEST)
|
|
}
|
|
|
|
void UGameplayDebuggingComponent::CollectBasicBehaviorData(APawn* MyPawn)
|
|
{
|
|
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST) && ENABLE_VISUAL_LOG
|
|
AAIController* MyAIController = Cast<AAIController>(MyPawn->GetController());
|
|
UBehaviorTreeComponent* BTC = MyAIController ? Cast<UBehaviorTreeComponent>(MyAIController->BrainComponent) : nullptr;
|
|
bIsUsingBehaviorTree = (BTC != nullptr);
|
|
|
|
if (BTC)
|
|
{
|
|
CurrentAITask = BTC->DescribeActiveTasks();
|
|
CurrentAIState = BTC->IsRunning() ? TEXT("Running") : BTC->IsPaused() ? TEXT("Paused") : TEXT("Inactive");
|
|
CurrentAIAssets = BTC->DescribeActiveTrees();
|
|
}
|
|
else
|
|
{
|
|
CurrentAITask = TEXT("");
|
|
CurrentAIState = TEXT("");
|
|
CurrentAIAssets = TEXT("");
|
|
}
|
|
|
|
UGameplayTasksComponent* GTComponent = MyPawn->FindComponentByClass<UGameplayTasksComponent>();
|
|
if (GTComponent)
|
|
{
|
|
GameplayTasksState = FString::Printf(TEXT("Ticking Tasks: %s\nTask Queue: %s"), *GTComponent->GetTickingTasksDescription(), *GTComponent->GetTasksPriorityQueueDescription());
|
|
}
|
|
else
|
|
{
|
|
GameplayTasksState = TEXT("");
|
|
}
|
|
#endif //!(UE_BUILD_SHIPPING || UE_BUILD_TEST)
|
|
}
|
|
|
|
void UGameplayDebuggingComponent::CollectBasicAbilityData(APawn* MyPawn)
|
|
{
|
|
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
|
|
if (IGameplayAbilitiesModule::IsAvailable())
|
|
{
|
|
bool bUsingAbilities;
|
|
IGameplayAbilitiesModule& AbilitiesModule = FModuleManager::LoadModuleChecked<IGameplayAbilitiesModule>("GameplayAbilities");
|
|
AbilitiesModule.GetActiveAbilitiesDebugDataForActor(MyPawn, AbilityInfo, bUsingAbilities);
|
|
bIsUsingAbilities = bUsingAbilities;
|
|
}
|
|
else
|
|
{
|
|
bIsUsingAbilities = false;
|
|
AbilityInfo = TEXT("None");
|
|
}
|
|
#endif //!(UE_BUILD_SHIPPING || UE_BUILD_TEST)
|
|
}
|
|
|
|
void UGameplayDebuggingComponent::CollectBasicAnimationData(APawn* MyPawn)
|
|
{
|
|
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
|
|
ACharacter* MyChar = Cast<ACharacter>(MyPawn);
|
|
MontageInfo = MyChar ? GetNameSafe(MyChar->GetCurrentMontage()) : TEXT("");
|
|
#endif //!(UE_BUILD_SHIPPING || UE_BUILD_TEST)
|
|
}
|
|
|
|
void UGameplayDebuggingComponent::CollectBehaviorTreeData()
|
|
{
|
|
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
|
|
if (!ShouldReplicateData(EAIDebugDrawDataView::BehaviorTree))
|
|
{
|
|
return;
|
|
}
|
|
|
|
BrainComponentName = TEXT("");
|
|
BrainComponentString = TEXT("");
|
|
|
|
APawn* MyPawn = Cast<APawn>(GetSelectedActor());
|
|
AAIController* MyController = Cast<AAIController>(MyPawn->Controller);
|
|
if (MyController != NULL && MyController->BrainComponent != NULL && MyController->IsPendingKillPending() == false)
|
|
{
|
|
BrainComponentName = MyController->BrainComponent != NULL ? MyController->BrainComponent->GetName() : TEXT("");
|
|
BrainComponentString = MyController->BrainComponent != NULL ? MyController->BrainComponent->GetDebugInfoString() : TEXT("");
|
|
|
|
BlackboardString = MyController->BrainComponent->GetBlackboardComponent() ? MyController->BrainComponent->GetBlackboardComponent()->GetDebugInfoString(EBlackboardDescription::KeyWithValue) : TEXT("");
|
|
|
|
if (World && World->GetNetMode() != NM_Standalone)
|
|
{
|
|
TArray<uint8> UncompressedBuffer;
|
|
FMemoryWriter ArWriter(UncompressedBuffer);
|
|
|
|
ArWriter << BlackboardString;
|
|
|
|
const int32 HeaderSize = sizeof(int32);
|
|
BlackboardRepData.Init(0, HeaderSize + FMath::TruncToInt(1.1f * UncompressedBuffer.Num()));
|
|
|
|
const int32 UncompressedSize = UncompressedBuffer.Num();
|
|
int32 CompressedSize = BlackboardRepData.Num() - HeaderSize;
|
|
uint8* DestBuffer = BlackboardRepData.GetData();
|
|
FMemory::Memcpy(DestBuffer, &UncompressedSize, HeaderSize);
|
|
DestBuffer += HeaderSize;
|
|
|
|
FCompression::CompressMemory((ECompressionFlags)(COMPRESS_ZLIB | COMPRESS_BiasMemory),
|
|
(void*)DestBuffer, CompressedSize, (void*)UncompressedBuffer.GetData(), UncompressedSize);
|
|
|
|
BlackboardRepData.SetNum(CompressedSize + HeaderSize, false);
|
|
}
|
|
}
|
|
#endif //!(UE_BUILD_SHIPPING || UE_BUILD_TEST)
|
|
}
|
|
void UGameplayDebuggingComponent::OnRep_UpdateBlackboard()
|
|
{
|
|
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
|
|
if (World && World->GetNetMode() != NM_Standalone)
|
|
{
|
|
TArray<uint8> UncompressedBuffer;
|
|
int32 UncompressedSize = 0;
|
|
const int32 HeaderSize = sizeof(int32);
|
|
uint8* SrcBuffer = (uint8*)BlackboardRepData.GetData();
|
|
FMemory::Memcpy(&UncompressedSize, SrcBuffer, HeaderSize);
|
|
SrcBuffer += HeaderSize;
|
|
const int32 CompressedSize = BlackboardRepData.Num() - HeaderSize;
|
|
|
|
UncompressedBuffer.AddZeroed(UncompressedSize);
|
|
|
|
FCompression::UncompressMemory((ECompressionFlags)(COMPRESS_ZLIB), (void*)UncompressedBuffer.GetData(), UncompressedSize, SrcBuffer, CompressedSize);
|
|
FMemoryReader ArReader(UncompressedBuffer);
|
|
|
|
ArReader << BlackboardString;
|
|
}
|
|
#endif //!(UE_BUILD_SHIPPING || UE_BUILD_TEST)
|
|
}
|
|
|
|
void UGameplayDebuggingComponent::CollectPathData()
|
|
{
|
|
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST) && ENABLE_VISUAL_LOG
|
|
APawn* MyPawn = Cast<APawn>(GetSelectedActor());
|
|
|
|
bool bRefreshRendering = false;
|
|
if (AAIController *MyController = Cast<AAIController>(MyPawn->Controller))
|
|
{
|
|
if (MyController->PathFollowingComponent && MyController->PathFollowingComponent->GetPath().IsValid())
|
|
{
|
|
NextPathPointIndex = MyController->PathFollowingComponent->GetNextPathIndex();
|
|
|
|
const FNavPathSharedPtr& NewPath = MyController->PathFollowingComponent->GetPath();
|
|
if (CurrentPath.HasSameObject(NewPath.Get()) == false || NewPath->GetTimeStamp() > LastStoredPathTimeStamp)
|
|
{
|
|
LastStoredPathTimeStamp = NewPath->GetTimeStamp();
|
|
|
|
FVisualLogEntry Snapshot;
|
|
NewPath->DescribeSelfToVisLog(&Snapshot);
|
|
PathCorridorPolygons.Reset(Snapshot.ElementsToDraw.Num());
|
|
FPathCorridorPolygons Polygon;
|
|
for (FVisualLogShapeElement& CurrentShape : Snapshot.ElementsToDraw)
|
|
{
|
|
if (CurrentShape.GetType() == EVisualLoggerShapeElement::Polygon)
|
|
{
|
|
Polygon.Color = CurrentShape.GetFColor();
|
|
Polygon.Points.Reset();
|
|
Polygon.Points.Append(CurrentShape.Points);
|
|
PathCorridorPolygons.Add(Polygon);
|
|
}
|
|
}
|
|
|
|
PathPoints.Reset();
|
|
for (int32 Index=0; Index < NewPath->GetPathPoints().Num(); ++Index)
|
|
{
|
|
PathPoints.Add(NewPath->GetPathPoints()[Index].Location);
|
|
}
|
|
CurrentPath = NewPath;
|
|
|
|
if (PathCorridorPolygons.Num() && World && World->GetNetMode() != NM_Client)
|
|
{
|
|
PathCorridorData.Reset();
|
|
TArray<uint8> HelpBuffer;
|
|
FMemoryWriter ArWriter(HelpBuffer);
|
|
int32 NumPolygons = PathCorridorPolygons.Num();
|
|
ArWriter << NumPolygons;
|
|
for (const FPathCorridorPolygons& CurrentPoly : PathCorridorPolygons)
|
|
{
|
|
FColor Color = CurrentPoly.Color;
|
|
ArWriter << Color;
|
|
int32 NumVerts = CurrentPoly.Points.Num();
|
|
ArWriter << NumVerts;
|
|
for (auto Vert : CurrentPoly.Points)
|
|
{
|
|
ArWriter << Vert;
|
|
}
|
|
}
|
|
PathCorridorData = HelpBuffer;
|
|
}
|
|
bRefreshRendering = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
NextPathPointIndex = INDEX_NONE;
|
|
CurrentPath = NULL;
|
|
PathPoints.Reset();
|
|
PathCorridorPolygons.Reset();
|
|
}
|
|
}
|
|
|
|
if (bRefreshRendering && World && World->GetNetMode() != NM_DedicatedServer)
|
|
{
|
|
UpdateBounds();
|
|
MarkRenderStateDirty();
|
|
}
|
|
#endif //!(UE_BUILD_SHIPPING || UE_BUILD_TEST)
|
|
}
|
|
|
|
void UGameplayDebuggingComponent::OnRep_PathCorridorData()
|
|
{
|
|
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
|
|
if (World && World->GetNetMode() != NM_DedicatedServer)
|
|
{
|
|
FMemoryReader ArReader(PathCorridorData);
|
|
int32 NumPolygons = 0;
|
|
ArReader << NumPolygons;
|
|
PathCorridorPolygons.Reset(NumPolygons);
|
|
for (int32 PolyIndex = 0; PolyIndex < NumPolygons; ++PolyIndex)
|
|
{
|
|
FPathCorridorPolygons Polygon;
|
|
FColor Color;
|
|
ArReader << Polygon.Color;
|
|
|
|
int32 NumVerts = 0;
|
|
ArReader << NumVerts;
|
|
for (int32 VertIndex = 0; VertIndex < NumVerts; ++VertIndex)
|
|
{
|
|
FVector CurrentVertex;
|
|
ArReader << CurrentVertex;
|
|
Polygon.Points.Add(CurrentVertex);
|
|
}
|
|
if (NumVerts > 2)
|
|
{
|
|
PathCorridorPolygons.Add(Polygon);
|
|
}
|
|
}
|
|
UpdateBounds();
|
|
MarkRenderStateDirty();
|
|
}
|
|
#endif
|
|
}
|
|
|
|
|
|
void UGameplayDebuggingComponent::EnableDebugDraw(bool bEnable, bool InFocusedComponent)
|
|
{
|
|
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
|
|
#if WITH_EQS
|
|
EnableClientEQSSceneProxy(bEnable);
|
|
#endif // WITH_EQS
|
|
#endif //!(UE_BUILD_SHIPPING || UE_BUILD_TEST)
|
|
}
|
|
|
|
void UGameplayDebuggingComponent::ServerReplicateData(uint32 InMessage, uint32 InDataView)
|
|
{
|
|
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
|
|
EDebugComponentMessage::Type Message = (EDebugComponentMessage::Type)InMessage;
|
|
EAIDebugDrawDataView::Type DataView = (EAIDebugDrawDataView::Type) InDataView;
|
|
switch (Message)
|
|
{
|
|
case EDebugComponentMessage::EnableExtendedView:
|
|
ShowExtendedInformatiomCounter++;
|
|
break;
|
|
|
|
case EDebugComponentMessage::DisableExtendedView:
|
|
ShowExtendedInformatiomCounter = FMath::Max(0, ShowExtendedInformatiomCounter - 1);
|
|
break;
|
|
|
|
case EDebugComponentMessage::ActivateReplication:
|
|
Activate();
|
|
break;
|
|
|
|
case EDebugComponentMessage::DeactivateReplilcation:
|
|
Deactivate();
|
|
break;
|
|
|
|
case EDebugComponentMessage::ActivateDataView:
|
|
if (ReplicateViewDataCounters.IsValidIndex(DataView))
|
|
{
|
|
ReplicateViewDataCounters[DataView] += 1;
|
|
}
|
|
break;
|
|
|
|
case EDebugComponentMessage::DeactivateDataView:
|
|
if (ReplicateViewDataCounters.IsValidIndex(DataView))
|
|
{
|
|
ReplicateViewDataCounters[DataView] = FMath::Max(0, ReplicateViewDataCounters[DataView] - 1);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
#endif //!(UE_BUILD_SHIPPING || UE_BUILD_TEST)
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// EQS Data
|
|
//////////////////////////////////////////////////////////////////////////
|
|
void UGameplayDebuggingComponent::OnCycleDetailsView()
|
|
{
|
|
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
|
|
if (++CurrentEQSIndex >= EQSLocalData.Num())
|
|
{
|
|
CurrentEQSIndex = 0;
|
|
}
|
|
|
|
UpdateBounds();
|
|
MarkRenderStateDirty();
|
|
#endif //!(UE_BUILD_SHIPPING || UE_BUILD_TEST)
|
|
}
|
|
|
|
const FEnvQueryResult* UGameplayDebuggingComponent::GetQueryResult() const
|
|
{
|
|
return CachedQueryInstance.Get();
|
|
}
|
|
|
|
const FEnvQueryInstance* UGameplayDebuggingComponent::GetQueryInstance() const
|
|
{
|
|
return CachedQueryInstance.Get();
|
|
}
|
|
|
|
void UGameplayDebuggingComponent::OnRep_UpdateEQS()
|
|
{
|
|
#if USE_EQS_DEBUGGER
|
|
// decode scoring data
|
|
if (World && World->GetNetMode() == NM_Client)
|
|
{
|
|
TArray<uint8> UncompressedBuffer;
|
|
int32 UncompressedSize = 0;
|
|
const int32 HeaderSize = sizeof(int32);
|
|
uint8* SrcBuffer = (uint8*)EQSRepData.GetData();
|
|
FMemory::Memcpy(&UncompressedSize, SrcBuffer, HeaderSize);
|
|
SrcBuffer += HeaderSize;
|
|
const int32 CompressedSize = EQSRepData.Num() - HeaderSize;
|
|
|
|
UncompressedBuffer.AddZeroed(UncompressedSize);
|
|
|
|
FCompression::UncompressMemory((ECompressionFlags)(COMPRESS_ZLIB), (void*)UncompressedBuffer.GetData(), UncompressedSize, SrcBuffer, CompressedSize);
|
|
FMemoryReader ArReader(UncompressedBuffer);
|
|
|
|
ArReader << EQSLocalData;
|
|
}
|
|
|
|
UpdateBounds();
|
|
MarkRenderStateDirty();
|
|
#endif //USE_EQS_DEBUGGER
|
|
}
|
|
|
|
void UGameplayDebuggingComponent::CollectEQSData()
|
|
{
|
|
#if USE_EQS_DEBUGGER
|
|
if (!ShouldReplicateData(EAIDebugDrawDataView::EQS))
|
|
{
|
|
return;
|
|
}
|
|
|
|
UWorld* World = GetWorld();
|
|
UEnvQueryManager* QueryManager = World ? UEnvQueryManager::GetCurrent(World) : NULL;
|
|
const AActor* Owner = GetSelectedActor();
|
|
AGameplayDebuggingReplicator* Replicator = Cast<AGameplayDebuggingReplicator>(GetOwner());
|
|
|
|
if (QueryManager == NULL || Owner == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
auto AllQueries = QueryManager->GetDebugger().GetAllQueriesForOwner(Owner);
|
|
const class APawn* OwnerAsPawn = Cast<class APawn>(Owner);
|
|
if (OwnerAsPawn != NULL && OwnerAsPawn->GetController())
|
|
{
|
|
const auto& AllControllerQueries = QueryManager->GetDebugger().GetAllQueriesForOwner(OwnerAsPawn->GetController());
|
|
AllQueries.Append(AllControllerQueries);
|
|
}
|
|
struct FEnvQueryInfoSort
|
|
{
|
|
FORCEINLINE bool operator()(const FEQSDebugger::FEnvQueryInfo& A, const FEQSDebugger::FEnvQueryInfo& B) const
|
|
{
|
|
return (A.Timestamp < B.Timestamp);
|
|
}
|
|
};
|
|
TArray<FEQSDebugger::FEnvQueryInfo> QueriesToSort = AllQueries;
|
|
QueriesToSort.Sort(FEnvQueryInfoSort()); //sort queries by timestamp
|
|
QueriesToSort.SetNum(FMath::Min<int32>(Replicator->MaxEQSQueries, AllQueries.Num()));
|
|
|
|
for (int32 Index = AllQueries.Num() - 1; Index >= 0; --Index)
|
|
{
|
|
auto &CurrentQuery = AllQueries[Index];
|
|
if (QueriesToSort.Find(CurrentQuery) == INDEX_NONE)
|
|
{
|
|
AllQueries.RemoveAt(Index);
|
|
}
|
|
}
|
|
|
|
|
|
EQSLocalData.Reset();
|
|
for (int32 Index = 0; Index < FMath::Min<int32>(Replicator->MaxEQSQueries, AllQueries.Num()); ++Index)
|
|
{
|
|
EQSDebug::FQueryData* CurrentLocalData = NULL;
|
|
CachedQueryInstance = AllQueries[Index].Instance;
|
|
const float CachedTimestamp = AllQueries[Index].Timestamp;
|
|
|
|
if (!CurrentLocalData)
|
|
{
|
|
EQSLocalData.AddZeroed();
|
|
CurrentLocalData = &EQSLocalData[EQSLocalData.Num()-1];
|
|
}
|
|
|
|
if (CachedQueryInstance.IsValid())
|
|
{
|
|
UEnvQueryDebugHelpers::QueryToDebugData(*CachedQueryInstance, *CurrentLocalData);
|
|
}
|
|
CurrentLocalData->Timestamp = AllQueries[Index].Timestamp;
|
|
}
|
|
|
|
TArray<uint8> UncompressedBuffer;
|
|
FMemoryWriter ArWriter(UncompressedBuffer);
|
|
|
|
ArWriter << EQSLocalData;
|
|
|
|
const int32 UncompressedSize = UncompressedBuffer.Num();
|
|
const int32 HeaderSize = sizeof(int32);
|
|
EQSRepData.Init(0, HeaderSize + FMath::TruncToInt(1.1f * UncompressedSize));
|
|
|
|
int32 CompressedSize = EQSRepData.Num() - HeaderSize;
|
|
uint8* DestBuffer = EQSRepData.GetData();
|
|
FMemory::Memcpy(DestBuffer, &UncompressedSize, HeaderSize);
|
|
DestBuffer += HeaderSize;
|
|
|
|
FCompression::CompressMemory((ECompressionFlags)(COMPRESS_ZLIB | COMPRESS_BiasMemory), (void*)DestBuffer, CompressedSize, (void*)UncompressedBuffer.GetData(), UncompressedSize);
|
|
|
|
EQSRepData.SetNum(CompressedSize + HeaderSize, false);
|
|
|
|
if (World && World->GetNetMode() != NM_DedicatedServer)
|
|
{
|
|
OnRep_UpdateEQS();
|
|
}
|
|
#endif
|
|
}
|
|
|
|
//----------------------------------------------------------------------//
|
|
// NavMesh rendering
|
|
//----------------------------------------------------------------------//
|
|
void UGameplayDebuggingComponent::OnRep_UpdateNavmesh()
|
|
{
|
|
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
|
|
NavMeshBounds = FBox(FVector(-HALF_WORLD_MAX1, -HALF_WORLD_MAX1, -HALF_WORLD_MAX1), FVector(HALF_WORLD_MAX1, HALF_WORLD_MAX1, HALF_WORLD_MAX1));
|
|
UpdateBounds();
|
|
MarkRenderStateDirty();
|
|
#endif //!(UE_BUILD_SHIPPING || UE_BUILD_TEST)
|
|
}
|
|
|
|
bool UGameplayDebuggingComponent::ServerCollectNavmeshData_Validate(FVector_NetQuantize10 TargetLocation)
|
|
{
|
|
bool bIsValid = false;
|
|
#if WITH_RECAST
|
|
UNavigationSystem* NavSys = UNavigationSystem::GetCurrent(GetWorld());
|
|
bIsValid = NavSys && NavSys->GetMainNavData(FNavigationSystem::DontCreate);
|
|
#endif
|
|
|
|
return bIsValid;
|
|
}
|
|
|
|
bool UGameplayDebuggingComponent::ServerDiscardNavmeshData_Validate()
|
|
{
|
|
return true;
|
|
}
|
|
|
|
namespace NavMeshDebug
|
|
{
|
|
struct FShortVector
|
|
{
|
|
int16 X;
|
|
int16 Y;
|
|
int16 Z;
|
|
|
|
FShortVector()
|
|
:X(0), Y(0), Z(0)
|
|
{
|
|
|
|
}
|
|
|
|
FShortVector(const FVector& Vec)
|
|
: X(Vec.X)
|
|
, Y(Vec.Y)
|
|
, Z(Vec.Z)
|
|
{
|
|
|
|
}
|
|
FShortVector& operator=(const FVector& R)
|
|
{
|
|
X = R.X;
|
|
Y = R.Y;
|
|
Z = R.Z;
|
|
return *this;
|
|
}
|
|
|
|
FVector ToVector() const
|
|
{
|
|
return FVector(X, Y, Z);
|
|
}
|
|
};
|
|
|
|
struct FOffMeshLinkFlags
|
|
{
|
|
uint8 Radius : 6;
|
|
uint8 Direction : 1;
|
|
uint8 ValidEnds : 1;
|
|
};
|
|
struct FOffMeshLink
|
|
{
|
|
FShortVector Left;
|
|
FShortVector Right;
|
|
FColor Color;
|
|
union
|
|
{
|
|
FOffMeshLinkFlags PackedFlags;
|
|
uint8 ByteFlags;
|
|
};
|
|
};
|
|
|
|
struct FAreaPolys
|
|
{
|
|
TArray<int32> Indices;
|
|
FColor Color;
|
|
};
|
|
|
|
struct FTileData
|
|
{
|
|
FVector Location;
|
|
TArray<FAreaPolys> Areas;
|
|
TArray<FShortVector> Verts;
|
|
TArray<FOffMeshLink> Links;
|
|
};
|
|
}
|
|
|
|
FArchive& operator<<(FArchive& Ar, NavMeshDebug::FShortVector& ShortVector)
|
|
{
|
|
Ar << ShortVector.X;
|
|
Ar << ShortVector.Y;
|
|
Ar << ShortVector.Z;
|
|
return Ar;
|
|
}
|
|
|
|
FArchive& operator<<(FArchive& Ar, NavMeshDebug::FOffMeshLink& Data)
|
|
{
|
|
Ar << Data.Left;
|
|
Ar << Data.Right;
|
|
Ar << Data.Color.R;
|
|
Ar << Data.Color.G;
|
|
Ar << Data.Color.B;
|
|
Ar << Data.ByteFlags;
|
|
return Ar;
|
|
}
|
|
|
|
FArchive& operator<<(FArchive& Ar, NavMeshDebug::FAreaPolys& Data)
|
|
{
|
|
Ar << Data.Indices;
|
|
Ar << Data.Color.R;
|
|
Ar << Data.Color.G;
|
|
Ar << Data.Color.B;
|
|
Data.Color.A = 255;
|
|
return Ar;
|
|
}
|
|
|
|
|
|
FArchive& operator<<(FArchive& Ar, NavMeshDebug::FTileData& Data)
|
|
{
|
|
Ar << Data.Areas;
|
|
Ar << Data.Location;
|
|
Ar << Data.Verts;
|
|
Ar << Data.Links;
|
|
return Ar;
|
|
}
|
|
|
|
void UGameplayDebuggingComponent::ServerDiscardNavmeshData_Implementation()
|
|
{
|
|
NavmeshRepData.Empty();
|
|
}
|
|
|
|
FORCEINLINE bool LineInCorrectDistance(const FVector& PlayerLoc, const FVector& Start, const FVector& End, float CorrectDistance = -1)
|
|
{
|
|
const float MaxDistance = CorrectDistance > 0 ? (CorrectDistance*CorrectDistance) : ARecastNavMesh::GetDrawDistanceSq();
|
|
|
|
if ((FVector::DistSquared(Start, PlayerLoc) > MaxDistance || FVector::DistSquared(End, PlayerLoc) > MaxDistance))
|
|
{
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
#if WITH_RECAST
|
|
ARecastNavMesh* UGameplayDebuggingComponent::GetNavData()
|
|
{
|
|
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
|
|
UNavigationSystem* NavSys = UNavigationSystem::GetCurrent(GetWorld());
|
|
if (NavSys == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
// Try to get the correct nav-mesh relative to the selected actor.
|
|
APawn* TargetPawn = Cast<APawn>(TargetActor);
|
|
if (TargetPawn != NULL)
|
|
{
|
|
const FNavAgentProperties& NavAgentProperties = TargetPawn->GetNavAgentPropertiesRef();
|
|
return Cast<ARecastNavMesh>(NavSys->GetNavDataForProps(NavAgentProperties));
|
|
}
|
|
|
|
// If it wasn't found, just get the main nav-mesh data.
|
|
return Cast<ARecastNavMesh>(NavSys->GetMainNavData(FNavigationSystem::DontCreate));
|
|
#else
|
|
return NULL;
|
|
#endif //!(UE_BUILD_SHIPPING || UE_BUILD_TEST)
|
|
}
|
|
#endif
|
|
|
|
void UGameplayDebuggingComponent::ServerCollectNavmeshData_Implementation(FVector_NetQuantize10 TargetLocation)
|
|
{
|
|
#if WITH_RECAST
|
|
UNavigationSystem* NavSys = UNavigationSystem::GetCurrent(GetWorld());
|
|
ARecastNavMesh* NavData = GetNavData();
|
|
if (NavData == NULL)
|
|
{
|
|
NavmeshRepData.Empty();
|
|
return;
|
|
}
|
|
|
|
const double Timer1 = FPlatformTime::Seconds();
|
|
|
|
TArray<int32> Indices;
|
|
int32 TileX = 0;
|
|
int32 TileY = 0;
|
|
const int32 DeltaX[] = { 0, 1, 1, 0, -1, -1, -1, 0, 1 };
|
|
const int32 DeltaY[] = { 0, 0, 1, 1, 1, 0, -1, -1, -1 };
|
|
|
|
NavData->BeginBatchQuery();
|
|
|
|
// add 3x3 neighborhood of target
|
|
int32 TargetTileX = 0;
|
|
int32 TargetTileY = 0;
|
|
NavData->GetNavMeshTileXY(TargetLocation, TargetTileX, TargetTileY);
|
|
for (int32 i = 0; i < ARRAY_COUNT(DeltaX); i++)
|
|
{
|
|
const int32 NeiX = TargetTileX + DeltaX[i];
|
|
const int32 NeiY = TargetTileY + DeltaY[i];
|
|
|
|
NavData->GetNavMeshTilesAt(NeiX, NeiY, Indices);
|
|
}
|
|
|
|
const FNavDataConfig& NavConfig = NavData->GetConfig();
|
|
FColor NavMeshColors[RECAST_MAX_AREAS];
|
|
NavMeshColors[RECAST_DEFAULT_AREA] = NavConfig.Color.DWColor() > 0 ? NavConfig.Color : NavMeshRenderColor_RecastMesh;
|
|
for (uint8 i = 0; i < RECAST_DEFAULT_AREA; ++i)
|
|
{
|
|
NavMeshColors[i] = NavData->GetAreaIDColor(i);
|
|
}
|
|
|
|
TArray<uint8> UncompressedBuffer;
|
|
FMemoryWriter ArWriter(UncompressedBuffer);
|
|
|
|
int32 NumIndices = Indices.Num();
|
|
ArWriter << NumIndices;
|
|
|
|
for (int32 i = 0; i < NumIndices; i++)
|
|
{
|
|
FRecastDebugGeometry NavMeshGeometry;
|
|
NavMeshGeometry.bGatherPolyEdges = false;
|
|
NavMeshGeometry.bGatherNavMeshEdges = false;
|
|
NavData->GetDebugGeometry(NavMeshGeometry, Indices[i]);
|
|
|
|
NavMeshDebug::FTileData TileData;
|
|
const FBox TileBoundingBox = NavData->GetNavMeshTileBounds(Indices[i]);
|
|
TileData.Location = TileBoundingBox.GetCenter();
|
|
for (int32 VertIndex = 0; VertIndex < NavMeshGeometry.MeshVerts.Num(); ++VertIndex)
|
|
{
|
|
const NavMeshDebug::FShortVector SV = NavMeshGeometry.MeshVerts[VertIndex] - TileData.Location;
|
|
TileData.Verts.Add(SV);
|
|
}
|
|
|
|
for (int32 iArea = 0; iArea < RECAST_MAX_AREAS; iArea++)
|
|
{
|
|
const int32 NumTris = NavMeshGeometry.AreaIndices[iArea].Num();
|
|
if (NumTris)
|
|
{
|
|
NavMeshDebug::FAreaPolys AreaPolys;
|
|
for (int32 AreaIndicesIndex = 0; AreaIndicesIndex < NavMeshGeometry.AreaIndices[iArea].Num(); ++AreaIndicesIndex)
|
|
{
|
|
AreaPolys.Indices.Add(NavMeshGeometry.AreaIndices[iArea][AreaIndicesIndex]);
|
|
}
|
|
AreaPolys.Color = NavMeshColors[iArea];
|
|
TileData.Areas.Add(AreaPolys);
|
|
}
|
|
}
|
|
|
|
TileData.Links.Reserve(NavMeshGeometry.OffMeshLinks.Num());
|
|
for (int32 iLink = 0; iLink < NavMeshGeometry.OffMeshLinks.Num(); iLink++)
|
|
{
|
|
const FRecastDebugGeometry::FOffMeshLink& SrcLink = NavMeshGeometry.OffMeshLinks[iLink];
|
|
NavMeshDebug::FOffMeshLink Link;
|
|
Link.Left = SrcLink.Left - TileData.Location;
|
|
Link.Right = SrcLink.Right - TileData.Location;
|
|
Link.Color = ((SrcLink.Direction && SrcLink.ValidEnds) || (SrcLink.ValidEnds & FRecastDebugGeometry::OMLE_Left)) ? DarkenColor(NavMeshColors[SrcLink.AreaID]) : NavMeshRenderColor_OffMeshConnectionInvalid;
|
|
Link.PackedFlags.Radius = (int8)SrcLink.Radius;
|
|
Link.PackedFlags.Direction = SrcLink.Direction;
|
|
Link.PackedFlags.ValidEnds = SrcLink.ValidEnds;
|
|
TileData.Links.Add(Link);
|
|
}
|
|
|
|
ArWriter << TileData;
|
|
}
|
|
NavData->FinishBatchQuery();
|
|
|
|
const double Timer2 = FPlatformTime::Seconds();
|
|
|
|
const int32 HeaderSize = sizeof(int32);
|
|
NavmeshRepData.Init(0, HeaderSize + FMath::TruncToInt(1.1f * UncompressedBuffer.Num()));
|
|
|
|
const int32 UncompressedSize = UncompressedBuffer.Num();
|
|
int32 CompressedSize = NavmeshRepData.Num() - HeaderSize;
|
|
uint8* DestBuffer = NavmeshRepData.GetData();
|
|
FMemory::Memcpy(DestBuffer, &UncompressedSize, HeaderSize);
|
|
DestBuffer += HeaderSize;
|
|
|
|
FCompression::CompressMemory((ECompressionFlags)(COMPRESS_ZLIB | COMPRESS_BiasMemory),
|
|
(void*)DestBuffer, CompressedSize, (void*)UncompressedBuffer.GetData(), UncompressedSize);
|
|
|
|
NavmeshRepData.SetNum(CompressedSize + HeaderSize, false);
|
|
|
|
const double Timer3 = FPlatformTime::Seconds();
|
|
UE_LOG(LogGameplayDebugger, Log, TEXT("Preparing navmesh data: %.1fkB took %.3fms (collect: %.3fms + compress %d%%: %.3fms)"),
|
|
NavmeshRepData.Num() / 1024.0f, 1000.0f * (Timer3 - Timer1),
|
|
1000.0f * (Timer2 - Timer1),
|
|
FMath::TruncToInt(100.0f * NavmeshRepData.Num() / UncompressedBuffer.Num()), 1000.0f * (Timer3 - Timer2));
|
|
#endif
|
|
|
|
if (World && World->GetNetMode() != NM_DedicatedServer)
|
|
{
|
|
OnRep_UpdateNavmesh();
|
|
}
|
|
}
|
|
|
|
void UGameplayDebuggingComponent::PrepareNavMeshData(struct FNavMeshSceneProxyData* CurrentData) const
|
|
{
|
|
#if WITH_RECAST
|
|
if (CurrentData)
|
|
{
|
|
CurrentData->Reset();
|
|
CurrentData->bNeedsNewData = false;
|
|
|
|
// uncompress data
|
|
TArray<uint8> UncompressedBuffer;
|
|
const int32 HeaderSize = sizeof(int32);
|
|
if (NavmeshRepData.Num() > HeaderSize)
|
|
{
|
|
int32 UncompressedSize = 0;
|
|
uint8* SrcBuffer = (uint8*)NavmeshRepData.GetData();
|
|
FMemory::Memcpy(&UncompressedSize, SrcBuffer, HeaderSize);
|
|
SrcBuffer += HeaderSize;
|
|
const int32 CompressedSize = NavmeshRepData.Num() - HeaderSize;
|
|
|
|
UncompressedBuffer.AddZeroed(UncompressedSize);
|
|
|
|
FCompression::UncompressMemory((ECompressionFlags)(COMPRESS_ZLIB),
|
|
(void*)UncompressedBuffer.GetData(), UncompressedSize, SrcBuffer, CompressedSize);
|
|
}
|
|
|
|
// read serialized values
|
|
CurrentData->bEnableDrawing = (UncompressedBuffer.Num() > 0);
|
|
if (!CurrentData->bEnableDrawing)
|
|
{
|
|
return;
|
|
}
|
|
|
|
FMemoryReader ArReader(UncompressedBuffer);
|
|
int32 NumTiles = 0;
|
|
ArReader << NumTiles;
|
|
|
|
int32 IndexesOffset = 0;
|
|
for (int32 iTile = 0; iTile < NumTiles; iTile++)
|
|
{
|
|
NavMeshDebug::FTileData TileData;
|
|
ArReader << TileData;
|
|
|
|
FVector OffsetLocation = TileData.Location;
|
|
TArray<FVector> Verts;
|
|
Verts.Reserve(TileData.Verts.Num());
|
|
for (int32 VertIndex = 0; VertIndex < TileData.Verts.Num(); ++VertIndex)
|
|
{
|
|
const FVector Loc = TileData.Verts[VertIndex].ToVector() + OffsetLocation;
|
|
Verts.Add(Loc);
|
|
}
|
|
CurrentData->Bounds += FBox(Verts);
|
|
|
|
for (int32 iArea = 0; iArea < TileData.Areas.Num(); iArea++)
|
|
{
|
|
const NavMeshDebug::FAreaPolys& SrcArea = TileData.Areas[iArea];
|
|
FNavMeshSceneProxyData::FDebugMeshData DebugMeshData;
|
|
DebugMeshData.ClusterColor = SrcArea.Color;
|
|
DebugMeshData.ClusterColor.A = 128;
|
|
|
|
for (int32 iTri = 0; iTri < SrcArea.Indices.Num(); iTri += 3)
|
|
{
|
|
const int32 Index0 = SrcArea.Indices[iTri + 0];
|
|
const int32 Index1 = SrcArea.Indices[iTri + 1];
|
|
const int32 Index2 = SrcArea.Indices[iTri + 2];
|
|
|
|
FVector V0 = Verts[Index0];
|
|
FVector V1 = Verts[Index1];
|
|
FVector V2 = Verts[Index2];
|
|
CurrentData->TileEdgeLines.Add(FDebugRenderSceneProxy::FDebugLine(V0 + CurrentData->NavMeshDrawOffset, V1 + CurrentData->NavMeshDrawOffset, NavMeshRenderColor_Recast_TileEdges));
|
|
CurrentData->TileEdgeLines.Add(FDebugRenderSceneProxy::FDebugLine(V1 + CurrentData->NavMeshDrawOffset, V2 + CurrentData->NavMeshDrawOffset, NavMeshRenderColor_Recast_TileEdges));
|
|
CurrentData->TileEdgeLines.Add(FDebugRenderSceneProxy::FDebugLine(V2 + CurrentData->NavMeshDrawOffset, V0 + CurrentData->NavMeshDrawOffset, NavMeshRenderColor_Recast_TileEdges));
|
|
|
|
AddTriangleHelper(DebugMeshData, Index0 + IndexesOffset, Index1 + IndexesOffset, Index2 + IndexesOffset);
|
|
}
|
|
|
|
for (int32 iVert = 0; iVert < Verts.Num(); iVert++)
|
|
{
|
|
AddVertexHelper(DebugMeshData, Verts[iVert] + CurrentData->NavMeshDrawOffset);
|
|
}
|
|
CurrentData->MeshBuilders.Add(DebugMeshData);
|
|
IndexesOffset += Verts.Num();
|
|
}
|
|
|
|
for (int32 i = 0; i < TileData.Links.Num(); i++)
|
|
{
|
|
const NavMeshDebug::FOffMeshLink& SrcLink = TileData.Links[i];
|
|
|
|
const FVector V0 = SrcLink.Left.ToVector() + OffsetLocation + CurrentData->NavMeshDrawOffset;
|
|
const FVector V1 = SrcLink.Right.ToVector() + OffsetLocation + CurrentData->NavMeshDrawOffset;
|
|
const FColor LinkColor = SrcLink.Color;
|
|
|
|
CacheArc(CurrentData->NavLinkLines, V0, V1, 0.4f, 4, LinkColor, LinkLines_LineThickness);
|
|
|
|
const FVector VOffset(0, 0, FVector::Dist(V0, V1) * 1.333f);
|
|
CacheArrowHead(CurrentData->NavLinkLines, V1, V0+VOffset, 30.f, LinkColor, LinkLines_LineThickness);
|
|
if (SrcLink.PackedFlags.Direction)
|
|
{
|
|
CacheArrowHead(CurrentData->NavLinkLines, V0, V1+VOffset, 30.f, LinkColor, LinkLines_LineThickness);
|
|
}
|
|
|
|
// if the connection as a whole is valid check if there are any of ends is invalid
|
|
if (LinkColor != NavMeshRenderColor_OffMeshConnectionInvalid)
|
|
{
|
|
if (SrcLink.PackedFlags.Direction && (SrcLink.PackedFlags.ValidEnds & FRecastDebugGeometry::OMLE_Left) == 0)
|
|
{
|
|
// left end invalid - mark it
|
|
DrawWireCylinder(CurrentData->NavLinkLines, V0, FVector(1, 0, 0), FVector(0, 1, 0), FVector(0, 0, 1), NavMeshRenderColor_OffMeshConnectionInvalid, SrcLink.PackedFlags.Radius, 30 /*NavMesh->AgentMaxStepHeight*/, 16, 0, DefaultEdges_LineThickness);
|
|
}
|
|
|
|
if ((SrcLink.PackedFlags.ValidEnds & FRecastDebugGeometry::OMLE_Right) == 0)
|
|
{
|
|
DrawWireCylinder(CurrentData->NavLinkLines, V1, FVector(1, 0, 0), FVector(0, 1, 0), FVector(0, 0, 1), NavMeshRenderColor_OffMeshConnectionInvalid, SrcLink.PackedFlags.Radius, 30 /*NavMesh->AgentMaxStepHeight*/, 16, 0, DefaultEdges_LineThickness);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
//----------------------------------------------------------------------//
|
|
// rendering
|
|
//----------------------------------------------------------------------//
|
|
class FPathDebugRenderSceneProxy : public FDebugRenderSceneProxy
|
|
{
|
|
public:
|
|
FPathDebugRenderSceneProxy(const UPrimitiveComponent* InComponent, const FString &InViewFlagName)
|
|
: FDebugRenderSceneProxy(InComponent)
|
|
{
|
|
DrawType = FDebugRenderSceneProxy::SolidAndWireMeshes;
|
|
DrawAlpha = 90;
|
|
ViewFlagName = InViewFlagName;
|
|
ViewFlagIndex = uint32(FEngineShowFlags::FindIndexByName(*ViewFlagName));
|
|
bWantsSelectionOutline = false;
|
|
}
|
|
|
|
|
|
FPrimitiveViewRelevance GetViewRelevance(const FSceneView* View) const override
|
|
{
|
|
FPrimitiveViewRelevance Result;
|
|
Result.bDrawRelevance = View->Family->EngineShowFlags.GetSingleFlag(ViewFlagIndex);// IsShown(View);
|
|
Result.bDynamicRelevance = true;
|
|
// ideally the TranslucencyRelevance should be filled out by the material, here we do it conservative
|
|
Result.bSeparateTranslucencyRelevance = Result.bNormalTranslucencyRelevance = IsShown(View);
|
|
return Result;
|
|
}
|
|
|
|
};
|
|
|
|
FPrimitiveSceneProxy* UGameplayDebuggingComponent::CreateSceneProxy()
|
|
{
|
|
FDebugRenderSceneCompositeProxy* CompositeProxy = NULL;
|
|
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
|
|
AGameplayDebuggingReplicator* Replicator = Cast<AGameplayDebuggingReplicator>(GetOwner());
|
|
if (!World || World->GetNetMode() == NM_DedicatedServer)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
if (!Replicator || !Replicator->IsDrawEnabled() || Replicator->IsPendingKill() || IsPendingKill())
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
#if WITH_RECAST
|
|
if (ShouldReplicateData(EAIDebugDrawDataView::NavMesh))
|
|
{
|
|
FNavMeshSceneProxyData NewNavmeshRenderData;
|
|
NewNavmeshRenderData.Reset();
|
|
NewNavmeshRenderData.bNeedsNewData = false;
|
|
NewNavmeshRenderData.bEnableDrawing = false;
|
|
PrepareNavMeshData(&NewNavmeshRenderData);
|
|
|
|
NavMeshBounds = NewNavmeshRenderData.Bounds.GetCenter().ContainsNaN() || NewNavmeshRenderData.Bounds.GetExtent().ContainsNaN() ? FBox(FVector(-HALF_WORLD_MAX1, -HALF_WORLD_MAX1, -HALF_WORLD_MAX1), FVector(HALF_WORLD_MAX1, HALF_WORLD_MAX1, HALF_WORLD_MAX1)) : NewNavmeshRenderData.Bounds;
|
|
CompositeProxy = CompositeProxy ? CompositeProxy : (new FDebugRenderSceneCompositeProxy(this));
|
|
CompositeProxy->AddChild(new FRecastRenderingSceneProxy(this, &NewNavmeshRenderData, true));
|
|
}
|
|
#endif
|
|
|
|
#if USE_EQS_DEBUGGER
|
|
if (ShouldReplicateData(EAIDebugDrawDataView::EQS) && IsClientEQSSceneProxyEnabled() && GetSelectedActor() != NULL)
|
|
{
|
|
const int32 EQSIndex = EQSLocalData.Num() > 0 ? FMath::Clamp(CurrentEQSIndex, 0, EQSLocalData.Num() - 1) : INDEX_NONE;
|
|
if (EQSLocalData.IsValidIndex(EQSIndex))
|
|
{
|
|
CompositeProxy = CompositeProxy ? CompositeProxy : (new FDebugRenderSceneCompositeProxy(this));
|
|
auto& CurrentLocalData = EQSLocalData[EQSIndex];
|
|
|
|
FString ViewFlagName = TEXT("Game");
|
|
#if WITH_EDITOR
|
|
UEditorEngine* EEngine = Cast<UEditorEngine>(GEngine);
|
|
if (EEngine && EEngine->bIsSimulatingInEditor)
|
|
{
|
|
ViewFlagName = TEXT("DebugAI");
|
|
}
|
|
#endif
|
|
CompositeProxy->AddChild(new FEQSSceneProxy(this, ViewFlagName, CurrentLocalData.SolidSpheres, CurrentLocalData.Texts));
|
|
}
|
|
}
|
|
#endif // USE_EQS_DEBUGGER
|
|
|
|
const bool bDrawFullData = Replicator->GetSelectedActorToDebug() == GetSelectedActor() && GetSelectedActor() != NULL;
|
|
if (bDrawFullData && ShouldReplicateData(EAIDebugDrawDataView::Basic))
|
|
{
|
|
CompositeProxy = CompositeProxy ? CompositeProxy : (new FDebugRenderSceneCompositeProxy(this));
|
|
|
|
TArray<FDebugRenderSceneProxy::FMesh> Meshes;
|
|
TArray<FDebugRenderSceneProxy::FDebugLine> Lines;
|
|
for (FPathCorridorPolygons& CurrentPoly : PathCorridorPolygons)
|
|
{
|
|
|
|
if (CurrentPoly.Points.Num() > 2)
|
|
{
|
|
int32 LastIndex = 0;
|
|
FVector FirstVertex = CurrentPoly.Points[0];
|
|
FDebugRenderSceneProxy::FMesh TestMesh;
|
|
TestMesh.Color = CurrentPoly.Color;// FColor::Green;
|
|
for (int32 Index = 1; Index < CurrentPoly.Points.Num()-1; Index += 1)
|
|
{
|
|
TestMesh.Vertices.Add(FDynamicMeshVertex(FirstVertex));
|
|
TestMesh.Vertices.Add(FDynamicMeshVertex(CurrentPoly.Points[Index + 0]));
|
|
TestMesh.Vertices.Add(FDynamicMeshVertex(CurrentPoly.Points[Index + 1]));
|
|
TestMesh.Indices.Add(LastIndex++);
|
|
TestMesh.Indices.Add(LastIndex++);
|
|
TestMesh.Indices.Add(LastIndex++);
|
|
}
|
|
Meshes.Add(TestMesh);
|
|
}
|
|
|
|
for (int32 VIdx = 0; VIdx < CurrentPoly.Points.Num(); VIdx++)
|
|
{
|
|
Lines.Add(FDebugRenderSceneProxy::FDebugLine(
|
|
CurrentPoly.Points[VIdx],
|
|
CurrentPoly.Points[(VIdx + 1) % CurrentPoly.Points.Num()],
|
|
CurrentPoly.Color,
|
|
2)
|
|
);
|
|
}
|
|
}
|
|
|
|
FString ViewFlagName = TEXT("Game");
|
|
#if WITH_EDITOR
|
|
UEditorEngine* EEngine = Cast<UEditorEngine>(GEngine);
|
|
if (EEngine && EEngine->bIsSimulatingInEditor)
|
|
{
|
|
ViewFlagName = TEXT("DebugAI");
|
|
}
|
|
#endif
|
|
FPathDebugRenderSceneProxy *DebugSceneProxy = new FPathDebugRenderSceneProxy(this, ViewFlagName);
|
|
DebugSceneProxy->Lines = Lines;
|
|
DebugSceneProxy->Meshes = Meshes;
|
|
CompositeProxy->AddChild(DebugSceneProxy);
|
|
}
|
|
|
|
#endif //!(UE_BUILD_SHIPPING || UE_BUILD_TEST)
|
|
return CompositeProxy;
|
|
}
|
|
|
|
FBoxSphereBounds UGameplayDebuggingComponent::CalcBounds(const FTransform& LocalToWorld) const
|
|
{
|
|
FBox MyBounds;
|
|
MyBounds.Init();
|
|
|
|
#if WITH_RECAST
|
|
if (ShouldReplicateData(EAIDebugDrawDataView::NavMesh))
|
|
{
|
|
MyBounds = NavMeshBounds;
|
|
}
|
|
#endif
|
|
|
|
#if USE_EQS_DEBUGGER
|
|
if ((EQSRepData.Num() && ShouldReplicateData(EAIDebugDrawDataView::EQS)) || PathCorridorPolygons.Num())
|
|
{
|
|
MyBounds = FBox(FVector(-HALF_WORLD_MAX1, -HALF_WORLD_MAX1, -HALF_WORLD_MAX1), FVector(HALF_WORLD_MAX1, HALF_WORLD_MAX1, HALF_WORLD_MAX1));
|
|
}
|
|
#endif // USE_EQS_DEBUGGER
|
|
|
|
return MyBounds;
|
|
}
|
|
|
|
void UGameplayDebuggingComponent::CreateRenderState_Concurrent()
|
|
{
|
|
Super::CreateRenderState_Concurrent();
|
|
|
|
#if WITH_EDITOR
|
|
if (SceneProxy)
|
|
{
|
|
static_cast<FDebugRenderSceneCompositeProxy*>(SceneProxy)->RegisterDebugDrawDelgate();
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void UGameplayDebuggingComponent::DestroyRenderState_Concurrent()
|
|
{
|
|
#if WITH_EDITOR
|
|
if (SceneProxy)
|
|
{
|
|
static_cast<FDebugRenderSceneCompositeProxy*>(SceneProxy)->UnregisterDebugDrawDelgate();
|
|
}
|
|
#endif
|
|
|
|
Super::DestroyRenderState_Concurrent();
|
|
}
|
|
|
|
void UGameplayDebuggingComponent::CollectPerceptionData()
|
|
{
|
|
#if USE_EQS_DEBUGGER
|
|
if (!ShouldReplicateData(EAIDebugDrawDataView::Perception))
|
|
{
|
|
return;
|
|
}
|
|
|
|
APawn* MyPawn = Cast<APawn>(GetSelectedActor());
|
|
if (MyPawn)
|
|
{
|
|
AAIController* BTAI = Cast<AAIController>(MyPawn->GetController());
|
|
if (BTAI)
|
|
{
|
|
const UAIPerceptionComponent* PerceptionComponent = BTAI->GetAIPerceptionComponent();
|
|
if (PerceptionComponent == nullptr)
|
|
{
|
|
PerceptionComponent = MyPawn->FindComponentByClass<UAIPerceptionComponent>();
|
|
}
|
|
if (PerceptionComponent)
|
|
{
|
|
TArray<FString> PerceptionTexts;
|
|
PerceptionShapeElements.Reset();
|
|
PerceptionComponent->GrabGameplayDebuggerData(PerceptionTexts, PerceptionShapeElements);
|
|
|
|
DistanceFromPlayer = DistanceFromSensor = -1;
|
|
|
|
AGameplayDebuggingReplicator* Replicator = Cast<AGameplayDebuggingReplicator>(GetOwner());
|
|
UGameplayDebuggingControllerComponent* GDC = Replicator ? Replicator->FindComponentByClass<UGameplayDebuggingControllerComponent>() : nullptr;
|
|
APlayerController* MyPC = GDC && GDC->GetDebugCameraController().IsValid() ? GDC->GetDebugCameraController().Get() : Replicator->GetLocalPlayerOwner();
|
|
|
|
if (MyPC && MyPC->GetPawn())
|
|
{
|
|
DistanceFromPlayer = (MyPawn->GetActorLocation() - MyPC->GetPawn()->GetActorLocation()).Size();
|
|
DistanceFromSensor = SensingComponentLocation != FVector::ZeroVector ? (SensingComponentLocation - MyPC->GetPawn()->GetActorLocation()).Size() : -1;
|
|
}
|
|
}
|
|
|
|
UAIPerceptionSystem* PerceptionSys = UAIPerceptionSystem::GetCurrent(MyPawn->GetWorld());
|
|
if (PerceptionSys)
|
|
{
|
|
PerceptionLegend = PerceptionSys->GetPerceptionDebugLegend();
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
}
|