diff --git a/Game/include/map/GameDataLoader.h b/Game/include/map/GameDataLoader.h index 1cdc5e2..547f16a 100644 --- a/Game/include/map/GameDataLoader.h +++ b/Game/include/map/GameDataLoader.h @@ -41,6 +41,11 @@ struct MoveAreaTarget { int area = -2; }; +struct MoveAreaBounds { + Vec2 leftTop = Vec2::Zero(); + Vec2 rightBottom = Vec2::Zero(); +}; + struct TileInfo { std::string spritePath; int spriteIndex = 0; @@ -65,6 +70,7 @@ struct MapConfig { std::vector soundIds; std::vector virtualMovablePolygon; std::vector townMovableAreas; + std::vector townMovableAreaBounds; std::vector townMovableAreaTargets; }; diff --git a/Game/include/map/GameMap.h b/Game/include/map/GameMap.h index 6f3437a..763ad6d 100644 --- a/Game/include/map/GameMap.h +++ b/Game/include/map/GameMap.h @@ -19,6 +19,7 @@ namespace frostbite2D { */ class GameMap : public Actor { public: + static constexpr size_t kInvalidMoveAreaIndex = static_cast(-1); using MapMoveArea = game::MoveAreaTarget; GameMap(); @@ -45,11 +46,16 @@ public: /// 检查当前位置是否进入 town move area,用于切图/传送判定。 MapMoveArea CheckIsItMoveArea(const Vec3& curPos) const; bool TryGetMoveAreaTarget(const Vec3& curPos, MapMoveArea& outTarget) const; + size_t FindMoveAreaIndex(const Vec3& curPos) const; const std::vector& GetMoveAreaInfo() const; + size_t GetMoveAreaCount() const { return moveArea_.size(); } Rect GetMovablePositionArea(size_t index) const; + const std::string& GetMapPath() const { return mapConfig_.mapPath; } + void SetDebugHighlightedMoveAreaIndex(size_t index); int GetBackgroundRepeatWidth() const { return backgroundRepeatWidth_; } bool IsDebugModeEnabled() const { return debugMode_; } + bool IsMovableAreaCheckEnabled() const { return movableAreaCheckEnabled_; } private: /// 初始化固定图层。图层名字与 DNF 地图层概念保持一致。 @@ -84,6 +90,8 @@ private: /// 地图配置里的整体 Y 偏移;既影响层位置,也影响地板校准。 int mapOffsetY_ = 0; bool debugMode_ = true; + /// 硬编码调试开关:关闭后忽略可行走区域检测,允许角色自由移动。 + bool movableAreaCheckEnabled_ = false; /// 当前地图正在播放的背景音乐。 Ptr currentMusic_; }; diff --git a/Game/include/map/GameMapLayer.h b/Game/include/map/GameMapLayer.h index f76e7c0..53e9004 100644 --- a/Game/include/map/GameMapLayer.h +++ b/Game/include/map/GameMapLayer.h @@ -7,17 +7,26 @@ namespace frostbite2D { class GameMapLayer : public Actor { public: + static constexpr size_t kInvalidMoveAreaIndex = static_cast(-1); + void Render() override; void SetDebugFeasibleAreaPolygon(const std::vector& polygon); - void AddDebugMoveAreaInfo(const Rect& rect); + void AddDebugMoveAreaInfo(const Rect& rect, size_t index); + void SetDebugHighlightedMoveAreaIndex(size_t index); void ClearDebugAreaInfo(); void AddObject(RefPtr obj); private: + struct DebugMoveAreaInfo { + Rect rect; + size_t index = kInvalidMoveAreaIndex; + }; + std::vector feasibleAreaPolygon_; std::vector feasibleAreaFillRects_; - std::vector moveAreaInfoList_; + std::vector moveAreaInfoList_; + size_t highlightedMoveAreaIndex_ = kInvalidMoveAreaIndex; }; } // namespace frostbite2D diff --git a/Game/src/common/GameDebugActor.cpp b/Game/src/common/GameDebugActor.cpp index bca9a77..1d46e3f 100644 --- a/Game/src/common/GameDebugActor.cpp +++ b/Game/src/common/GameDebugActor.cpp @@ -17,6 +17,17 @@ constexpr float kDebugHudPaddingX = 8.0f; constexpr float kDebugHudPaddingY = 6.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() { @@ -64,6 +75,9 @@ void GameDebugActor::DetachFromParent() { } void GameDebugActor::SetDebugMap(GameMap* map) { + if (debugMap_ && debugMap_ != map) { + debugMap_->SetDebugHighlightedMoveAreaIndex(GameMap::kInvalidMoveAreaIndex); + } debugMap_ = map; updateOverlay(); } @@ -74,6 +88,9 @@ void GameDebugActor::SetTrackedCharacter(CharacterObject* character) { } void GameDebugActor::ClearDebugContext() { + if (debugMap_) { + debugMap_->SetDebugHighlightedMoveAreaIndex(GameMap::kInvalidMoveAreaIndex); + } debugMap_ = nullptr; trackedCharacter_ = nullptr; setOverlayVisible(false); @@ -100,10 +117,7 @@ void GameDebugActor::initOverlay() { } coordText_ = TextSprite::create(); - coordText_->SetName("debugCoordText"); - coordText_->SetFont("default"); - coordText_->SetTextColor(1.0f, 1.0f, 1.0f, 1.0f); - coordText_->SetZOrder(1); + ConfigureTextLine(coordText_, "debugCoordText", 1); AddChild(coordText_); setOverlayVisible(false); @@ -112,19 +126,29 @@ void GameDebugActor::initOverlay() { void GameDebugActor::updateOverlay() { if (!coordText_ || !debugMap_ || !trackedCharacter_ || !debugMap_->IsDebugModeEnabled()) { + if (debugMap_) { + debugMap_->SetDebugHighlightedMoveAreaIndex(GameMap::kInvalidMoveAreaIndex); + } setOverlayVisible(false); return; } const CharacterWorldPosition& worldPosition = trackedCharacter_->GetWorldPosition(); - char textBuffer[64]; - SDL_snprintf(textBuffer, sizeof(textBuffer), "角色坐标: (%d, %d, %d)", - worldPosition.x, worldPosition.y, worldPosition.z); - coordText_->SetText(textBuffer); + Vec3 currentWorldPos(static_cast(worldPosition.x), + static_cast(worldPosition.y), + static_cast(worldPosition.z)); + size_t moveAreaIndex = debugMap_->FindMoveAreaIndex(currentWorldPos); + debugMap_->SetDebugHighlightedMoveAreaIndex(moveAreaIndex); - Vec2 textSize = coordText_->GetTextSize(); - Vec2 panelSize(textSize.x + kDebugHudPaddingX * 2.0f, - textSize.y + kDebugHudPaddingY * 2.0f); + char coordTextBuffer[96]; + SDL_snprintf(coordTextBuffer, sizeof(coordTextBuffer), "角色坐标: (%d, %d, %d)", + worldPosition.x, worldPosition.y, worldPosition.z); + coordText_->SetText(coordTextBuffer); + + Vec2 coordTextSize = coordText_->GetTextSize(); + float panelWidth = coordTextSize.x + kDebugHudPaddingX * 2.0f; + float panelHeight = coordTextSize.y + kDebugHudPaddingY * 2.0f; + Vec2 panelSize(panelWidth, panelHeight); if (background_) { background_->SetSize(panelSize); } diff --git a/Game/src/map/GameDataLoader.cpp b/Game/src/map/GameDataLoader.cpp index b48eccf..0ce6baf 100644 --- a/Game/src/map/GameDataLoader.cpp +++ b/Game/src/map/GameDataLoader.cpp @@ -101,6 +101,21 @@ int toInt(const std::string& value, int fallback = 0) { } } +MoveAreaBounds normalizeMoveAreaBounds(int x1, int y1, int x2, int y2) { + MoveAreaBounds bounds; + bounds.leftTop.x = static_cast(std::min(x1, x2)); + bounds.leftTop.y = static_cast(std::min(y1, y2)); + bounds.rightBottom.x = static_cast(std::max(x1, x2)); + bounds.rightBottom.y = static_cast(std::max(y1, y2)); + return bounds; +} + +Rect makeMoveAreaRect(const MoveAreaBounds& bounds) { + return Rect(bounds.leftTop.x, bounds.leftTop.y, + bounds.rightBottom.x - bounds.leftTop.x, + bounds.rightBottom.y - bounds.leftTop.y); +} + bool readScript(const std::string& path, ScriptTokenStream& outStream) { (void)path; return outStream.isValid(); @@ -257,14 +272,18 @@ bool loadMapConfig(const std::string& mapPath, MapConfig& outConfig) { if (token == "[/town movable area]") { break; } - Rect rect(static_cast(toInt(token)), - static_cast(toInt(stream.get())), - static_cast(toInt(stream.get())), - static_cast(toInt(stream.get()))); + int leftTopX = toInt(token); + int leftTopY = toInt(stream.get()); + int rightBottomX = toInt(stream.get()); + int rightBottomY = toInt(stream.get()); + MoveAreaBounds bounds = normalizeMoveAreaBounds( + leftTopX, leftTopY, rightBottomX, rightBottomY); + Rect rect = makeMoveAreaRect(bounds); MoveAreaTarget target; target.town = toInt(stream.get(), -2); target.area = toInt(stream.get(), -2); outConfig.townMovableAreas.push_back(rect); + outConfig.townMovableAreaBounds.push_back(bounds); outConfig.townMovableAreaTargets.push_back(target); } } else if (segment == "[virtual movable area]") { diff --git a/Game/src/map/GameMap.cpp b/Game/src/map/GameMap.cpp index c4b5e05..8de1d6a 100644 --- a/Game/src/map/GameMap.cpp +++ b/Game/src/map/GameMap.cpp @@ -131,6 +131,7 @@ void GameMap::clearLayerChildren() { moveArea_.clear(); currentMusic_.Reset(); backgroundRepeatWidth_ = 0; + SetDebugHighlightedMoveAreaIndex(kInvalidMoveAreaIndex); } bool GameMap::LoadMap(const std::string &mapName) { @@ -321,19 +322,10 @@ void GameMap::InitVirtualMovableArea() { if (movablePolygon_.empty()) { return; } - - float minX = movablePolygon_.front().x; - float minY = movablePolygon_.front().y; - float maxX = movablePolygon_.front().x; - float maxY = movablePolygon_.front().y; - for (const auto& point : movablePolygon_) { - minX = std::min(minX, point.x); - minY = std::min(minY, point.y); - maxX = std::max(maxX, point.x); - maxY = std::max(maxY, point.y); + if (!movableAreaCheckEnabled_) { + SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, + "GameMap: movable area check disabled, allowing free movement"); } - SDL_Log("GameMap: movable polygon vertices=%zu bounds=(%.0f, %.0f)-(%.0f, %.0f)", - movablePolygon_.size(), minX, minY, maxX, maxY); if (!debugMode_) { return; @@ -357,8 +349,10 @@ void GameMap::InitMoveArea() { return; } - for (const auto& rect : moveArea_) { - layerIt->second->AddDebugMoveAreaInfo(rect); + for (size_t i = 0; + i < moveArea_.size() && i < mapConfig_.townMovableAreaTargets.size(); + ++i) { + layerIt->second->AddDebugMoveAreaInfo(moveArea_[i], i); } } @@ -460,6 +454,9 @@ Vec3 GameMap::CheckIsItMovable(const Vec3& curPos, const Vec3& posOffset) const int targetY = RoundWorldCoordinate(curPos.y + posOffset.y); int targetZ = RoundWorldCoordinate(curPos.z + posOffset.z); Vec3 result = MakeIntegerWorldPosition(currentX, currentY, targetZ); + if (!movableAreaCheckEnabled_) { + return MakeIntegerWorldPosition(targetX, targetY, targetZ); + } if (movablePolygon_.size() < 3) { return MakeIntegerWorldPosition(targetX, targetY, targetZ); } @@ -502,19 +499,37 @@ GameMap::MapMoveArea GameMap::CheckIsItMoveArea(const Vec3& curPos) const { return MapMoveArea(); } -bool GameMap::TryGetMoveAreaTarget(const Vec3& curPos, MapMoveArea& outTarget) const { - // moveArea_ and townMovableAreaTargets share the same index mapping. +size_t GameMap::FindMoveAreaIndex(const Vec3& curPos) const { int currentX = RoundWorldCoordinate(curPos.x); int currentY = RoundWorldCoordinate(curPos.y); - for (size_t i = 0; i < moveArea_.size() && i < mapConfig_.townMovableAreaTargets.size(); ++i) { + for (size_t i = 0; + i < moveArea_.size() && i < mapConfig_.townMovableAreaTargets.size(); + ++i) { if (moveArea_[i].containsPoint(MakeIntegerWorldPoint(currentX, currentY))) { - outTarget = mapConfig_.townMovableAreaTargets[i]; - return true; + return i; } } + return kInvalidMoveAreaIndex; +} + +bool GameMap::TryGetMoveAreaTarget(const Vec3& curPos, MapMoveArea& outTarget) const { + size_t index = FindMoveAreaIndex(curPos); + if (index != kInvalidMoveAreaIndex && + index < mapConfig_.townMovableAreaTargets.size()) { + outTarget = mapConfig_.townMovableAreaTargets[index]; + return true; + } return false; } +void GameMap::SetDebugHighlightedMoveAreaIndex(size_t index) { + auto layerIt = layerMap_.find("max"); + if (layerIt == layerMap_.end() || !layerIt->second) { + return; + } + layerIt->second->SetDebugHighlightedMoveAreaIndex(index); +} + const std::vector& GameMap::GetMoveAreaInfo() const { return mapConfig_.townMovableAreaTargets; } diff --git a/Game/src/map/GameMapLayer.cpp b/Game/src/map/GameMapLayer.cpp index 116d433..8f1884d 100644 --- a/Game/src/map/GameMapLayer.cpp +++ b/Game/src/map/GameMapLayer.cpp @@ -13,6 +13,19 @@ constexpr float kDebugEdgePointSize = 5.0f; constexpr float kDebugVertexSize = 9.0f; constexpr float kDebugEdgeStep = 4.0f; +std::vector BuildRectPolygon(const Rect& rect) { + if (rect.empty()) { + return {}; + } + + return { + Vec2(rect.left(), rect.top()), + Vec2(rect.right(), rect.top()), + Vec2(rect.right(), rect.bottom()), + Vec2(rect.left(), rect.bottom()), + }; +} + std::vector BuildPolygonFillRects(const std::vector& polygon) { std::vector fillRects; if (polygon.size() < 3) { @@ -124,10 +137,15 @@ void GameMapLayer::Render() { Renderer::get().drawQuad(drawRect, Color(0.0f, 1.0f, 0.0f, kDebugAreaAlpha)); } - for (const auto& rect : moveAreaInfoList_) { - Rect drawRect(worldOrigin.x + rect.origin.x, worldOrigin.y + rect.origin.y, - rect.width(), rect.height()); - Renderer::get().drawQuad(drawRect, Color(0.0f, 0.0f, 1.0f, kDebugAreaAlpha)); + for (const auto& moveArea : moveAreaInfoList_) { + bool isHighlighted = moveArea.index == highlightedMoveAreaIndex_; + Color fillColor = + isHighlighted ? Color(0.0f, 1.0f, 1.0f, 0.60f) + : Color(0.0f, 0.0f, 1.0f, kDebugAreaAlpha); + Rect drawRect(worldOrigin.x + moveArea.rect.origin.x, + worldOrigin.y + moveArea.rect.origin.y, + moveArea.rect.width(), moveArea.rect.height()); + Renderer::get().drawQuad(drawRect, fillColor); } if (!feasibleAreaPolygon_.empty()) { @@ -135,6 +153,19 @@ void GameMapLayer::Render() { DrawPolygonOutline(feasibleAreaPolygon_, worldOrigin, outlineColor); DrawPolygonVertices(feasibleAreaPolygon_, worldOrigin, outlineColor); } + + for (auto& moveArea : moveAreaInfoList_) { + std::vector polygon = BuildRectPolygon(moveArea.rect); + if (polygon.empty()) { + continue; + } + bool isHighlighted = moveArea.index == highlightedMoveAreaIndex_; + Color moveAreaOutlineColor = + isHighlighted ? Color(0.0f, 1.0f, 1.0f, 1.0f) + : Color(0.0f, 0.0f, 1.0f, kDebugOutlineAlpha); + DrawPolygonOutline(polygon, worldOrigin, moveAreaOutlineColor); + DrawPolygonVertices(polygon, worldOrigin, moveAreaOutlineColor); + } } void GameMapLayer::SetDebugFeasibleAreaPolygon(const std::vector& polygon) { @@ -142,14 +173,23 @@ void GameMapLayer::SetDebugFeasibleAreaPolygon(const std::vector& polygon) feasibleAreaFillRects_ = BuildPolygonFillRects(feasibleAreaPolygon_); } -void GameMapLayer::AddDebugMoveAreaInfo(const Rect& rect) { - moveAreaInfoList_.push_back(rect); +void GameMapLayer::AddDebugMoveAreaInfo(const Rect& rect, size_t index) { + DebugMoveAreaInfo debugArea; + debugArea.rect = rect; + debugArea.index = index; + + moveAreaInfoList_.push_back(std::move(debugArea)); +} + +void GameMapLayer::SetDebugHighlightedMoveAreaIndex(size_t index) { + highlightedMoveAreaIndex_ = index; } void GameMapLayer::ClearDebugAreaInfo() { feasibleAreaPolygon_.clear(); feasibleAreaFillRects_.clear(); moveAreaInfoList_.clear(); + highlightedMoveAreaIndex_ = kInvalidMoveAreaIndex; } void GameMapLayer::AddObject(RefPtr obj) { diff --git a/Game/src/scene/GameMapTestScene.cpp b/Game/src/scene/GameMapTestScene.cpp index 88c453b..c65e40f 100644 --- a/Game/src/scene/GameMapTestScene.cpp +++ b/Game/src/scene/GameMapTestScene.cpp @@ -1,4 +1,4 @@ -#include "scene/GameMapTestScene.h" +#include "scene/GameMapTestScene.h" #include #include #include @@ -7,7 +7,7 @@ namespace frostbite2D { namespace { -constexpr char kTestMapPath[] = "map/elvengard/elvengard.map"; +constexpr char kTestMapPath[] = "map/elvengard/d_elvengard.map"; } // namespace