Files
UnrealEngine/Engine/Source/Runtime/GameplayDebugger/Public/GameplayDebuggerCategory.h
2025-05-18 13:04:45 +08:00

213 lines
9.0 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
// GAMEPLAY DEBUGGER CATEGORY
//
// Single category of gameplay debugger tool, responsible for gathering and presenting data.
// Category instances are created on both server and local sides, and can use replication to
// show server's state on client.
//
// It should be compiled and used only when module is included, so every category class
// needs be placed in #if WITH_GAMEPLAY_DEBUGGER block (or WITH_GAMEPLAY_DEBUGGER_MENU when using WITH_GAMEPLAY_DEBUGGER_CORE).
//
//
// Server side category :
// - CollectData() function will be called in category having authority (server / standalone)
// - set CollectDataInterval for adding delay between data collection, default value is 0, meaning: every tick
// - AddTextLine() and AddShape() adds new data to replicate, both arrays are cleared before calling CollectData()
// - SetDataPackReplication() marks struct member variable for replication
// - MarkDataPackDirty() forces data pack replication, sometimes changes can go unnoticed (CRC based)
//
// Local category :
// - DrawData() function will be called in every tick to present gathered data
// - everything added by AddTextLine() and AddShape() will be shown before calling DrawData()
// - CreateSceneProxy() allows creating custom scene proxies, use with MarkRenderStateDirty()
// - OnDataPackReplicated() notifies about receiving new data, use with MarkRenderStateDirty() if needed
// - BindKeyPress() allows creating custom key bindings active only when category is being displayed
//
//
// Categories needs to be manually registered and unregistered with GameplayDebugger.
// It's best to do it in owning module's Startup / Shutdown, similar to detail view customizations.
// Check AIModule.cpp for examples.
#pragma once
#include "CoreMinimal.h"
#include "GameplayDebuggerTypes.h"
#include "GameplayDebuggerAddonBase.h"
class AActor;
class APlayerController;
class FDebugRenderSceneProxy;
class UPrimitiveComponent;
struct FDebugDrawDelegateHelper;
/**
* Single category of visual debugger tool
*/
class FGameplayDebuggerCategory : public FGameplayDebuggerAddonBase
{
public:
GAMEPLAYDEBUGGER_API FGameplayDebuggerCategory();
GAMEPLAYDEBUGGER_API virtual ~FGameplayDebuggerCategory();
/** [AUTH] gather data for replication */
GAMEPLAYDEBUGGER_API virtual void CollectData(APlayerController* OwnerPC, AActor* DebugActor);
/** [LOCAL] draw collected data */
GAMEPLAYDEBUGGER_API virtual void DrawData(APlayerController* OwnerPC, FGameplayDebuggerCanvasContext& CanvasContext);
/** [LOCAL] creates a scene proxy for more advanced debug rendering */
GAMEPLAYDEBUGGER_API virtual FDebugRenderSceneProxy* CreateDebugSceneProxy(const UPrimitiveComponent* InComponent, FDebugDrawDelegateHelper*& OutDelegateHelper);
/** [LOCAL] called after successful replication of entire data pack to client */
GAMEPLAYDEBUGGER_API virtual void OnDataPackReplicated(int32 DataPackId);
/** [AUTH] adds line of text tagged with {color} to replicated data */
GAMEPLAYDEBUGGER_API void AddTextLine(const FString& TextLine);
/** [AUTH] adds shape to replicated data */
GAMEPLAYDEBUGGER_API void AddShape(const FGameplayDebuggerShape& Shape);
/** [LOCAL] draw category */
GAMEPLAYDEBUGGER_API void DrawCategory(APlayerController* OwnerPC, FGameplayDebuggerCanvasContext& CanvasContext);
/** [LOCAL] check if category should be drawn */
bool ShouldDrawCategory(bool bHasDebugActor) const { return IsCategoryEnabled() && (!bShowOnlyWithDebugActor || bHasDebugActor); }
/** [LOCAL] check if data pack replication status */
bool ShouldDrawReplicationStatus() const { return bShowDataPackReplication; }
/** [ALL] get name of category */
FName GetCategoryName() const { return CategoryName; }
/** [ALL] check if category header should be drawn */
bool IsCategoryHeaderVisible() const { return bShowCategoryName; }
/** [ALL] check if category is enabled */
bool IsCategoryEnabled() const { return bIsEnabled; }
/** [ALL] check if category is local (present data) */
bool IsCategoryLocal() const { return bIsLocal; }
/** [ALL] check if category has authority (collects data) */
bool IsCategoryAuth() const { return bHasAuthority; }
/** [LOCAL] */
bool ShouldCollectDataOnClient() const { return bAllowLocalDataCollection; }
int32 GetNumDataPacks() const { return ReplicatedDataPacks.Num(); }
float GetDataPackProgress(int32 DataPackId) const { return ReplicatedDataPacks.IsValidIndex(DataPackId) ? ReplicatedDataPacks[DataPackId].GetProgress() : 0.0f; }
bool IsDataPackReplicating(int32 DataPackId) const { return ReplicatedDataPacks.IsValidIndex(DataPackId) && ReplicatedDataPacks[DataPackId].IsInProgress(); }
FGameplayDebuggerDataPack::FHeader GetDataPackHeaderCopy(int32 DataPackId) const { return ReplicatedDataPacks.IsValidIndex(DataPackId) ? ReplicatedDataPacks[DataPackId].Header : FGameplayDebuggerDataPack::FHeader(); }
// temporary functions for compatibility, will be removed soon
TArray<FString> GetReplicatedLinesCopy() const { return ReplicatedLines; }
TArray<FGameplayDebuggerShape> GetReplicatedShapesCopy() const { return ReplicatedShapes; }
protected:
/**
* Returns view location and direction from replicated data if available or will extract it from
* the provided player controller. The replicated data is used first since it might be provided
* from a detached camera.
* @return whether OutViewLocation and OutViewDirection were assigned to valid values
* @note Method is expected to always return valid outputs if valid controller is provided. Otherwise it
* depends if the view information has been replicated before the methods gets called.
*/
GAMEPLAYDEBUGGER_API bool GetViewPoint(const APlayerController* OwnerPC, FVector& OutViewLocation, FVector& OutViewDirection) const;
/**
* Indicates if a given location is within a vision cone built from provided view location and direction based
* on MaxViewDistance and MaxViewAngle from GameplayDebuggerUserSettings
*/
static GAMEPLAYDEBUGGER_API bool IsLocationInViewCone(const FVector& ViewLocation, const FVector& ViewDirection, const FVector& TargetLocation);
/** [AUTH] marks data pack as needing replication */
GAMEPLAYDEBUGGER_API void MarkDataPackDirty(int32 DataPackId);
/** [LOCAL] requests new scene proxy */
GAMEPLAYDEBUGGER_API void MarkRenderStateDirty();
/** [LOCAL] preferred view flag for creating scene proxy */
GAMEPLAYDEBUGGER_API FString GetSceneProxyViewFlag() const;
/** [ALL] sets up DataPack replication, needs address of property holding data, DataPack's struct must define Serialize(FArchive& Ar) function
* returns DataPackId
*/
template<typename T>
int32 SetDataPackReplication(T* DataPackAddr, EGameplayDebuggerDataPack Flags = EGameplayDebuggerDataPack::ResetOnTick)
{
FGameplayDebuggerDataPack NewDataPack;
NewDataPack.PackId = ReplicatedDataPacks.Num();
NewDataPack.Flags = Flags;
NewDataPack.SerializeDelegate.BindRaw(DataPackAddr, &T::Serialize);
NewDataPack.ResetDelegate.BindLambda([=] { *DataPackAddr = T(); });
ReplicatedDataPacks.Add(NewDataPack);
return NewDataPack.PackId;
}
/** [AUTH] force data collection on next update */
void ForceImmediateCollect() { LastCollectDataTime = -MAX_dbl; }
/** [ALL] Clears out accumulated replicated data. */
GAMEPLAYDEBUGGER_API void ResetReplicatedData();
/** update interval, 0 = each tick */
float CollectDataInterval;
/** include data pack replication details in drawn messages */
uint32 bShowDataPackReplication : 1;
/** include remaining time to next data collection in drawn messages */
uint32 bShowUpdateTimer : 1;
/** include category name in drawn messages */
uint32 bShowCategoryName : 1;
/** draw category only when DebugActor is present */
uint32 bShowOnlyWithDebugActor : 1;
/**
* False by default. If set to True will make data collection functions work on Local categories as well.
* Note that in client-server setting the data is still being replicated over from the server as well,
* and data collection on the client can also be used to annotate the incoming data. If this is undesired
* you need to call ResetReplicatedData before gathering the client-side data.
*/
uint32 bAllowLocalDataCollection : 1;
private:
friend class FGameplayDebuggerAddonManager;
friend class AGameplayDebuggerCategoryReplicator;
friend struct FGameplayDebuggerNetPack;
/** if set, this category object can display data */
uint32 bIsLocal : 1;
/** if set, this category object can collect data */
uint32 bHasAuthority : 1;
/** if set, this category object is enabled in debugger */
uint32 bIsEnabled : 1;
/** Id number assigned to this category object */
int32 CategoryId;
/** timestamp of last update */
double LastCollectDataTime;
/** name of debugger category (auto assigned during category registration) */
FName CategoryName;
/** list of replicated text lines, will be reset before CollectData call on AUTH */
TArray<FString> ReplicatedLines;
/** list of replicated shapes, will be reset before CollectData call on AUTH */
TArray<FGameplayDebuggerShape> ReplicatedShapes;
/** list of replicated data packs */
TArray<FGameplayDebuggerDataPack> ReplicatedDataPacks;
};