Overlay de fronteira (cliente): paredes do grid + labels Cell X,Y
- AZeusFrontierOverlayActor: paredes (ProceduralMesh, material de portal) em
todas as fronteiras do grid; label DrawDebugString no centro de cada cell com
notacao de quadtree ("Cell 0" / "Cell 0,2" pos-split) + nome do servidor.
- UZeusAOIComponent: liga via toggle "Fronteiras (Mesh)" / CVar zeus.debug.frontier,
spawna/atualiza o ator, re-pede o 6163 a cada 2s (reflete split/merge).
- ZMMO.Build.cs: + ProceduralMeshComponent.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -3,6 +3,7 @@
|
||||
#include "ZeusAOIComponent.h"
|
||||
|
||||
#include "ZeusNetworkingClientSubsystem.h" // V1 canonico: OnDebugAoiInfo (6161) + EmitDebugAoiRequest (6160)
|
||||
#include "ZeusFrontierOverlayActor.h" // ator que desenha as paredes de fronteira
|
||||
#include "ZMMONetLog.h" // Batch 2.5: LogZeusAOI (categoria do modulo ZMMO)
|
||||
|
||||
#include "DrawDebugHelpers.h"
|
||||
@@ -23,6 +24,8 @@ static TAutoConsoleVariable<int32> CVarShowHandoff(
|
||||
TEXT("zeus.debug.handoff"), 0, TEXT("Desenha a linha de handoff (placeholder)."), ECVF_Default);
|
||||
static TAutoConsoleVariable<int32> CVarShowDrift(
|
||||
TEXT("zeus.debug.drift"), 0, TEXT("Desenha o drift autoritativo (TODO)."), ECVF_Default);
|
||||
static TAutoConsoleVariable<int32> CVarShowFrontier(
|
||||
TEXT("zeus.debug.frontier"), 0, TEXT("Desenha as paredes das fronteiras do mesh (own<->neighbor)."), ECVF_Default);
|
||||
|
||||
namespace
|
||||
{
|
||||
@@ -62,6 +65,7 @@ void UZeusAOIComponent::BeginPlay()
|
||||
if (UZeusNetworkingClientSubsystem* NetC = GI->GetSubsystem<UZeusNetworkingClientSubsystem>())
|
||||
{
|
||||
AoiConfigHandle_ = NetC->OnDebugAoiInfo.AddUObject(this, &UZeusAOIComponent::HandleAoiConfig);
|
||||
FrontierConfigHandle_ = NetC->OnDebugFrontierInfo.AddUObject(this, &UZeusAOIComponent::HandleFrontierConfig);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -73,7 +77,7 @@ void UZeusAOIComponent::BeginPlay()
|
||||
|
||||
void UZeusAOIComponent::EndPlay(const EEndPlayReason::Type EndPlayReason)
|
||||
{
|
||||
if (AoiConfigHandle_.IsValid())
|
||||
if (AoiConfigHandle_.IsValid() || FrontierConfigHandle_.IsValid())
|
||||
{
|
||||
if (UWorld* World = GetWorld())
|
||||
{
|
||||
@@ -81,11 +85,18 @@ void UZeusAOIComponent::EndPlay(const EEndPlayReason::Type EndPlayReason)
|
||||
{
|
||||
if (UZeusNetworkingClientSubsystem* NetC = GI->GetSubsystem<UZeusNetworkingClientSubsystem>())
|
||||
{
|
||||
NetC->OnDebugAoiInfo.Remove(AoiConfigHandle_);
|
||||
if (AoiConfigHandle_.IsValid()) { NetC->OnDebugAoiInfo.Remove(AoiConfigHandle_); }
|
||||
if (FrontierConfigHandle_.IsValid()) { NetC->OnDebugFrontierInfo.Remove(FrontierConfigHandle_); }
|
||||
}
|
||||
}
|
||||
}
|
||||
AoiConfigHandle_.Reset();
|
||||
FrontierConfigHandle_.Reset();
|
||||
}
|
||||
if (FrontierActor_)
|
||||
{
|
||||
FrontierActor_->Destroy();
|
||||
FrontierActor_ = nullptr;
|
||||
}
|
||||
Super::EndPlay(EndPlayReason);
|
||||
}
|
||||
@@ -95,6 +106,26 @@ void UZeusAOIComponent::TickComponent(float DeltaTime, ELevelTick TickType,
|
||||
{
|
||||
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
|
||||
|
||||
// Frontier: sincroniza o overlay com o estado atual. Pega o liga/desliga via
|
||||
// CVar zeus.debug.frontier (que nao passa por SetOverlayEnabled) e garante que
|
||||
// ao desligar as paredes sejam REMOVIDAS (UpdateFrontierOverlay -> ClearOverlay).
|
||||
if (ResolveOverlay(EZeusAOIOverlay::FrontierCells) != bFrontierApplied_)
|
||||
{
|
||||
UpdateFrontierOverlay();
|
||||
}
|
||||
|
||||
// Re-pede o frontier periodicamente enquanto ligado -> o overlay reflete
|
||||
// split/merge da topologia dinamica (debug client-side, baixa freq, so' ON).
|
||||
if (ResolveOverlay(EZeusAOIOverlay::FrontierCells))
|
||||
{
|
||||
FrontierRepollAccumSec_ += DeltaTime;
|
||||
if (FrontierRepollAccumSec_ >= FrontierRepollIntervalSec)
|
||||
{
|
||||
FrontierRepollAccumSec_ = 0.0f;
|
||||
RequestFrontierConfig();
|
||||
}
|
||||
}
|
||||
|
||||
if (AnyOverlayActive())
|
||||
{
|
||||
DrawOverlays();
|
||||
@@ -127,6 +158,64 @@ void UZeusAOIComponent::HandleAoiConfig(float InterestCm, float DespawnCm)
|
||||
DespawnCm, DespawnCm / 100.0f);
|
||||
}
|
||||
|
||||
void UZeusAOIComponent::RequestFrontierConfig()
|
||||
{
|
||||
if (UWorld* World = GetWorld())
|
||||
{
|
||||
if (UGameInstance* GI = World->GetGameInstance())
|
||||
{
|
||||
if (UZeusNetworkingClientSubsystem* NetC = GI->GetSubsystem<UZeusNetworkingClientSubsystem>())
|
||||
{
|
||||
NetC->EmitDebugFrontierRequest(); // V1: no-op se ainda nao Accepted
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UZeusAOIComponent::HandleFrontierConfig(const ZeusV1::FDebugFrontierInfo& Info)
|
||||
{
|
||||
FrontierInfo_ = Info;
|
||||
bFrontierReceived_ = true;
|
||||
UE_LOG(LogZeusAOI, Display,
|
||||
TEXT("Frontier cfg recv own=%d neighbors=%d"),
|
||||
Info.OwnedCells.Num(), Info.NeighborCells.Num());
|
||||
UpdateFrontierOverlay();
|
||||
}
|
||||
|
||||
AZeusFrontierOverlayActor* UZeusAOIComponent::EnsureFrontierActor()
|
||||
{
|
||||
if (FrontierActor_) { return FrontierActor_; }
|
||||
UWorld* World = GetWorld();
|
||||
if (!World) { return nullptr; }
|
||||
FActorSpawnParameters Params;
|
||||
Params.Owner = GetOwner();
|
||||
Params.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn;
|
||||
FrontierActor_ = World->SpawnActor<AZeusFrontierOverlayActor>(
|
||||
AZeusFrontierOverlayActor::StaticClass(), FVector::ZeroVector, FRotator::ZeroRotator, Params);
|
||||
return FrontierActor_;
|
||||
}
|
||||
|
||||
void UZeusAOIComponent::UpdateFrontierOverlay()
|
||||
{
|
||||
const bool bOn = ResolveOverlay(EZeusAOIOverlay::FrontierCells);
|
||||
bFrontierApplied_ = bOn;
|
||||
if (!bOn)
|
||||
{
|
||||
if (FrontierActor_) { FrontierActor_->ClearOverlay(); }
|
||||
return;
|
||||
}
|
||||
if (!bFrontierReceived_)
|
||||
{
|
||||
// Ainda nao temos as cells; pede e espera o 6163 (HandleFrontierConfig re-chama).
|
||||
RequestFrontierConfig();
|
||||
return;
|
||||
}
|
||||
AZeusFrontierOverlayActor* Actor = EnsureFrontierActor();
|
||||
if (!Actor) { return; }
|
||||
const float AnchorZ = GetOwner() ? GetOwner()->GetActorLocation().Z : 0.0f;
|
||||
Actor->RebuildFromFrontier(FrontierInfo_, AnchorZ);
|
||||
}
|
||||
|
||||
bool UZeusAOIComponent::ResolveOverlay(EZeusAOIOverlay Overlay) const
|
||||
{
|
||||
switch (Overlay)
|
||||
@@ -137,6 +226,7 @@ bool UZeusAOIComponent::ResolveOverlay(EZeusAOIOverlay Overlay) const
|
||||
case EZeusAOIOverlay::ProxyLabels: return bShowProxyLabels || CVarShowProxy.GetValueOnGameThread() > 0;
|
||||
case EZeusAOIOverlay::HandoffLine: return bShowHandoffLine || CVarShowHandoff.GetValueOnGameThread() > 0;
|
||||
case EZeusAOIOverlay::AuthDrift: return bShowAuthDrift || CVarShowDrift.GetValueOnGameThread() > 0;
|
||||
case EZeusAOIOverlay::FrontierCells: return bShowFrontier || CVarShowFrontier.GetValueOnGameThread() > 0;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
@@ -148,7 +238,8 @@ bool UZeusAOIComponent::AnyOverlayActive() const
|
||||
|| ResolveOverlay(EZeusAOIOverlay::CellBounds)
|
||||
|| ResolveOverlay(EZeusAOIOverlay::ProxyLabels)
|
||||
|| ResolveOverlay(EZeusAOIOverlay::HandoffLine)
|
||||
|| ResolveOverlay(EZeusAOIOverlay::AuthDrift);
|
||||
|| ResolveOverlay(EZeusAOIOverlay::AuthDrift)
|
||||
|| ResolveOverlay(EZeusAOIOverlay::FrontierCells);
|
||||
}
|
||||
|
||||
void UZeusAOIComponent::DrawOverlays()
|
||||
@@ -198,6 +289,7 @@ void UZeusAOIComponent::SetOverlayEnabled_Implementation(EZeusAOIOverlay Overlay
|
||||
case EZeusAOIOverlay::ProxyLabels: bShowProxyLabels = bEnabled; break;
|
||||
case EZeusAOIOverlay::HandoffLine: bShowHandoffLine = bEnabled; break;
|
||||
case EZeusAOIOverlay::AuthDrift: bShowAuthDrift = bEnabled; break;
|
||||
case EZeusAOIOverlay::FrontierCells: bShowFrontier = bEnabled; break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
@@ -207,17 +299,27 @@ void UZeusAOIComponent::SetOverlayEnabled_Implementation(EZeusAOIOverlay Overlay
|
||||
{
|
||||
RequestAoiConfig();
|
||||
}
|
||||
|
||||
// Frontier: ao ligar pede as cells (6162) e reconstroi; ao desligar limpa as paredes.
|
||||
if (Overlay == EZeusAOIOverlay::FrontierCells)
|
||||
{
|
||||
if (bEnabled) { RequestFrontierConfig(); }
|
||||
UpdateFrontierOverlay();
|
||||
}
|
||||
}
|
||||
|
||||
void UZeusAOIComponent::SetAllOverlays_Implementation(bool bEnabled)
|
||||
{
|
||||
bShowAOIRadius = bShowDespawnZone = bShowCellBounds = bEnabled;
|
||||
bShowProxyLabels = bShowHandoffLine = bShowAuthDrift = bEnabled;
|
||||
bShowFrontier = bEnabled;
|
||||
|
||||
if (bEnabled && InterestRadiusCm <= 0.0f)
|
||||
{
|
||||
RequestAoiConfig();
|
||||
}
|
||||
if (bEnabled) { RequestFrontierConfig(); }
|
||||
UpdateFrontierOverlay();
|
||||
}
|
||||
|
||||
bool UZeusAOIComponent::IsOverlayEnabled_Implementation(EZeusAOIOverlay Overlay) const
|
||||
@@ -239,5 +341,6 @@ float UZeusAOIComponent::GetDespawnRadiusCm_Implementation() const
|
||||
int32 UZeusAOIComponent::GetEnabledOverlayCount() const
|
||||
{
|
||||
return (bShowAOIRadius ? 1 : 0) + (bShowDespawnZone ? 1 : 0) + (bShowCellBounds ? 1 : 0)
|
||||
+ (bShowProxyLabels ? 1 : 0) + (bShowHandoffLine ? 1 : 0) + (bShowAuthDrift ? 1 : 0);
|
||||
+ (bShowProxyLabels ? 1 : 0) + (bShowHandoffLine ? 1 : 0) + (bShowAuthDrift ? 1 : 0)
|
||||
+ (bShowFrontier ? 1 : 0);
|
||||
}
|
||||
|
||||
@@ -5,8 +5,11 @@
|
||||
#include "CoreMinimal.h"
|
||||
#include "Components/ActorComponent.h"
|
||||
#include "ZeusAOIDebugTarget.h" // plugin ZeusAdminToolsRuntime: interface + EZeusAOIOverlay
|
||||
#include "ZeusV1Protocol.h" // ZeusV1::FDebugFrontierInfo (overlay de fronteira)
|
||||
#include "ZeusAOIComponent.generated.h"
|
||||
|
||||
class AZeusFrontierOverlayActor;
|
||||
|
||||
/**
|
||||
* UZeusAOIComponent (cliente / jogo)
|
||||
*
|
||||
@@ -53,6 +56,9 @@ public:
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Zeus|AOI Debug|Overlays") bool bShowProxyLabels = false;
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Zeus|AOI Debug|Overlays") bool bShowHandoffLine = false;
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Zeus|AOI Debug|Overlays") bool bShowAuthDrift = false;
|
||||
// Fronteiras do mesh: paredes own<->neighbor com label "Cell <id>", renderizadas
|
||||
// por AZeusFrontierOverlayActor a partir do DEBUG_FRONTIER_INFO (6163).
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Zeus|AOI Debug|Overlays") bool bShowFrontier = false;
|
||||
|
||||
// === Parametros ===
|
||||
// Raios reais vem do servidor (S_DEBUG_AOI_INFO). 0 = ainda nao recebido (loading).
|
||||
@@ -76,6 +82,30 @@ private:
|
||||
/** Bind de UZeusNetworkSubsystem::OnDebugAoiInfo: armazena os raios reais. */
|
||||
void HandleAoiConfig(float InterestCm, float DespawnCm);
|
||||
|
||||
/** Pede o frontier do mesh ao servidor (DEBUG_FRONTIER_REQUEST 6162). */
|
||||
void RequestFrontierConfig();
|
||||
/** Bind de OnDebugFrontierInfo: guarda as cells e (se overlay on) reconstroi as paredes. */
|
||||
void HandleFrontierConfig(const ZeusV1::FDebugFrontierInfo& Info);
|
||||
/** Spawna (lazy) o ator que desenha as paredes + labels. */
|
||||
AZeusFrontierOverlayActor* EnsureFrontierActor();
|
||||
/** Liga / reconstroi / limpa o overlay de fronteira conforme bShowFrontier + dados. */
|
||||
void UpdateFrontierOverlay();
|
||||
|
||||
/** Handle do bind do OnDebugAoiInfo (pra remover no EndPlay). */
|
||||
FDelegateHandle AoiConfigHandle_;
|
||||
|
||||
/** Handle do bind do OnDebugFrontierInfo (frontier overlay). */
|
||||
FDelegateHandle FrontierConfigHandle_;
|
||||
/** Ultimo frontier recebido do server (own cells + neighbors). */
|
||||
ZeusV1::FDebugFrontierInfo FrontierInfo_;
|
||||
bool bFrontierReceived_ = false;
|
||||
/** Estado do overlay JA aplicado (paredes desenhadas). O TickComponent compara
|
||||
* com ResolveOverlay p/ pegar liga/desliga via CVar tambem (nao so' o toggle). */
|
||||
bool bFrontierApplied_ = false;
|
||||
/** Re-poll periodico do frontier enquanto ligado -> overlay reflete split/merge
|
||||
* da topologia dinamica (o 6163 traz o estado atual do topology:current). */
|
||||
float FrontierRepollIntervalSec = 2.0f;
|
||||
float FrontierRepollAccumSec_ = 0.0f;
|
||||
/** Ator que desenha as paredes + labels (spawn lazy ao ligar o overlay). */
|
||||
UPROPERTY() TObjectPtr<AZeusFrontierOverlayActor> FrontierActor_ = nullptr;
|
||||
};
|
||||
|
||||
205
Source/ZMMO/Game/Network/ZeusFrontierOverlayActor.cpp
Normal file
205
Source/ZMMO/Game/Network/ZeusFrontierOverlayActor.cpp
Normal file
@@ -0,0 +1,205 @@
|
||||
// Copyright Zeus Server Engine. All rights reserved.
|
||||
|
||||
#include "ZeusFrontierOverlayActor.h"
|
||||
|
||||
#include "ZMMONetLog.h" // LogZeusAOI (categoria do modulo ZMMO)
|
||||
|
||||
#include "ProceduralMeshComponent.h"
|
||||
#include "Materials/MaterialInterface.h"
|
||||
#include "Engine/World.h"
|
||||
#include "DrawDebugHelpers.h"
|
||||
#include "UObject/ConstructorHelpers.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
const TCHAR* kWallMaterialPath =
|
||||
TEXT("/Game/ZMMO/Materials/VFX/Portal/GridBoundary/Instancia/MI_VFX_Portal_GridBoundary_01.MI_VFX_Portal_GridBoundary_01");
|
||||
|
||||
const FColor kLabelAlive(93, 213, 255); // ciano -- server online
|
||||
const FColor kLabelDead(255, 90, 90); // vermelho -- server offline / cell vazia
|
||||
|
||||
// Offset do cellId de filha de split (espelha CHILD_CELLID_OFFSET do server).
|
||||
constexpr uint32 kChildCellIdOffset = 1000000u;
|
||||
|
||||
// Decodifica o cellId int na notacao de quadtree: raiz -> "0".."3"; filha ->
|
||||
// "<pai>,<quadrante>" (ex "0,2"); neto -> "0,2,1" (recursivo). Espelha
|
||||
// childCellId = offset + pai*4 + quadrante do SplitCoordinator.
|
||||
FString DecodeCellPath(uint32 CellId)
|
||||
{
|
||||
if (CellId < kChildCellIdOffset)
|
||||
{
|
||||
return FString::Printf(TEXT("%u"), CellId);
|
||||
}
|
||||
const uint32 Rel = CellId - kChildCellIdOffset;
|
||||
return DecodeCellPath(Rel / 4u) + FString::Printf(TEXT(",%u"), Rel % 4u);
|
||||
}
|
||||
|
||||
// Aresta compartilhada entre dois retangulos axis-aligned (no plano XY).
|
||||
bool SharedEdge(float aMinX, float aMinY, float aMaxX, float aMaxY,
|
||||
float bMinX, float bMinY, float bMaxX, float bMaxY,
|
||||
float Eps, FVector2D& OutA, FVector2D& OutB)
|
||||
{
|
||||
const float yLo = FMath::Max(aMinY, bMinY), yHi = FMath::Min(aMaxY, bMaxY);
|
||||
const float xLo = FMath::Max(aMinX, bMinX), xHi = FMath::Min(aMaxX, bMaxX);
|
||||
|
||||
if (FMath::Abs(aMaxX - bMinX) <= Eps && yHi > yLo) { OutA = {aMaxX, yLo}; OutB = {aMaxX, yHi}; return true; }
|
||||
if (FMath::Abs(aMinX - bMaxX) <= Eps && yHi > yLo) { OutA = {aMinX, yLo}; OutB = {aMinX, yHi}; return true; }
|
||||
if (FMath::Abs(aMaxY - bMinY) <= Eps && xHi > xLo) { OutA = {xLo, aMaxY}; OutB = {xHi, aMaxY}; return true; }
|
||||
if (FMath::Abs(aMinY - bMaxY) <= Eps && xHi > xLo) { OutA = {xLo, aMinY}; OutB = {xHi, aMinY}; return true; }
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
AZeusFrontierOverlayActor::AZeusFrontierOverlayActor()
|
||||
{
|
||||
PrimaryActorTick.bCanEverTick = true;
|
||||
PrimaryActorTick.bStartWithTickEnabled = true;
|
||||
|
||||
WallMesh = CreateDefaultSubobject<UProceduralMeshComponent>(TEXT("WallMesh"));
|
||||
SetRootComponent(WallMesh);
|
||||
WallMesh->bUseAsyncCooking = false;
|
||||
WallMesh->SetCollisionEnabled(ECollisionEnabled::NoCollision);
|
||||
WallMesh->SetCastShadow(false);
|
||||
|
||||
static ConstructorHelpers::FObjectFinder<UMaterialInterface> MatFinder(kWallMaterialPath);
|
||||
if (MatFinder.Succeeded())
|
||||
{
|
||||
WallMaterial = MatFinder.Object;
|
||||
}
|
||||
}
|
||||
|
||||
void AZeusFrontierOverlayActor::RebuildFromFrontier(const ZeusV1::FDebugFrontierInfo& Info, float AnchorZCm)
|
||||
{
|
||||
if (!WallMesh) { return; }
|
||||
AnchorZ_ = AnchorZCm;
|
||||
|
||||
// 1) Junta own + neighbors numa lista unica de cells (grid inteiro). Dedup por cellId.
|
||||
Cells_.Reset();
|
||||
auto AddCell = [this](uint32 CellId, int32 MinX, int32 MinY, int32 MaxX, int32 MaxY,
|
||||
const FString& InOwner, bool bIsDead)
|
||||
{
|
||||
for (const FCellView& Ex : Cells_) { if (Ex.CellId == CellId) { return; } }
|
||||
FCellView V;
|
||||
V.CellId = CellId;
|
||||
V.MinX = static_cast<float>(MinX); V.MinY = static_cast<float>(MinY);
|
||||
V.MaxX = static_cast<float>(MaxX); V.MaxY = static_cast<float>(MaxY);
|
||||
V.Owner = InOwner; V.bDead = bIsDead;
|
||||
Cells_.Add(MoveTemp(V));
|
||||
};
|
||||
for (const ZeusV1::FFrontierOwnCell& C : Info.OwnedCells)
|
||||
{
|
||||
AddCell(C.CellId, C.MinXCm, C.MinYCm, C.MaxXCm, C.MaxYCm, C.OwnerName, false);
|
||||
}
|
||||
for (const ZeusV1::FFrontierNeighborCell& Nb : Info.NeighborCells)
|
||||
{
|
||||
AddCell(Nb.CellId, Nb.MinXCm, Nb.MinYCm, Nb.MaxXCm, Nb.MaxYCm, Nb.OwnerName, Nb.bDead);
|
||||
}
|
||||
|
||||
// 2) Paredes: 1 quad por aresta compartilhada entre QUALQUER par (i<j -> dedup).
|
||||
const float BaseZ = AnchorZCm - WallHeightCm * 0.5f;
|
||||
const float TopZ = AnchorZCm + WallHeightCm * 0.5f;
|
||||
|
||||
TArray<FVector> Verts;
|
||||
TArray<int32> Tris;
|
||||
TArray<FVector> Normals;
|
||||
TArray<FVector2D> UVs;
|
||||
TArray<FProcMeshTangent> Tangents;
|
||||
TArray<FLinearColor> Colors;
|
||||
|
||||
for (int32 i = 0; i < Cells_.Num(); ++i)
|
||||
{
|
||||
for (int32 j = i + 1; j < Cells_.Num(); ++j)
|
||||
{
|
||||
const FCellView& A = Cells_[i];
|
||||
const FCellView& B = Cells_[j];
|
||||
FVector2D P0, P1;
|
||||
if (!SharedEdge(A.MinX, A.MinY, A.MaxX, A.MaxY,
|
||||
B.MinX, B.MinY, B.MaxX, B.MaxY, EdgeEpsilonCm, P0, P1))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
const int32 Base = Verts.Num();
|
||||
Verts.Add(FVector(P0.X, P0.Y, BaseZ));
|
||||
Verts.Add(FVector(P1.X, P1.Y, BaseZ));
|
||||
Verts.Add(FVector(P1.X, P1.Y, TopZ));
|
||||
Verts.Add(FVector(P0.X, P0.Y, TopZ));
|
||||
|
||||
const FVector2D Dir2D = (P1 - P0).GetSafeNormal();
|
||||
const FVector N(-Dir2D.Y, Dir2D.X, 0.f);
|
||||
Normals.Add(N); Normals.Add(N); Normals.Add(N); Normals.Add(N);
|
||||
|
||||
UVs.Add(FVector2D(0.f, 1.f));
|
||||
UVs.Add(FVector2D(1.f, 1.f));
|
||||
UVs.Add(FVector2D(1.f, 0.f));
|
||||
UVs.Add(FVector2D(0.f, 0.f));
|
||||
|
||||
const FProcMeshTangent Tan(FVector(Dir2D.X, Dir2D.Y, 0.f), false);
|
||||
Tangents.Add(Tan); Tangents.Add(Tan); Tangents.Add(Tan); Tangents.Add(Tan);
|
||||
|
||||
const FLinearColor C = (A.bDead || B.bDead) ? FLinearColor(kLabelDead) : FLinearColor(kLabelAlive);
|
||||
Colors.Add(C); Colors.Add(C); Colors.Add(C); Colors.Add(C);
|
||||
|
||||
Tris.Add(Base + 0); Tris.Add(Base + 1); Tris.Add(Base + 2);
|
||||
Tris.Add(Base + 0); Tris.Add(Base + 2); Tris.Add(Base + 3);
|
||||
Tris.Add(Base + 0); Tris.Add(Base + 2); Tris.Add(Base + 1);
|
||||
Tris.Add(Base + 0); Tris.Add(Base + 3); Tris.Add(Base + 2);
|
||||
}
|
||||
}
|
||||
|
||||
WallMesh->ClearAllMeshSections();
|
||||
WallMesh->SetVisibility(true);
|
||||
if (Verts.Num() > 0)
|
||||
{
|
||||
WallMesh->CreateMeshSection_LinearColor(
|
||||
/*SectionIndex=*/0, Verts, Tris, Normals, UVs, Colors, Tangents, /*bCreateCollision=*/false);
|
||||
if (WallMaterial) { WallMesh->SetMaterial(0, WallMaterial); }
|
||||
}
|
||||
bHasOverlay_ = (Cells_.Num() > 0);
|
||||
|
||||
UE_LOG(LogZeusAOI, Display,
|
||||
TEXT("Frontier overlay rebuilt: cells=%d walls=%d anchorZ=%.0f%s"),
|
||||
Cells_.Num(), Verts.Num() / 4, AnchorZCm,
|
||||
WallMaterial ? TEXT("") : TEXT(" [WARN: material nula]"));
|
||||
}
|
||||
|
||||
void AZeusFrontierOverlayActor::ClearOverlay()
|
||||
{
|
||||
if (WallMesh)
|
||||
{
|
||||
WallMesh->ClearAllMeshSections();
|
||||
WallMesh->SetVisibility(false);
|
||||
}
|
||||
Cells_.Reset();
|
||||
bHasOverlay_ = false;
|
||||
}
|
||||
|
||||
void AZeusFrontierOverlayActor::DrawCellLabels()
|
||||
{
|
||||
if (!bHasOverlay_ || Cells_.Num() == 0) { return; }
|
||||
UWorld* World = GetWorld();
|
||||
if (!World) { return; }
|
||||
|
||||
for (const FCellView& C : Cells_)
|
||||
{
|
||||
const float Cx = (C.MinX + C.MaxX) * 0.5f;
|
||||
const float Cy = (C.MinY + C.MaxY) * 0.5f;
|
||||
const FVector Pos(Cx, Cy, AnchorZ_ + LabelHeightCm);
|
||||
|
||||
// "Cell 0" / "Cell 0,2" (notacao da quadtree) + nome do server dono.
|
||||
FString Txt = FString::Printf(TEXT("Cell %s"), *DecodeCellPath(C.CellId));
|
||||
if (!C.Owner.IsEmpty()) { Txt += TEXT("\n") + C.Owner; }
|
||||
const FColor Col = C.bDead ? kLabelDead : kLabelAlive;
|
||||
|
||||
// DrawDebugString: billboard de tela (visivel de qualquer lado, nao gira,
|
||||
// tamanho de tela constante). Re-desenhado a cada frame (Duration 0).
|
||||
DrawDebugString(World, Pos, Txt, /*TestBaseActor=*/nullptr, Col,
|
||||
/*Duration=*/0.0f, /*bDrawShadow=*/true, LabelFontScale);
|
||||
}
|
||||
}
|
||||
|
||||
void AZeusFrontierOverlayActor::Tick(float DeltaSeconds)
|
||||
{
|
||||
Super::Tick(DeltaSeconds);
|
||||
DrawCellLabels();
|
||||
}
|
||||
70
Source/ZMMO/Game/Network/ZeusFrontierOverlayActor.h
Normal file
70
Source/ZMMO/Game/Network/ZeusFrontierOverlayActor.h
Normal file
@@ -0,0 +1,70 @@
|
||||
// Copyright Zeus Server Engine. All rights reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "GameFramework/Actor.h"
|
||||
#include "ZeusV1Protocol.h" // ZeusV1::FDebugFrontierInfo (plugin ZeusNetwork)
|
||||
#include "ZeusFrontierOverlayActor.generated.h"
|
||||
|
||||
class UProceduralMeshComponent;
|
||||
class UMaterialInterface;
|
||||
|
||||
/**
|
||||
* AZeusFrontierOverlayActor (cliente / debug)
|
||||
*
|
||||
* Desenha no MUNDO as "paredes" de TODAS as fronteiras do grid de server-meshing
|
||||
* (todas as arestas compartilhadas entre quaisquer duas cells, com dedup), usando
|
||||
* a material de portal MI_VFX_Portal_GridBoundary_01. As cells vem do
|
||||
* DEBUG_FRONTIER_INFO (6163), que reflete a topologia DINAMICA (split/merge).
|
||||
*
|
||||
* Label: no CENTRO de cada cell, um texto via DrawDebugString (billboard de tela,
|
||||
* visivel de qualquer lado, nao gira). Mostra a notacao da cell:
|
||||
* raiz -> "Cell 0", "Cell 1", ...
|
||||
* split -> "Cell 0,0", "Cell 0,1", ... (pai,quadrante) -- reflete a quadtree.
|
||||
* + o nome do servidor dono. NAO e' replicado: visual no cliente local.
|
||||
*/
|
||||
UCLASS()
|
||||
class ZMMO_API AZeusFrontierOverlayActor : public AActor
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
AZeusFrontierOverlayActor();
|
||||
|
||||
virtual void Tick(float DeltaSeconds) override;
|
||||
|
||||
/** (Re)constroi as paredes do grid + guarda as cells pros labels.
|
||||
* AnchorZCm ancora a altura das paredes/labels (tipicamente o Z do player). */
|
||||
void RebuildFromFrontier(const ZeusV1::FDebugFrontierInfo& Info, float AnchorZCm);
|
||||
|
||||
/** Limpa a geometria (overlay desligado). */
|
||||
void ClearOverlay();
|
||||
|
||||
// === Parametros das paredes ===
|
||||
UPROPERTY(EditAnywhere, Category = "Zeus|Frontier") float WallHeightCm = 12000.0f; // 120m
|
||||
UPROPERTY(EditAnywhere, Category = "Zeus|Frontier") float EdgeEpsilonCm = 4.0f; // tolerancia "compartilha aresta"
|
||||
|
||||
// === Label "Cell X,Y" no centro de cada cell (DrawDebugString) ===
|
||||
UPROPERTY(EditAnywhere, Category = "Zeus|Frontier|Label") float LabelFontScale = 1.4f; // tamanho na tela (screen-space)
|
||||
UPROPERTY(EditAnywhere, Category = "Zeus|Frontier|Label") float LabelHeightCm = 250.0f; // altura do label acima do anchor
|
||||
|
||||
private:
|
||||
UPROPERTY() UProceduralMeshComponent* WallMesh = nullptr;
|
||||
UPROPERTY() UMaterialInterface* WallMaterial = nullptr;
|
||||
|
||||
/// Snapshot leve das cells (own + neighbors) pros labels.
|
||||
struct FCellView
|
||||
{
|
||||
uint32 CellId = 0;
|
||||
float MinX = 0.f, MinY = 0.f, MaxX = 0.f, MaxY = 0.f;
|
||||
FString Owner; // "Server1" (ou vazio)
|
||||
bool bDead = false;
|
||||
};
|
||||
TArray<FCellView> Cells_;
|
||||
float AnchorZ_ = 0.0f;
|
||||
bool bHasOverlay_ = false;
|
||||
|
||||
/// Desenha "Cell X,Y\n<server>" no centro de cada cell (chamado no Tick).
|
||||
void DrawCellLabels();
|
||||
};
|
||||
@@ -19,6 +19,7 @@ public class ZMMO : ModuleRules
|
||||
"CommonInput",
|
||||
"GameplayTags",
|
||||
"GameplayAbilities", // GAS: UAttributeSet, FGameplayAttributeData
|
||||
"ProceduralMeshComponent", // paredes do overlay de fronteira (AZeusFrontierOverlayActor)
|
||||
"ZeusNetwork",
|
||||
"ZeusGAS", // UZeusGASComponent, UZeusAttributeSet, FZeusAttributesSnapshot
|
||||
"ZeusJobs",
|
||||
|
||||
Reference in New Issue
Block a user