feat: 添加游戏数学工具类并重构相关代码
refactor: 将数学工具函数移至GameMath类 feat(音频): 实现地图音频控制器 feat(调试): 添加游戏调试UI组件 feat(地图): 增加移动区域边界获取方法 fix(角色): 修复角色移动区域抑制逻辑 refactor(世界): 重构游戏世界场景初始化 docs(音频): 完善音频数据库注释
This commit is contained in:
@@ -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());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user