From 6cd1b42fef21fa5cc3f2cb46a8ae0e1f70e245ce Mon Sep 17 00:00:00 2001 From: Lenheart <947330670@qq.com> Date: Sun, 5 Apr 2026 12:04:07 +0800 Subject: [PATCH] =?UTF-8?q?feat(=E5=9C=B0=E5=9B=BE=E7=B3=BB=E7=BB=9F):=20?= =?UTF-8?q?=E5=AE=9E=E7=8E=B0=E8=A7=92=E8=89=B2=E7=A7=BB=E5=8A=A8=E7=BA=A6?= =?UTF-8?q?=E6=9D=9F=E5=92=8C=E5=9C=B0=E5=9B=BE=E5=88=87=E6=8D=A2=E5=8A=9F?= =?UTF-8?q?=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 添加地图移动区域检测和角色移动约束逻辑 引入地图切换请求队列机制,支持延迟处理角色传送 在CharacterObject中实现地图边界检测和位置约束应用 --- Game/include/character/CharacterObject.h | 5 ++ Game/include/map/GameMap.h | 1 + Game/include/world/GameWorld.h | 11 +++++ Game/src/character/CharacterObject.cpp | 62 ++++++++++++++++++++++++ Game/src/map/GameMap.cpp | 17 +++++-- Game/src/world/GameWorld.cpp | 25 ++++++++++ 6 files changed, 117 insertions(+), 4 deletions(-) diff --git a/Game/include/character/CharacterObject.h b/Game/include/character/CharacterObject.h index 9fc8f90..514e4cf 100644 --- a/Game/include/character/CharacterObject.h +++ b/Game/include/character/CharacterObject.h @@ -15,6 +15,8 @@ namespace frostbite2D { +class GameMap; + /// @brief 角色系统的主聚合对象。 /// /// 这个类可以理解为“角色外壳”: @@ -170,6 +172,9 @@ private: void CommitPendingActionContext(const std::string& defaultRequestedActionId, const std::string& defaultSourceActionId, CharacterStateId defaultSourceStateId); + GameMap* FindOwningMap() const; + void ApplyMapMovementConstraints(const CharacterWorldPosition& previousPosition); + void QueueMapTransitionIfNeeded(); void SyncActorPositionFromWorld(); bool SetActionStrict(const std::string& actionName, const char* phase, diff --git a/Game/include/map/GameMap.h b/Game/include/map/GameMap.h index a74acf2..89e7b34 100644 --- a/Game/include/map/GameMap.h +++ b/Game/include/map/GameMap.h @@ -44,6 +44,7 @@ public: Vec3 CheckIsItMovable(const Vec3& curPos, const Vec3& posOffset) const; /// 检查当前位置是否进入 town move area,用于切图/传送判定。 MapMoveArea CheckIsItMoveArea(const Vec3& curPos) const; + bool TryGetMoveAreaTarget(const Vec3& curPos, MapMoveArea& outTarget) const; const std::vector& GetMoveAreaInfo() const; Rect GetMovablePositionArea(size_t index) const; diff --git a/Game/include/world/GameWorld.h b/Game/include/world/GameWorld.h index 27ce05e..4272a75 100644 --- a/Game/include/world/GameWorld.h +++ b/Game/include/world/GameWorld.h @@ -3,6 +3,7 @@ #include "world/GameTown.h" #include #include +#include namespace frostbite2D { @@ -13,18 +14,28 @@ public: void onEnter() override; void onExit() override; + void Update(float deltaTime) override; void AddCharacter(RefPtr actor, int townId); void MoveCharacter(RefPtr actor, int townId, int area); + void RequestMoveCharacter(RefPtr actor, int townId, int area); static GameWorld* GetWorld(); private: + struct PendingCharacterMove { + RefPtr actor; + int townId = -1; + int area = -1; + }; + bool InitWorld(); + void ProcessPendingCharacterMove(); std::map townPathMap_; std::map> townMap_; RefPtr mainActor_; + std::optional pendingCharacterMove_; int curTown_ = -1; bool initialized_ = false; }; diff --git a/Game/src/character/CharacterObject.cpp b/Game/src/character/CharacterObject.cpp index 3091353..d0c9b5c 100644 --- a/Game/src/character/CharacterObject.cpp +++ b/Game/src/character/CharacterObject.cpp @@ -1,5 +1,8 @@ #include "character/CharacterObject.h" +#include "map/GameMap.h" +#include "world/GameWorld.h" #include +#include #include #include #include @@ -34,6 +37,15 @@ const char* NonEmptyOrPlaceholder(const std::string& value, const char* placehol return value.empty() ? placeholder : value.c_str(); } +Vec3 ToWorldVector(const CharacterWorldPosition& position) { + return Vec3(static_cast(position.x), static_cast(position.y), + static_cast(position.z)); +} + +int32 RoundWorldCoordinate(float value) { + return static_cast(std::lround(value)); +} + } // namespace bool CharacterObject::Construction(int jobId) { @@ -147,6 +159,53 @@ void CharacterObject::SyncActorPositionFromWorld() { SetZOrder(worldPosition.y); } +GameMap* CharacterObject::FindOwningMap() const { + Actor* node = GetParent(); + while (node) { + if (auto* map = dynamic_cast(node)) { + return map; + } + node = node->GetParent(); + } + return nullptr; +} + +void CharacterObject::ApplyMapMovementConstraints( + const CharacterWorldPosition& previousPosition) { + GameMap* map = FindOwningMap(); + if (!map) { + return; + } + + Vec3 previousWorldPos = ToWorldVector(previousPosition); + Vec3 worldOffset(static_cast(motor_.position.x - previousPosition.x), + static_cast(motor_.position.y - previousPosition.y), + static_cast(motor_.position.z - previousPosition.z)); + Vec3 resolvedWorldPos = map->CheckIsItMovable(previousWorldPos, worldOffset); + motor_.position.x = RoundWorldCoordinate(resolvedWorldPos.x); + motor_.position.y = RoundWorldCoordinate(resolvedWorldPos.y); + motor_.position.z = RoundWorldCoordinate(resolvedWorldPos.z); +} + +void CharacterObject::QueueMapTransitionIfNeeded() { + GameMap* map = FindOwningMap(); + if (!map) { + return; + } + + GameMap::MapMoveArea target; + if (!map->TryGetMoveAreaTarget(ToWorldVector(motor_.position), target)) { + return; + } + + GameWorld* world = GameWorld::GetWorld(); + if (!world) { + return; + } + + world->RequestMoveCharacter(RefPtr(this), target.town, target.area); +} + void CharacterObject::PushCommand(const CharacterCommand& command) { commandBuffer_.Submit(command); } @@ -244,7 +303,10 @@ void CharacterObject::OnUpdate(float deltaTime) { inputRouter_.EmitCommands(commandBuffer_); currentIntent_ = commandBuffer_.BuildIntent(); stateMachine_.Update(*this, commandBuffer_, currentIntent_, deltaTime); + CharacterWorldPosition previousPosition = motor_.position; motor_.Update(deltaTime); + ApplyMapMovementConstraints(previousPosition); + QueueMapTransitionIfNeeded(); SyncActorPositionFromWorld(); SetFacing(motor_.facing); } diff --git a/Game/src/map/GameMap.cpp b/Game/src/map/GameMap.cpp index 83e1b84..37c38f7 100644 --- a/Game/src/map/GameMap.cpp +++ b/Game/src/map/GameMap.cpp @@ -95,8 +95,8 @@ bool GameMap::LoadMap(const std::string &mapName) { InitTile(); InitBackgroundAnimation(); InitMapAnimation(); - // InitVirtualMovableArea(); - // InitMoveArea(); + InitVirtualMovableArea(); + InitMoveArea(); return true; } @@ -405,15 +405,24 @@ Vec3 GameMap::CheckIsItMovable(const Vec3& curPos, const Vec3& posOffset) const } GameMap::MapMoveArea GameMap::CheckIsItMoveArea(const Vec3& curPos) const { + MapMoveArea target; + if (TryGetMoveAreaTarget(curPos, target)) { + return target; + } + return MapMoveArea(); +} + +bool GameMap::TryGetMoveAreaTarget(const Vec3& curPos, MapMoveArea& outTarget) const { // moveArea_ and townMovableAreaTargets share the same index mapping. int currentX = RoundWorldCoordinate(curPos.x); int currentY = RoundWorldCoordinate(curPos.y); for (size_t i = 0; i < moveArea_.size() && i < mapConfig_.townMovableAreaTargets.size(); ++i) { if (moveArea_[i].containsPoint(MakeIntegerWorldPoint(currentX, currentY))) { - return mapConfig_.townMovableAreaTargets[i]; + outTarget = mapConfig_.townMovableAreaTargets[i]; + return true; } } - return MapMoveArea(); + return false; } const std::vector& GameMap::GetMoveAreaInfo() const { diff --git a/Game/src/world/GameWorld.cpp b/Game/src/world/GameWorld.cpp index 2b85bca..da7689b 100644 --- a/Game/src/world/GameWorld.cpp +++ b/Game/src/world/GameWorld.cpp @@ -20,6 +20,11 @@ void GameWorld::onExit() { Scene::onExit(); } +void GameWorld::Update(float deltaTime) { + Scene::Update(deltaTime); + ProcessPendingCharacterMove(); +} + bool GameWorld::InitWorld() { townPathMap_ = game::loadTownList(); if (townPathMap_.empty()) { @@ -77,6 +82,26 @@ void GameWorld::MoveCharacter(RefPtr actor, int townId, int area) { AddChild(townIt->second); } +void GameWorld::RequestMoveCharacter(RefPtr actor, int townId, int area) { + if (!actor) { + SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, + "GameWorld: ignore move request without actor"); + return; + } + + pendingCharacterMove_ = PendingCharacterMove{actor, townId, area}; +} + +void GameWorld::ProcessPendingCharacterMove() { + if (!pendingCharacterMove_) { + return; + } + + PendingCharacterMove move = *pendingCharacterMove_; + pendingCharacterMove_.reset(); + MoveCharacter(move.actor, move.townId, move.area); +} + GameWorld* GameWorld::GetWorld() { return dynamic_cast(SceneManager::get().GetCurrentScene()); }