Add player camera controller

This commit is contained in:
2026-06-09 14:11:12 +08:00
parent 8a89346978
commit 47936abed0
6 changed files with 156 additions and 26 deletions
+72
View File
@@ -0,0 +1,72 @@
#include "camera_controller.h"
#include <algorithm>
#include <cmath>
namespace ns_game {
namespace {
float clampValue(float value, float minValue, float maxValue) {
if (maxValue < minValue) {
return minValue;
}
return std::clamp(value, minValue, maxValue);
}
float smoothFactor(float smoothing, float deltaTime) {
if (smoothing <= 0.0f) {
return 1.0f;
}
return 1.0f - std::exp(-smoothing * std::max(deltaTime, 0.0f));
}
} // namespace
void CameraController::Configure(const CameraControllerConfig& config) {
config_ = config;
}
void CameraController::SnapToTarget(frostbite2D::Camera& camera,
const frostbite2D::Rect& targetBounds) {
camera.setPosition(clampToWorld(
resolveDesiredPosition(camera.getPosition(), targetBounds)));
}
void CameraController::Update(frostbite2D::Camera& camera,
const frostbite2D::Rect& targetBounds,
float deltaTime) {
const frostbite2D::Vec2 current = camera.getPosition();
const frostbite2D::Vec2 desired =
clampToWorld(resolveDesiredPosition(current, targetBounds));
const float t = smoothFactor(config_.smoothing, deltaTime);
camera.setPosition(frostbite2D::Vec2(
current.x + (desired.x - current.x) * t,
current.y + (desired.y - current.y) * t));
}
frostbite2D::Vec2 CameraController::resolveDesiredPosition(
const frostbite2D::Vec2& currentPosition,
const frostbite2D::Rect& targetBounds) const {
frostbite2D::Vec2 desired = currentPosition;
const float targetCenterX = targetBounds.left() + targetBounds.width() * 0.5f;
const float screenX = targetCenterX - currentPosition.x;
if (screenX < config_.deadZone.left()) {
desired.x = targetCenterX - config_.deadZone.left();
} else if (screenX > config_.deadZone.right()) {
desired.x = targetCenterX - config_.deadZone.right();
}
desired.y = config_.worldBounds.top();
return desired;
}
frostbite2D::Vec2 CameraController::clampToWorld(
const frostbite2D::Vec2& position) const {
return frostbite2D::Vec2(
clampValue(position.x, config_.worldBounds.left(),
config_.worldBounds.right() - config_.viewportWidth),
clampValue(position.y, config_.worldBounds.top(),
config_.worldBounds.bottom() - config_.viewportHeight));
}
} // namespace ns_game
+33
View File
@@ -0,0 +1,33 @@
#pragma once
#include <frostbite2D/graphics/camera.h>
#include <frostbite2D/types/type_math.h>
namespace ns_game {
struct CameraControllerConfig {
frostbite2D::Rect worldBounds;
float viewportWidth = 1280.0f;
float viewportHeight = 720.0f;
frostbite2D::Rect deadZone = frostbite2D::Rect(420.0f, 0.0f, 340.0f, 720.0f);
float smoothing = 14.0f;
};
class CameraController {
public:
void Configure(const CameraControllerConfig& config);
void SnapToTarget(frostbite2D::Camera& camera,
const frostbite2D::Rect& targetBounds);
void Update(frostbite2D::Camera& camera, const frostbite2D::Rect& targetBounds,
float deltaTime);
private:
frostbite2D::Vec2 resolveDesiredPosition(
const frostbite2D::Vec2& currentPosition,
const frostbite2D::Rect& targetBounds) const;
frostbite2D::Vec2 clampToWorld(const frostbite2D::Vec2& position) const;
CameraControllerConfig config_;
};
} // namespace ns_game
+33 -13
View File
@@ -6,7 +6,6 @@
#include <frostbite2D/event/key_event.h>
#include <frostbite2D/graphics/camera.h>
#include <frostbite2D/graphics/renderer.h>
#include <algorithm>
namespace ns_game {
@@ -18,11 +17,12 @@ void WhiteboxScene::onEnter() {
player_->SetTopLeftPosition(level_.playerSpawn);
player_->SetMovementWorld(&level_.collision);
AddChild(player_);
configureCamera();
}
void WhiteboxScene::OnUpdate(float deltaTime) {
(void)deltaTime;
updateCamera();
void WhiteboxScene::Update(float deltaTime) {
frostbite2D::Scene::Update(deltaTime);
updateCamera(deltaTime);
}
bool WhiteboxScene::OnEvent(const frostbite2D::Event& event) {
@@ -46,6 +46,31 @@ void WhiteboxScene::Render() {
debugOverlay_.Render(level_, debugFlags_, player_ ? &playerBounds : nullptr);
}
frostbite2D::Camera* WhiteboxScene::activeCamera() const {
auto* renderer = frostbite2D::Application::get().getRenderer();
if (!renderer) {
return nullptr;
}
return renderer->getCamera();
}
void WhiteboxScene::configureCamera() {
auto* camera = activeCamera();
if (!camera) {
return;
}
CameraControllerConfig config;
config.worldBounds = level_.cameraBounds;
config.viewportWidth = static_cast<float>(config::kVirtualWidth);
config.viewportHeight = static_cast<float>(config::kVirtualHeight);
cameraController_.Configure(config);
if (player_) {
cameraController_.SnapToTarget(*camera, player_->GetWorldBounds());
}
}
void WhiteboxScene::toggleDebugOverlay() {
const bool enabled = !(debugFlags_.showStageGrid || debugFlags_.showCollision ||
debugFlags_.showActorBounds ||
@@ -58,22 +83,17 @@ void WhiteboxScene::toggleDebugOverlay() {
debugFlags_.showSpawnPoints = enabled;
}
void WhiteboxScene::updateCamera() {
void WhiteboxScene::updateCamera(float deltaTime) {
if (!player_) {
return;
}
auto* renderer = frostbite2D::Application::get().getRenderer();
if (!renderer || !renderer->getCamera()) {
auto* camera = activeCamera();
if (!camera) {
return;
}
const auto playerPos = player_->GetTopLeftPosition();
const float targetX = std::clamp(playerPos.x - 420.0f,
level_.cameraBounds.left(),
level_.cameraBounds.right() -
static_cast<float>(config::kVirtualWidth));
renderer->getCamera()->setPosition(frostbite2D::Vec2(targetX, 0.0f));
cameraController_.Update(*camera, player_->GetWorldBounds(), deltaTime);
}
} // namespace ns_game
+6 -2
View File
@@ -4,6 +4,7 @@
#include "../core/debug_flags.h"
#include "../data/level_definition.h"
#include "../debug/debug_overlay.h"
#include "camera_controller.h"
#include "../stage/stage_renderer.h"
#include <frostbite2D/scene/scene.h>
@@ -14,16 +15,19 @@ namespace ns_game {
class WhiteboxScene : public frostbite2D::Scene {
public:
void onEnter() override;
void OnUpdate(float deltaTime) override;
void Update(float deltaTime) override;
bool OnEvent(const frostbite2D::Event& event) override;
void Render() override;
private:
frostbite2D::Camera* activeCamera() const;
void configureCamera();
void toggleDebugOverlay();
void updateCamera();
void updateCamera(float deltaTime);
LevelDefinition level_;
DebugFlags debugFlags_;
CameraController cameraController_;
StageRenderer stageRenderer_;
DebugOverlay debugOverlay_;
frostbite2D::Ptr<PlayerActor> player_;