Smooth battle zone camera transitions
This commit is contained in:
@@ -178,7 +178,7 @@ mindmap
|
|||||||
|
|
||||||
- `scene/whitebox_scene.*`
|
- `scene/whitebox_scene.*`
|
||||||
- `scene/camera_controller.*`:玩家跟随、dead zone、平滑和世界边界 clamp。
|
- `scene/camera_controller.*`:玩家跟随、dead zone、平滑和世界边界 clamp。
|
||||||
- `CameraZoneLock`:玩家进入 battle zone 后摄像机锁定到 zone camera bounds,锁定范围会防御性保证不小于视口,当前用 `F2` 或离开 zone 做 debug 清场解锁。
|
- `CameraZoneLock`:玩家进入 battle zone 后摄像机平滑切换到 zone camera bounds,可移动锁定范围会防御性保证不小于视口,当前用 `F2` 或离开 zone 做 debug 清场解锁。
|
||||||
|
|
||||||
下一步:
|
下一步:
|
||||||
|
|
||||||
|
|||||||
@@ -24,6 +24,7 @@
|
|||||||
- `CameraController` 已接入:修复场景 `OnUpdate()` 不被调用导致摄像机不动的问题,并提供跟随、dead zone、平滑和世界边界 clamp。
|
- `CameraController` 已接入:修复场景 `OnUpdate()` 不被调用导致摄像机不动的问题,并提供跟随、dead zone、平滑和世界边界 clamp。
|
||||||
- `CameraZoneLock` 已接入:玩家进入 battle zone 后摄像机临时锁定到 zone camera bounds,`F2` 临时清场并恢复全关卡跟随。
|
- `CameraZoneLock` 已接入:玩家进入 battle zone 后摄像机临时锁定到 zone camera bounds,`F2` 临时清场并恢复全关卡跟随。
|
||||||
- 已修复 zone camera bounds 小于 1280 视口导致摄像机锁死的问题;所有锁镜头范围必须不小于当前视口,debug 阶段玩家离开 zone 会自动清场解锁。
|
- 已修复 zone camera bounds 小于 1280 视口导致摄像机锁死的问题;所有锁镜头范围必须不小于当前视口,debug 阶段玩家离开 zone 会自动清场解锁。
|
||||||
|
- 已修复进入 battle zone 时摄像机硬切的问题;zone camera bounds 表示摄像机可移动范围,进入/离开 zone 都通过短过渡平滑切换约束。
|
||||||
|
|
||||||
## 2. 第一关场景目标
|
## 2. 第一关场景目标
|
||||||
|
|
||||||
|
|||||||
@@ -112,17 +112,17 @@ LevelDefinition CreateWhiteboxLevel() {
|
|||||||
level.battleZones = {
|
level.battleZones = {
|
||||||
{"zone_01_intro",
|
{"zone_01_intro",
|
||||||
frostbite2D::Rect(520.0f, 260.0f, 560.0f, 340.0f),
|
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_a", frostbite2D::Vec2(760.0f, 560.0f)},
|
||||||
{"zone_01_enemy_b", frostbite2D::Vec2(980.0f, 560.0f)}}},
|
{"zone_01_enemy_b", frostbite2D::Vec2(980.0f, 560.0f)}}},
|
||||||
{"zone_02_platform",
|
{"zone_02_platform",
|
||||||
frostbite2D::Rect(1420.0f, 250.0f, 680.0f, 350.0f),
|
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_a", frostbite2D::Vec2(1620.0f, 560.0f)},
|
||||||
{"zone_02_enemy_b", frostbite2D::Vec2(1900.0f, 560.0f)}}},
|
{"zone_02_enemy_b", frostbite2D::Vec2(1900.0f, 560.0f)}}},
|
||||||
{"zone_03_exit",
|
{"zone_03_exit",
|
||||||
frostbite2D::Rect(2380.0f, 250.0f, 720.0f, 350.0f),
|
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_a", frostbite2D::Vec2(2580.0f, 560.0f)},
|
||||||
{"zone_03_enemy_b", frostbite2D::Vec2(2860.0f, 560.0f)}}},
|
{"zone_03_enemy_b", frostbite2D::Vec2(2860.0f, 560.0f)}}},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
#include <frostbite2D/event/key_event.h>
|
#include <frostbite2D/event/key_event.h>
|
||||||
#include <frostbite2D/graphics/camera.h>
|
#include <frostbite2D/graphics/camera.h>
|
||||||
#include <frostbite2D/graphics/renderer.h>
|
#include <frostbite2D/graphics/renderer.h>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
namespace ns_game {
|
namespace ns_game {
|
||||||
|
|
||||||
@@ -26,6 +27,7 @@ void WhiteboxScene::onEnter() {
|
|||||||
void WhiteboxScene::Update(float deltaTime) {
|
void WhiteboxScene::Update(float deltaTime) {
|
||||||
frostbite2D::Scene::Update(deltaTime);
|
frostbite2D::Scene::Update(deltaTime);
|
||||||
updateBattleZones();
|
updateBattleZones();
|
||||||
|
updateCameraBoundsBlend(deltaTime);
|
||||||
updateCamera(deltaTime);
|
updateCamera(deltaTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -93,6 +95,7 @@ void WhiteboxScene::configureCamera() {
|
|||||||
config.viewportWidth = static_cast<float>(config::kVirtualWidth);
|
config.viewportWidth = static_cast<float>(config::kVirtualWidth);
|
||||||
config.viewportHeight = static_cast<float>(config::kVirtualHeight);
|
config.viewportHeight = static_cast<float>(config::kVirtualHeight);
|
||||||
cameraController_.Configure(config);
|
cameraController_.Configure(config);
|
||||||
|
currentCameraBounds_ = level_.cameraBounds;
|
||||||
|
|
||||||
if (player_) {
|
if (player_) {
|
||||||
cameraController_.SnapToTarget(*camera, player_->GetWorldBounds());
|
cameraController_.SnapToTarget(*camera, player_->GetWorldBounds());
|
||||||
@@ -111,6 +114,14 @@ void WhiteboxScene::toggleDebugOverlay() {
|
|||||||
debugFlags_.showSpawnPoints = enabled;
|
debugFlags_.showSpawnPoints = enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void WhiteboxScene::beginCameraBoundsBlend(
|
||||||
|
const frostbite2D::Rect& targetBounds) {
|
||||||
|
cameraBoundsBlendStart_ = currentCameraBounds_;
|
||||||
|
cameraBoundsBlendTarget_ = targetBounds;
|
||||||
|
cameraBoundsBlendTimer_ = 0.0f;
|
||||||
|
cameraBoundsBlendActive_ = true;
|
||||||
|
}
|
||||||
|
|
||||||
void WhiteboxScene::clearActiveBattleZone() {
|
void WhiteboxScene::clearActiveBattleZone() {
|
||||||
if (activeBattleZoneIndex_ < 0 ||
|
if (activeBattleZoneIndex_ < 0 ||
|
||||||
activeBattleZoneIndex_ >= static_cast<int>(battleZoneStates_.size())) {
|
activeBattleZoneIndex_ >= static_cast<int>(battleZoneStates_.size())) {
|
||||||
@@ -119,7 +130,34 @@ void WhiteboxScene::clearActiveBattleZone() {
|
|||||||
|
|
||||||
battleZoneStates_[static_cast<size_t>(activeBattleZoneIndex_)].cleared = true;
|
battleZoneStates_[static_cast<size_t>(activeBattleZoneIndex_)].cleared = true;
|
||||||
activeBattleZoneIndex_ = -1;
|
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() {
|
void WhiteboxScene::updateBattleZones() {
|
||||||
@@ -146,7 +184,7 @@ void WhiteboxScene::updateBattleZones() {
|
|||||||
|
|
||||||
runtime.triggered = true;
|
runtime.triggered = true;
|
||||||
activeBattleZoneIndex_ = static_cast<int>(i);
|
activeBattleZoneIndex_ = static_cast<int>(i);
|
||||||
cameraController_.SetWorldBounds(level_.battleZones[i].cameraBounds);
|
beginCameraBoundsBlend(level_.battleZones[i].cameraBounds);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,7 +31,9 @@ private:
|
|||||||
std::vector<BattleZoneDebugInfo> buildBattleZoneDebugInfo() const;
|
std::vector<BattleZoneDebugInfo> buildBattleZoneDebugInfo() const;
|
||||||
void configureCamera();
|
void configureCamera();
|
||||||
void toggleDebugOverlay();
|
void toggleDebugOverlay();
|
||||||
|
void beginCameraBoundsBlend(const frostbite2D::Rect& targetBounds);
|
||||||
void clearActiveBattleZone();
|
void clearActiveBattleZone();
|
||||||
|
void updateCameraBoundsBlend(float deltaTime);
|
||||||
void updateBattleZones();
|
void updateBattleZones();
|
||||||
void updateCamera(float deltaTime);
|
void updateCamera(float deltaTime);
|
||||||
|
|
||||||
@@ -39,6 +41,12 @@ private:
|
|||||||
DebugFlags debugFlags_;
|
DebugFlags debugFlags_;
|
||||||
std::vector<BattleZoneRuntime> battleZoneStates_;
|
std::vector<BattleZoneRuntime> battleZoneStates_;
|
||||||
int activeBattleZoneIndex_ = -1;
|
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_;
|
CameraController cameraController_;
|
||||||
StageRenderer stageRenderer_;
|
StageRenderer stageRenderer_;
|
||||||
DebugOverlay debugOverlay_;
|
DebugOverlay debugOverlay_;
|
||||||
|
|||||||
Reference in New Issue
Block a user