#include "common/debug/GameDebugActor.h" #include "character/CharacterObject.h" #include "map/GameMap.h" #include "ui/NineSliceActor.h" #include #include #include #include #include namespace frostbite2D { namespace { constexpr float kDebugHudMarginX = 12.0f; constexpr float kDebugHudMarginY = 12.0f; constexpr float kDebugHudPaddingX = 8.0f; constexpr float kDebugHudPaddingY = 6.0f; constexpr float kDebugHudLineGap = 4.0f; constexpr char kDebugHudPopupImg[] = "sprite/interface/newstyle/windows/popup/popup.img"; void ConfigureTextLine(RefPtr textSprite, const char* name, int zOrder) { if (!textSprite) { return; } textSprite->SetName(name); textSprite->SetFont("default"); textSprite->SetTextColor(1.0f, 1.0f, 1.0f, 1.0f); textSprite->SetZOrder(zOrder); } } // namespace GameDebugActor::GameDebugActor() { SetName("GameDebugActor"); SetZOrder(std::numeric_limits::max()); initOverlay(); } GameDebugActor& GameDebugActor::get() { return *getPtr(); } Ptr GameDebugActor::getPtr() { static Ptr instance(new GameDebugActor()); return instance; } bool GameDebugActor::AttachToScene(Scene* scene) { return AttachToParent(scene); } bool GameDebugActor::AttachToParent(Actor* parent) { if (!parent || parent == this) { return false; } Ptr self = getPtr(); if (GetParent() == parent) { return true; } if (GetParent()) { GetParent()->RemoveChild(self); } parent->AddChild(self); return GetParent() == parent; } void GameDebugActor::DetachFromParent() { Ptr self = getPtr(); if (GetParent()) { GetParent()->RemoveChild(self); } } void GameDebugActor::SetDebugMap(GameMap* map) { if (debugMap_ && debugMap_ != map) { debugMap_->SetDebugHighlightedMoveAreaIndex(GameMap::kInvalidMoveAreaIndex); } debugMap_ = map; updateOverlay(); } void GameDebugActor::SetTrackedCharacter(CharacterObject* character) { trackedCharacter_ = character; updateOverlay(); } void GameDebugActor::ClearDebugContext() { if (debugMap_) { debugMap_->SetDebugHighlightedMoveAreaIndex(GameMap::kInvalidMoveAreaIndex); } debugMap_ = nullptr; trackedCharacter_ = nullptr; setOverlayVisible(false); } void GameDebugActor::OnUpdate(float deltaTime) { (void)deltaTime; updateOverlay(); } void GameDebugActor::initOverlay() { if (background_ || coordText_ || actionText_) { return; } background_ = NineSliceActor::createFromNpk(kDebugHudPopupImg, 0); if (background_) { background_->SetName("debugHudBackground"); AddChild(background_); } else { SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "GameDebugActor: failed to load HUD background from %s", kDebugHudPopupImg); } coordText_ = TextSprite::create(); ConfigureTextLine(coordText_, "debugCoordText", 1); AddChild(coordText_); actionText_ = TextSprite::create(); ConfigureTextLine(actionText_, "debugActionText", 1); AddChild(actionText_); setOverlayVisible(false); } void GameDebugActor::updateOverlay() { if (!coordText_ || !actionText_) { setOverlayVisible(false); return; } updateMapDebugHighlight(); if (!trackedCharacter_) { setOverlayVisible(false); return; } const CharacterWorldPosition& worldPosition = trackedCharacter_->GetWorldPosition(); char coordTextBuffer[96]; SDL_snprintf(coordTextBuffer, sizeof(coordTextBuffer), "角色坐标: (%d, %d, %d)", worldPosition.x, worldPosition.y, worldPosition.z); coordText_->SetText(coordTextBuffer); int frameCount = std::max(trackedCharacter_->GetCurrentAnimationFrameCount(), 1); int frameIndex = std::clamp(trackedCharacter_->GetCurrentAnimationFrameIndex(), 0, frameCount - 1); char actionTextBuffer[128]; SDL_snprintf(actionTextBuffer, sizeof(actionTextBuffer), "Action: %s dir:%c frame:%d/%d", trackedCharacter_->GetCurrentAction().empty() ? "" : trackedCharacter_->GetCurrentAction().c_str(), trackedCharacter_->GetDirection() >= 0 ? 'R' : 'L', frameIndex + 1, frameCount); actionText_->SetText(actionTextBuffer); Vec2 coordTextSize = coordText_->GetTextSize(); Vec2 actionTextSize = actionText_->GetTextSize(); float panelWidth = std::max(coordTextSize.x, actionTextSize.x) + kDebugHudPaddingX * 2.0f; float contentHeight = coordTextSize.y; if (actionText_->IsVisible()) { contentHeight += kDebugHudLineGap + actionTextSize.y; } float panelHeight = contentHeight + kDebugHudPaddingY * 2.0f; Vec2 panelSize(panelWidth, panelHeight); if (background_) { background_->SetSize(panelSize); } SetPosition(kDebugHudMarginX, kDebugHudMarginY); SetScale(1.0f); float currentY = kDebugHudPaddingY; coordText_->SetPosition(kDebugHudPaddingX, currentY); currentY += coordTextSize.y; if (actionText_->IsVisible()) { currentY += kDebugHudLineGap; actionText_->SetPosition(kDebugHudPaddingX, currentY); currentY += actionTextSize.y; } setOverlayVisible(true); } void GameDebugActor::updateMapDebugHighlight() { if (!debugMap_) { return; } if (!trackedCharacter_ || !debugMap_->IsDebugModeEnabled()) { debugMap_->SetDebugHighlightedMoveAreaIndex(GameMap::kInvalidMoveAreaIndex); return; } const CharacterWorldPosition& worldPosition = trackedCharacter_->GetWorldPosition(); Vec3 currentWorldPos(static_cast(worldPosition.x), static_cast(worldPosition.y), static_cast(worldPosition.z)); size_t moveAreaIndex = debugMap_->FindMoveAreaIndex(currentWorldPos); debugMap_->SetDebugHighlightedMoveAreaIndex(moveAreaIndex); } void GameDebugActor::setOverlayVisible(bool visible) { if (background_) { background_->SetVisible(visible); } if (coordText_) { coordText_->SetVisible(visible); } if (actionText_) { actionText_->SetVisible(visible); } } } // namespace frostbite2D