feat: 添加游戏数学工具类并重构相关代码

refactor: 将数学工具函数移至GameMath类
feat(音频): 实现地图音频控制器
feat(调试): 添加游戏调试UI组件
feat(地图): 增加移动区域边界获取方法
fix(角色): 修复角色移动区域抑制逻辑
refactor(世界): 重构游戏世界场景初始化
docs(音频): 完善音频数据库注释
This commit is contained in:
2026-04-06 22:22:40 +08:00
parent f86ce35b68
commit 35c80247b3
21 changed files with 893 additions and 215 deletions

View File

@@ -15,7 +15,9 @@ Vec2 RoundWorldPoint(const Vec2& pos) {
} // namespace
GameTown::GameTown() = default;
GameTown::GameTown() {
EnableEventReceive();
}
bool GameTown::Init(int index, const std::string& townPath) {
game::TownConfig config;
@@ -68,10 +70,25 @@ RefPtr<GameMap> GameTown::GetArea(int index) const {
return it->map;
}
RefPtr<GameMap> GameTown::GetCurrentArea() const {
if (curMapIndex_ == -1) {
return nullptr;
}
return GetArea(curMapIndex_);
}
void GameTown::AddCharacter(RefPtr<Actor> actor, int areaIndex) {
int targetMapIndex = areaIndex;
if (areaIndex == -2) {
targetMapIndex = sariaRoomId_ != -1 ? sariaRoomId_ : (mapList_.empty() ? -1 : mapList_.front().areaId);
if (sariaRoomId_ != -1) {
targetMapIndex = sariaRoomId_;
} else {
targetMapIndex = mapList_.empty() ? -1 : mapList_.front().areaId;
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
"GameTown: no gate area configured, fallback to first area %d",
targetMapIndex);
}
}
auto mapIt = std::find_if(mapList_.begin(), mapList_.end(),

View File

@@ -1,22 +1,57 @@
#include "world/GameWorld.h"
#include "character/CharacterObject.h"
#include "map/GameDataLoader.h"
#include "scene/GameDebugUIScene.h"
#include <SDL2/SDL.h>
#include <frostbite2D/scene/scene_manager.h>
namespace frostbite2D {
namespace {
constexpr int kDefaultTownId = 1;
Vec2 ComputeMoveAreaCenter(const game::MoveAreaBounds& bounds) {
return Vec2((bounds.leftTop.x + bounds.rightBottom.x) * 0.5f,
(bounds.leftTop.y + bounds.rightBottom.y) * 0.5f);
}
void SetActorGroundPosition(RefPtr<Actor> actor, const Vec2& position) {
if (!actor) {
return;
}
if (auto* character = dynamic_cast<CharacterObject*>(actor.Get())) {
character->SetCharacterPosition(position);
return;
}
actor->SetPosition(position);
}
} // namespace
GameWorld::GameWorld() = default;
void GameWorld::onEnter() {
Scene::onEnter();
if (initialized_) {
return;
EnsureDebugScene();
if (!initialized_) {
initialized_ = InitWorld();
}
initialized_ = InitWorld();
RefreshDebugContext();
}
void GameWorld::onExit() {
pendingCharacterMove_.reset();
ClearSuppressedMoveArea();
if (debugScene_) {
debugScene_->ClearDebugContext();
SceneManager::get().RemoveUIScene(debugScene_.Get());
}
Scene::onExit();
}
@@ -26,6 +61,14 @@ void GameWorld::Update(float deltaTime) {
}
bool GameWorld::InitWorld() {
RemoveAllChildren();
townPathMap_.clear();
townMap_.clear();
mainActor_.Reset();
pendingCharacterMove_.reset();
ClearSuppressedMoveArea();
curTown_ = -1;
townPathMap_ = game::loadTownList();
if (townPathMap_.empty()) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "GameWorld: no town entries found");
@@ -45,7 +88,36 @@ bool GameWorld::InitWorld() {
return false;
}
AddCharacter(nullptr, townMap_.begin()->first);
int defaultTownId = kDefaultTownId;
if (townMap_.find(defaultTownId) == townMap_.end()) {
defaultTownId = townMap_.begin()->first;
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
"GameWorld: default town %d missing, fallback to town %d",
kDefaultTownId, defaultTownId);
}
if (!InitMainCharacter(defaultTownId)) {
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
"GameWorld: falling back to empty town %d without main character",
defaultTownId);
AddCharacter(nullptr, defaultTownId);
}
return true;
}
bool GameWorld::InitMainCharacter(int townId) {
auto mainCharacter = MakePtr<CharacterObject>();
if (!mainCharacter->Construction(0)) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"GameWorld: failed to construct default main character");
return false;
}
mainCharacter->EnableEventReceive();
mainCharacter->SetEventPriority(-100);
mainCharacter->SetInputEnabled(true);
AddCharacter(mainCharacter, townId);
return true;
}
@@ -60,6 +132,7 @@ void GameWorld::AddCharacter(RefPtr<Actor> actor, int townId) {
curTown_ = townId;
it->second->AddCharacter(actor);
AddChild(it->second);
RefreshDebugContext();
}
void GameWorld::MoveCharacter(RefPtr<Actor> actor, int townId, int area) {
@@ -69,6 +142,44 @@ void GameWorld::MoveCharacter(RefPtr<Actor> actor, int townId, int area) {
return;
}
ClearSuppressedMoveArea();
int sourceTownId = curTown_;
int sourceAreaId = -1;
if (curTown_ != -1) {
auto currentTown = townMap_.find(curTown_);
if (currentTown != townMap_.end()) {
sourceAreaId = currentTown->second->GetCurAreaIndex();
}
}
RefPtr<GameMap> targetMap = townIt->second->GetArea(area);
if (actor && targetMap && sourceTownId != -1 && sourceAreaId != -1) {
bool foundTransitionEntry = false;
const auto& moveAreaInfo = targetMap->GetMoveAreaInfo();
for (size_t i = 0; i < moveAreaInfo.size(); ++i) {
if (moveAreaInfo[i].town != sourceTownId || moveAreaInfo[i].area != sourceAreaId) {
continue;
}
game::MoveAreaBounds bounds = targetMap->GetMovablePositionBounds(i);
SetActorGroundPosition(actor, ComputeMoveAreaCenter(bounds));
SetSuppressedMoveArea(targetMap.Get(), i);
foundTransitionEntry = true;
break;
}
if (!foundTransitionEntry) {
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
"GameWorld: no matching move area in town %d area %d for source town %d area %d",
townId, area, sourceTownId, sourceAreaId);
}
} else if (actor && !targetMap) {
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
"GameWorld: target area %d missing in town %d, use town fallback spawn",
area, townId);
}
if (curTown_ != -1) {
auto currentTown = townMap_.find(curTown_);
if (currentTown != townMap_.end()) {
@@ -80,6 +191,7 @@ void GameWorld::MoveCharacter(RefPtr<Actor> actor, int townId, int area) {
mainActor_ = actor;
townIt->second->AddCharacter(actor, area);
AddChild(townIt->second);
RefreshDebugContext();
}
void GameWorld::RequestMoveCharacter(RefPtr<Actor> actor, int townId, int area) {
@@ -92,6 +204,22 @@ void GameWorld::RequestMoveCharacter(RefPtr<Actor> actor, int townId, int area)
pendingCharacterMove_ = PendingCharacterMove{actor, townId, area};
}
void GameWorld::UpdateMoveAreaSuppression(GameMap* map, size_t moveAreaIndex) {
if (!suppressedMoveArea_) {
return;
}
if (suppressedMoveArea_->map != map ||
suppressedMoveArea_->moveAreaIndex != moveAreaIndex) {
ClearSuppressedMoveArea();
}
}
bool GameWorld::ShouldSuppressMoveArea(GameMap* map, size_t moveAreaIndex) const {
return suppressedMoveArea_ && suppressedMoveArea_->map == map &&
suppressedMoveArea_->moveAreaIndex == moveAreaIndex;
}
void GameWorld::ProcessPendingCharacterMove() {
if (!pendingCharacterMove_) {
return;
@@ -102,6 +230,63 @@ void GameWorld::ProcessPendingCharacterMove() {
MoveCharacter(move.actor, move.townId, move.area);
}
void GameWorld::EnsureDebugScene() {
if (!debugScene_) {
debugScene_ = MakePtr<GameDebugUIScene>();
}
SceneManager::get().RemoveUIScene(debugScene_.Get());
SceneManager::get().PushUIScene(debugScene_);
}
void GameWorld::RefreshDebugContext() {
if (!debugScene_) {
return;
}
debugScene_->SetDebugContext(GetCurrentMap(), GetMainCharacter());
}
void GameWorld::SetSuppressedMoveArea(GameMap* map, size_t moveAreaIndex) {
if (!map || moveAreaIndex == GameMap::kInvalidMoveAreaIndex) {
ClearSuppressedMoveArea();
return;
}
suppressedMoveArea_ = SuppressedMoveArea{map, moveAreaIndex};
}
void GameWorld::ClearSuppressedMoveArea() {
suppressedMoveArea_.reset();
}
Actor* GameWorld::GetMainActor() const {
return mainActor_.Get();
}
CharacterObject* GameWorld::GetMainCharacter() const {
return dynamic_cast<CharacterObject*>(mainActor_.Get());
}
GameTown* GameWorld::GetCurrentTown() const {
auto it = townMap_.find(curTown_);
if (it == townMap_.end()) {
return nullptr;
}
return it->second.Get();
}
GameMap* GameWorld::GetCurrentMap() const {
GameTown* town = GetCurrentTown();
if (!town) {
return nullptr;
}
RefPtr<GameMap> area = town->GetCurrentArea();
return area.Get();
}
GameWorld* GameWorld::GetWorld() {
return dynamic_cast<GameWorld*>(SceneManager::get().GetCurrentScene());
}