From 3e2e25eb93c606de3a7fcea75edd2a412678e37a Mon Sep 17 00:00:00 2001 From: 1165565692-lang <1165565692@qq.com> Date: Tue, 9 Jun 2026 14:50:50 +0800 Subject: [PATCH] Smooth battle zone camera transitions --- game/docs/planning/game_architecture_board.md | 2 +- game/docs/planning/scene_build_plan.md | 1 + game/src/data/whitebox_level.cpp | 6 +-- game/src/scene/whitebox_scene.cpp | 42 ++++++++++++++++++- game/src/scene/whitebox_scene.h | 8 ++++ 5 files changed, 53 insertions(+), 6 deletions(-) diff --git a/game/docs/planning/game_architecture_board.md b/game/docs/planning/game_architecture_board.md index 990a4e4..e63ca22 100644 --- a/game/docs/planning/game_architecture_board.md +++ b/game/docs/planning/game_architecture_board.md @@ -178,7 +178,7 @@ mindmap - `scene/whitebox_scene.*` - `scene/camera_controller.*`:玩家跟随、dead zone、平滑和世界边界 clamp。 -- `CameraZoneLock`:玩家进入 battle zone 后摄像机锁定到 zone camera bounds,锁定范围会防御性保证不小于视口,当前用 `F2` 或离开 zone 做 debug 清场解锁。 +- `CameraZoneLock`:玩家进入 battle zone 后摄像机平滑切换到 zone camera bounds,可移动锁定范围会防御性保证不小于视口,当前用 `F2` 或离开 zone 做 debug 清场解锁。 下一步: diff --git a/game/docs/planning/scene_build_plan.md b/game/docs/planning/scene_build_plan.md index c26caf6..f51c47f 100644 --- a/game/docs/planning/scene_build_plan.md +++ b/game/docs/planning/scene_build_plan.md @@ -24,6 +24,7 @@ - `CameraController` 已接入:修复场景 `OnUpdate()` 不被调用导致摄像机不动的问题,并提供跟随、dead zone、平滑和世界边界 clamp。 - `CameraZoneLock` 已接入:玩家进入 battle zone 后摄像机临时锁定到 zone camera bounds,`F2` 临时清场并恢复全关卡跟随。 - 已修复 zone camera bounds 小于 1280 视口导致摄像机锁死的问题;所有锁镜头范围必须不小于当前视口,debug 阶段玩家离开 zone 会自动清场解锁。 +- 已修复进入 battle zone 时摄像机硬切的问题;zone camera bounds 表示摄像机可移动范围,进入/离开 zone 都通过短过渡平滑切换约束。 ## 2. 第一关场景目标 diff --git a/game/src/data/whitebox_level.cpp b/game/src/data/whitebox_level.cpp index 4d21ce9..b457ba0 100644 --- a/game/src/data/whitebox_level.cpp +++ b/game/src/data/whitebox_level.cpp @@ -112,17 +112,17 @@ LevelDefinition CreateWhiteboxLevel() { level.battleZones = { {"zone_01_intro", frostbite2D::Rect(520.0f, 260.0f, 560.0f, 340.0f), - frostbite2D::Rect(320.0f, 0.0f, 1280.0f, 720.0f), + frostbite2D::Rect(0.0f, 0.0f, 1940.0f, 720.0f), {{"zone_01_enemy_a", frostbite2D::Vec2(760.0f, 560.0f)}, {"zone_01_enemy_b", frostbite2D::Vec2(980.0f, 560.0f)}}}, {"zone_02_platform", frostbite2D::Rect(1420.0f, 250.0f, 680.0f, 350.0f), - frostbite2D::Rect(1280.0f, 0.0f, 1280.0f, 720.0f), + frostbite2D::Rect(660.0f, 0.0f, 2300.0f, 720.0f), {{"zone_02_enemy_a", frostbite2D::Vec2(1620.0f, 560.0f)}, {"zone_02_enemy_b", frostbite2D::Vec2(1900.0f, 560.0f)}}}, {"zone_03_exit", frostbite2D::Rect(2380.0f, 250.0f, 720.0f, 350.0f), - frostbite2D::Rect(1920.0f, 0.0f, 1280.0f, 720.0f), + frostbite2D::Rect(1600.0f, 0.0f, 1600.0f, 720.0f), {{"zone_03_enemy_a", frostbite2D::Vec2(2580.0f, 560.0f)}, {"zone_03_enemy_b", frostbite2D::Vec2(2860.0f, 560.0f)}}}, }; diff --git a/game/src/scene/whitebox_scene.cpp b/game/src/scene/whitebox_scene.cpp index 8b5844f..e53aa3e 100644 --- a/game/src/scene/whitebox_scene.cpp +++ b/game/src/scene/whitebox_scene.cpp @@ -6,6 +6,7 @@ #include #include #include +#include namespace ns_game { @@ -26,6 +27,7 @@ void WhiteboxScene::onEnter() { void WhiteboxScene::Update(float deltaTime) { frostbite2D::Scene::Update(deltaTime); updateBattleZones(); + updateCameraBoundsBlend(deltaTime); updateCamera(deltaTime); } @@ -93,6 +95,7 @@ void WhiteboxScene::configureCamera() { config.viewportWidth = static_cast(config::kVirtualWidth); config.viewportHeight = static_cast(config::kVirtualHeight); cameraController_.Configure(config); + currentCameraBounds_ = level_.cameraBounds; if (player_) { cameraController_.SnapToTarget(*camera, player_->GetWorldBounds()); @@ -111,6 +114,14 @@ void WhiteboxScene::toggleDebugOverlay() { debugFlags_.showSpawnPoints = enabled; } +void WhiteboxScene::beginCameraBoundsBlend( + const frostbite2D::Rect& targetBounds) { + cameraBoundsBlendStart_ = currentCameraBounds_; + cameraBoundsBlendTarget_ = targetBounds; + cameraBoundsBlendTimer_ = 0.0f; + cameraBoundsBlendActive_ = true; +} + void WhiteboxScene::clearActiveBattleZone() { if (activeBattleZoneIndex_ < 0 || activeBattleZoneIndex_ >= static_cast(battleZoneStates_.size())) { @@ -119,7 +130,34 @@ void WhiteboxScene::clearActiveBattleZone() { battleZoneStates_[static_cast(activeBattleZoneIndex_)].cleared = true; activeBattleZoneIndex_ = -1; - cameraController_.SetWorldBounds(level_.cameraBounds); + beginCameraBoundsBlend(level_.cameraBounds); +} + +void WhiteboxScene::updateCameraBoundsBlend(float deltaTime) { + if (!cameraBoundsBlendActive_) { + return; + } + + cameraBoundsBlendTimer_ += deltaTime; + const float t = cameraBoundsBlendDuration_ > 0.0f + ? std::min(cameraBoundsBlendTimer_ / + cameraBoundsBlendDuration_, + 1.0f) + : 1.0f; + const auto lerp = [t](float from, float to) { + return from + (to - from) * t; + }; + + currentCameraBounds_ = frostbite2D::Rect( + lerp(cameraBoundsBlendStart_.left(), cameraBoundsBlendTarget_.left()), + lerp(cameraBoundsBlendStart_.top(), cameraBoundsBlendTarget_.top()), + lerp(cameraBoundsBlendStart_.width(), cameraBoundsBlendTarget_.width()), + lerp(cameraBoundsBlendStart_.height(), cameraBoundsBlendTarget_.height())); + cameraController_.SetWorldBounds(currentCameraBounds_); + + if (t >= 1.0f) { + cameraBoundsBlendActive_ = false; + } } void WhiteboxScene::updateBattleZones() { @@ -146,7 +184,7 @@ void WhiteboxScene::updateBattleZones() { runtime.triggered = true; activeBattleZoneIndex_ = static_cast(i); - cameraController_.SetWorldBounds(level_.battleZones[i].cameraBounds); + beginCameraBoundsBlend(level_.battleZones[i].cameraBounds); break; } } diff --git a/game/src/scene/whitebox_scene.h b/game/src/scene/whitebox_scene.h index 6acc653..a830dfa 100644 --- a/game/src/scene/whitebox_scene.h +++ b/game/src/scene/whitebox_scene.h @@ -31,7 +31,9 @@ private: std::vector buildBattleZoneDebugInfo() const; void configureCamera(); void toggleDebugOverlay(); + void beginCameraBoundsBlend(const frostbite2D::Rect& targetBounds); void clearActiveBattleZone(); + void updateCameraBoundsBlend(float deltaTime); void updateBattleZones(); void updateCamera(float deltaTime); @@ -39,6 +41,12 @@ private: DebugFlags debugFlags_; std::vector battleZoneStates_; int activeBattleZoneIndex_ = -1; + bool cameraBoundsBlendActive_ = false; + float cameraBoundsBlendTimer_ = 0.0f; + float cameraBoundsBlendDuration_ = 0.35f; + frostbite2D::Rect cameraBoundsBlendStart_; + frostbite2D::Rect cameraBoundsBlendTarget_; + frostbite2D::Rect currentCameraBounds_; CameraController cameraController_; StageRenderer stageRenderer_; DebugOverlay debugOverlay_;