Files
gig-line/Game/GigLine/Source/GigLine/GigLineHUD.cpp
T

110 lines
3.7 KiB
C++

#include "GigLineHUD.h"
#include "Engine/Canvas.h"
#include "Engine/Engine.h"
#include "GameFramework/PlayerController.h"
#include "GigLineCharacter.h"
#include "HAL/IConsoleManager.h"
#include "Kismet/GameplayStatics.h"
// Console variable so a packaged build can be checked without a rebuild:
// gigline.Debug 1
// The HUD reads it every frame; the F3 binding toggles it.
static TAutoConsoleVariable<int32> CVarGigLineDebug(
TEXT("gigline.Debug"),
0,
TEXT("Draw the performance and state overlay (0=off, 1=on). F3 toggles it."),
ECVF_Cheat);
void AGigLineHUD::UpdateFrameStats(float DeltaSeconds)
{
if (DeltaSeconds <= 0.0f)
{
return;
}
++FramesDrawn;
const float Ms = DeltaSeconds * 1000.0f;
if (WarmupSeconds > 0.0f)
{
WarmupSeconds -= DeltaSeconds;
SmoothedFrameMs = FMath::Lerp(SmoothedFrameMs, Ms, 0.1f);
WorstFrameMs = Ms;
WorstWindowSeconds = 0.0f;
return;
}
// A 0.9/0.1 EMA settles in a few frames and still reacts to a stall.
SmoothedFrameMs = FMath::Lerp(SmoothedFrameMs, Ms, 0.1f);
WorstFrameMs = FMath::Max(WorstFrameMs, Ms);
// Rolling worst-frame window: a spike stays visible for two seconds, so a
// hitch is not missed between glances (and not averaged away).
WorstWindowSeconds += DeltaSeconds;
if (WorstWindowSeconds >= 2.0f)
{
WorstWindowSeconds = 0.0f;
WorstFrameMs = Ms;
}
}
void AGigLineHUD::DrawHUD()
{
Super::DrawHUD();
// Console variable is authoritative; the UPROPERTY is the per-instance default
// for tests that set it before BeginPlay.
if (IConsoleVariable* Var = IConsoleManager::Get().FindConsoleVariable(TEXT("gigline.Debug")))
{
bShowDebug = Var->GetInt() != 0;
}
UpdateFrameStats(GetWorld() ? GetWorld()->GetDeltaSeconds() : 0.0f);
if (!Canvas)
{
return;
}
const FLinearColor Shadow(0.0f, 0.0f, 0.0f, 0.55f);
const FLinearColor Ink(1.0f, 1.0f, 1.0f, 0.92f);
// Control legend, bottom-right (0.1.0 release check: controls shown in-game).
if (!ControlsLine.IsEmpty())
{
float IgnoredW = 0.0f, ControlsH = 0.0f;
GetTextSize(ControlsLine, IgnoredW, ControlsH, nullptr, TextScale * 0.82f);
const float CX = Canvas->ClipX - MarginPx - IgnoredW;
const float CY = Canvas->ClipY - MarginPx - ControlsH;
DrawText(ControlsLine, Shadow, CX + 1.0f, CY + 1.0f, nullptr, TextScale * 0.82f);
DrawText(ControlsLine, Ink, CX, CY, nullptr, TextScale * 0.82f);
}
if (!bShowDebug)
{
return;
}
// Performance and state readout, top-left. Everything here is cheap enough to
// leave on during a capture: four formatted lines and no additional passes.
const APawn* Pawn = UGameplayStatics::GetPlayerPawn(this, 0);
const AGigLineCharacter* Character = Cast<AGigLineCharacter>(Pawn);
const float Fps = SmoothedFrameMs > 0.0f ? 1000.0f / SmoothedFrameMs : 0.0f;
const FVector Where = Pawn ? Pawn->GetActorLocation() / 100.0f : FVector::ZeroVector;
const float Speed = Pawn ? Pawn->GetVelocity().Size2D() / 100.0f : 0.0f;
const FString Header = FString::Printf(TEXT("%.0f FPS %.2f ms worst %.2f ms"), Fps, SmoothedFrameMs, WorstFrameMs);
const FString State = FString::Printf(TEXT("%s %.2f m/s frames %lld"),
Character && Character->IsThirdPerson() ? TEXT("third person") : TEXT("first person"),
Speed, FramesDrawn);
const FString Place = FString::Printf(TEXT("x %.1f y %.1f z %.1f m"), Where.X, Where.Y, Where.Z);
const FString Hint = TEXT("F3 hide");
const FLinearColor DebugInk(0.85f, 1.0f, 0.55f, 0.95f);
const FLinearColor DebugShadow(0.0f, 0.0f, 0.0f, 0.6f);
const float DX = MarginPx;
float DY = MarginPx;
for (const FString& Line : { Header, State, Place, Hint })
{
DrawText(Line, DebugShadow, DX + 1.0f, DY + 1.0f, nullptr, TextScale * 0.9f);
DrawText(Line, DebugInk, DX, DY, nullptr, TextScale * 0.9f);
DY += 22.0f * TextScale * 0.9f;
}
}