feat(地图系统): 增强移动区域调试功能
- 添加移动区域边界结构体MoveAreaBounds并集成到地图配置中 - 实现移动区域索引查找和高亮显示功能 - 增加可移动区域检查开关movableAreaCheckEnabled - 优化调试信息显示,包括坐标和当前所在移动区域 - 重构移动区域相关代码,提高可维护性
This commit is contained in:
@@ -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> 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<float>(worldPosition.x),
|
||||
static_cast<float>(worldPosition.y),
|
||||
static_cast<float>(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);
|
||||
}
|
||||
|
||||
@@ -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<float>(std::min(x1, x2));
|
||||
bounds.leftTop.y = static_cast<float>(std::min(y1, y2));
|
||||
bounds.rightBottom.x = static_cast<float>(std::max(x1, x2));
|
||||
bounds.rightBottom.y = static_cast<float>(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<float>(toInt(token)),
|
||||
static_cast<float>(toInt(stream.get())),
|
||||
static_cast<float>(toInt(stream.get())),
|
||||
static_cast<float>(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]") {
|
||||
|
||||
@@ -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::MapMoveArea>& GameMap::GetMoveAreaInfo() const {
|
||||
return mapConfig_.townMovableAreaTargets;
|
||||
}
|
||||
|
||||
@@ -13,6 +13,19 @@ constexpr float kDebugEdgePointSize = 5.0f;
|
||||
constexpr float kDebugVertexSize = 9.0f;
|
||||
constexpr float kDebugEdgeStep = 4.0f;
|
||||
|
||||
std::vector<Vec2> 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<Rect> BuildPolygonFillRects(const std::vector<Vec2>& polygon) {
|
||||
std::vector<Rect> 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<Vec2> 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<Vec2>& polygon) {
|
||||
@@ -142,14 +173,23 @@ void GameMapLayer::SetDebugFeasibleAreaPolygon(const std::vector<Vec2>& 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<Actor> obj) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "scene/GameMapTestScene.h"
|
||||
#include "scene/GameMapTestScene.h"
|
||||
#include <SDL2/SDL.h>
|
||||
#include <frostbite2D/scene/scene_manager.h>
|
||||
#include <frostbite2D/utils/startup_trace.h>
|
||||
@@ -7,7 +7,7 @@ namespace frostbite2D {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr char kTestMapPath[] = "map/elvengard/elvengard.map";
|
||||
constexpr char kTestMapPath[] = "map/elvengard/d_elvengard.map";
|
||||
|
||||
} // namespace
|
||||
|
||||
|
||||
Reference in New Issue
Block a user