Build first scene skeleton

This commit is contained in:
2026-06-09 13:56:30 +08:00
parent 3c5a4968ba
commit 51f1bdd166
11 changed files with 270 additions and 44 deletions
+2
View File
@@ -6,6 +6,8 @@ struct DebugFlags {
bool showStageGrid = true;
bool showCollision = true;
bool showActorBounds = true;
bool showBattleZones = true;
bool showSpawnPoints = true;
};
} // namespace ns_game
+40
View File
@@ -3,18 +3,58 @@
#include "../movement/platform_world.h"
#include <frostbite2D/types/type_color.h>
#include <frostbite2D/types/type_math.h>
#include <string>
#include <vector>
namespace ns_game {
enum class StageLayerRole {
BackgroundFar,
BackgroundMid,
LevelVisual,
PropsBack,
PropsFront
};
struct StageRect {
frostbite2D::Rect bounds;
frostbite2D::Color color;
StageLayerRole layer = StageLayerRole::PropsBack;
};
struct SpawnPoint {
std::string id;
frostbite2D::Vec2 position;
};
struct BattleZoneDefinition {
std::string id;
frostbite2D::Rect trigger;
frostbite2D::Rect cameraBounds;
std::vector<SpawnPoint> enemySpawns;
};
struct StageLayer {
std::string id;
StageLayerRole role = StageLayerRole::LevelVisual;
float parallax = 1.0f;
std::vector<StageRect> rects;
};
struct WhiteboxProp {
frostbite2D::Rect bounds;
frostbite2D::Color color;
};
struct LevelDefinition {
float worldWidth = 0.0f;
float worldHeight = 0.0f;
frostbite2D::Vec2 playerSpawn;
frostbite2D::Rect cameraBounds;
PlatformWorld collision;
std::vector<StageLayer> layers;
std::vector<WhiteboxProp> props;
std::vector<BattleZoneDefinition> battleZones;
};
LevelDefinition CreateWhiteboxLevel();
+107 -2
View File
@@ -4,14 +4,102 @@ namespace ns_game {
LevelDefinition CreateWhiteboxLevel() {
LevelDefinition level;
level.worldWidth = 3200.0f;
level.worldHeight = 720.0f;
level.playerSpawn = frostbite2D::Vec2(160.0f, 380.0f);
level.cameraBounds = frostbite2D::Rect(0.0f, 0.0f, level.worldWidth,
level.worldHeight);
level.collision.levelLeft = 0.0f;
level.collision.levelRight = 1600.0f;
level.collision.levelRight = level.worldWidth;
level.collision.platforms = {
frostbite2D::Rect(0.0f, 600.0f, 1600.0f, 80.0f),
frostbite2D::Rect(0.0f, 600.0f, 3200.0f, 80.0f),
frostbite2D::Rect(230.0f, 500.0f, 220.0f, 28.0f),
frostbite2D::Rect(560.0f, 430.0f, 260.0f, 28.0f),
frostbite2D::Rect(930.0f, 510.0f, 260.0f, 28.0f),
frostbite2D::Rect(1250.0f, 420.0f, 230.0f, 28.0f),
frostbite2D::Rect(1760.0f, 500.0f, 300.0f, 28.0f),
frostbite2D::Rect(2180.0f, 455.0f, 260.0f, 28.0f),
frostbite2D::Rect(2650.0f, 515.0f, 360.0f, 28.0f),
};
level.layers = {
{"background_far",
StageLayerRole::BackgroundFar,
0.25f,
{{frostbite2D::Rect(0.0f, 0.0f, 3200.0f, 720.0f),
frostbite2D::Color(0.08f, 0.10f, 0.14f, 1.0f),
StageLayerRole::BackgroundFar},
{frostbite2D::Rect(360.0f, 120.0f, 420.0f, 220.0f),
frostbite2D::Color(0.13f, 0.16f, 0.21f, 1.0f),
StageLayerRole::BackgroundFar},
{frostbite2D::Rect(1380.0f, 90.0f, 560.0f, 260.0f),
frostbite2D::Color(0.12f, 0.15f, 0.20f, 1.0f),
StageLayerRole::BackgroundFar},
{frostbite2D::Rect(2440.0f, 140.0f, 520.0f, 210.0f),
frostbite2D::Color(0.13f, 0.16f, 0.21f, 1.0f),
StageLayerRole::BackgroundFar}}},
{"background_mid",
StageLayerRole::BackgroundMid,
0.55f,
{{frostbite2D::Rect(80.0f, 360.0f, 280.0f, 240.0f),
frostbite2D::Color(0.18f, 0.22f, 0.27f, 1.0f),
StageLayerRole::BackgroundMid},
{frostbite2D::Rect(1020.0f, 310.0f, 340.0f, 290.0f),
frostbite2D::Color(0.18f, 0.22f, 0.27f, 1.0f),
StageLayerRole::BackgroundMid},
{frostbite2D::Rect(2020.0f, 330.0f, 360.0f, 270.0f),
frostbite2D::Color(0.18f, 0.22f, 0.27f, 1.0f),
StageLayerRole::BackgroundMid},
{frostbite2D::Rect(2860.0f, 280.0f, 260.0f, 320.0f),
frostbite2D::Color(0.18f, 0.22f, 0.27f, 1.0f),
StageLayerRole::BackgroundMid}}},
{"level_visual",
StageLayerRole::LevelVisual,
1.0f,
{{frostbite2D::Rect(0.0f, 600.0f, 3200.0f, 80.0f),
frostbite2D::Color(0.62f, 0.68f, 0.73f, 1.0f),
StageLayerRole::LevelVisual},
{frostbite2D::Rect(230.0f, 500.0f, 220.0f, 28.0f),
frostbite2D::Color(0.62f, 0.68f, 0.73f, 1.0f),
StageLayerRole::LevelVisual},
{frostbite2D::Rect(560.0f, 430.0f, 260.0f, 28.0f),
frostbite2D::Color(0.62f, 0.68f, 0.73f, 1.0f),
StageLayerRole::LevelVisual},
{frostbite2D::Rect(930.0f, 510.0f, 260.0f, 28.0f),
frostbite2D::Color(0.62f, 0.68f, 0.73f, 1.0f),
StageLayerRole::LevelVisual},
{frostbite2D::Rect(1250.0f, 420.0f, 230.0f, 28.0f),
frostbite2D::Color(0.62f, 0.68f, 0.73f, 1.0f),
StageLayerRole::LevelVisual},
{frostbite2D::Rect(1760.0f, 500.0f, 300.0f, 28.0f),
frostbite2D::Color(0.62f, 0.68f, 0.73f, 1.0f),
StageLayerRole::LevelVisual},
{frostbite2D::Rect(2180.0f, 455.0f, 260.0f, 28.0f),
frostbite2D::Color(0.62f, 0.68f, 0.73f, 1.0f),
StageLayerRole::LevelVisual},
{frostbite2D::Rect(2650.0f, 515.0f, 360.0f, 28.0f),
frostbite2D::Color(0.62f, 0.68f, 0.73f, 1.0f),
StageLayerRole::LevelVisual}}},
{"props_back",
StageLayerRole::PropsBack,
1.0f,
{{frostbite2D::Rect(118.0f, 392.0f, 88.0f, 208.0f),
frostbite2D::Color(0.26f, 0.30f, 0.34f, 1.0f),
StageLayerRole::PropsBack},
{frostbite2D::Rect(780.0f, 250.0f, 72.0f, 180.0f),
frostbite2D::Color(0.26f, 0.30f, 0.34f, 1.0f),
StageLayerRole::PropsBack},
{frostbite2D::Rect(1440.0f, 300.0f, 80.0f, 300.0f),
frostbite2D::Color(0.26f, 0.30f, 0.34f, 1.0f),
StageLayerRole::PropsBack},
{frostbite2D::Rect(1880.0f, 360.0f, 110.0f, 240.0f),
frostbite2D::Color(0.26f, 0.30f, 0.34f, 1.0f),
StageLayerRole::PropsBack},
{frostbite2D::Rect(2520.0f, 390.0f, 92.0f, 210.0f),
frostbite2D::Color(0.26f, 0.30f, 0.34f, 1.0f),
StageLayerRole::PropsBack},
{frostbite2D::Rect(3040.0f, 315.0f, 82.0f, 285.0f),
frostbite2D::Color(0.26f, 0.30f, 0.34f, 1.0f),
StageLayerRole::PropsBack}}},
};
level.props = {
{frostbite2D::Rect(118.0f, 392.0f, 88.0f, 208.0f),
@@ -21,6 +109,23 @@ LevelDefinition CreateWhiteboxLevel() {
{frostbite2D::Rect(1440.0f, 300.0f, 80.0f, 300.0f),
frostbite2D::Color(0.26f, 0.30f, 0.34f, 1.0f)},
};
level.battleZones = {
{"zone_01_intro",
frostbite2D::Rect(520.0f, 260.0f, 560.0f, 340.0f),
frostbite2D::Rect(320.0f, 0.0f, 760.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, 900.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(2240.0f, 0.0f, 960.0f, 720.0f),
{{"zone_03_enemy_a", frostbite2D::Vec2(2580.0f, 560.0f)},
{"zone_03_enemy_b", frostbite2D::Vec2(2860.0f, 560.0f)}}},
};
return level;
}
+36
View File
@@ -24,6 +24,19 @@ void drawOutline(const frostbite2D::Rect& rect, const frostbite2D::Color& color,
color);
}
void drawCross(const frostbite2D::Vec2& position,
const frostbite2D::Color& color) {
constexpr float kSize = 18.0f;
constexpr float kThickness = 3.0f;
drawRect(frostbite2D::Rect(position.x - kSize * 0.5f,
position.y - kThickness * 0.5f, kSize,
kThickness),
color);
drawRect(frostbite2D::Rect(position.x - kThickness * 0.5f,
position.y - kSize * 0.5f, kThickness, kSize),
color);
}
} // namespace
void DebugOverlay::Render(const LevelDefinition& level, const DebugFlags& flags,
@@ -31,6 +44,12 @@ void DebugOverlay::Render(const LevelDefinition& level, const DebugFlags& flags,
if (flags.showCollision) {
drawCollision(level);
}
if (flags.showBattleZones) {
drawBattleZones(level);
}
if (flags.showSpawnPoints) {
drawSpawnPoints(level);
}
if (flags.showActorBounds && playerBounds) {
drawActorBounds(*playerBounds);
}
@@ -46,4 +65,21 @@ void DebugOverlay::drawActorBounds(const frostbite2D::Rect& bounds) const {
drawOutline(bounds, frostbite2D::Color(1.0f, 0.78f, 0.1f, 0.9f));
}
void DebugOverlay::drawBattleZones(const LevelDefinition& level) const {
for (const BattleZoneDefinition& zone : level.battleZones) {
drawOutline(zone.trigger, frostbite2D::Color(0.95f, 0.25f, 0.25f, 0.9f),
3.0f);
drawOutline(zone.cameraBounds,
frostbite2D::Color(0.25f, 0.55f, 1.0f, 0.75f), 2.0f);
}
}
void DebugOverlay::drawSpawnPoints(const LevelDefinition& level) const {
for (const BattleZoneDefinition& zone : level.battleZones) {
for (const SpawnPoint& spawn : zone.enemySpawns) {
drawCross(spawn.position, frostbite2D::Color(1.0f, 0.2f, 0.9f, 0.95f));
}
}
}
} // namespace ns_game
+2
View File
@@ -15,6 +15,8 @@ public:
private:
void drawCollision(const LevelDefinition& level) const;
void drawActorBounds(const frostbite2D::Rect& bounds) const;
void drawBattleZones(const LevelDefinition& level) const;
void drawSpawnPoints(const LevelDefinition& level) const;
};
} // namespace ns_game
+29 -2
View File
@@ -3,6 +3,7 @@
#include "../core/game_config.h"
#include <frostbite2D/core/application.h>
#include <frostbite2D/event/key_event.h>
#include <frostbite2D/graphics/camera.h>
#include <frostbite2D/graphics/renderer.h>
#include <algorithm>
@@ -14,6 +15,7 @@ void WhiteboxScene::onEnter() {
level_ = CreateWhiteboxLevel();
player_ = frostbite2D::MakePtr<PlayerActor>();
player_->SetTopLeftPosition(level_.playerSpawn);
player_->SetMovementWorld(&level_.collision);
AddChild(player_);
}
@@ -23,6 +25,19 @@ void WhiteboxScene::OnUpdate(float deltaTime) {
updateCamera();
}
bool WhiteboxScene::OnEvent(const frostbite2D::Event& event) {
if (event.getType() == frostbite2D::EventType::KeyDown) {
const auto& keyEvent = static_cast<const frostbite2D::KeyEvent&>(event);
if (!keyEvent.isRepeat() &&
keyEvent.getKeyCode() == frostbite2D::KeyCode::F1) {
toggleDebugOverlay();
return true;
}
}
return frostbite2D::Scene::OnEvent(event);
}
void WhiteboxScene::Render() {
stageRenderer_.Render(level_, debugFlags_);
frostbite2D::Scene::Render();
@@ -31,6 +46,18 @@ void WhiteboxScene::Render() {
debugOverlay_.Render(level_, debugFlags_, player_ ? &playerBounds : nullptr);
}
void WhiteboxScene::toggleDebugOverlay() {
const bool enabled = !(debugFlags_.showStageGrid || debugFlags_.showCollision ||
debugFlags_.showActorBounds ||
debugFlags_.showBattleZones ||
debugFlags_.showSpawnPoints);
debugFlags_.showStageGrid = enabled;
debugFlags_.showCollision = enabled;
debugFlags_.showActorBounds = enabled;
debugFlags_.showBattleZones = enabled;
debugFlags_.showSpawnPoints = enabled;
}
void WhiteboxScene::updateCamera() {
if (!player_) {
return;
@@ -43,8 +70,8 @@ void WhiteboxScene::updateCamera() {
const auto playerPos = player_->GetTopLeftPosition();
const float targetX = std::clamp(playerPos.x - 420.0f,
level_.collision.levelLeft,
level_.collision.levelRight -
level_.cameraBounds.left(),
level_.cameraBounds.right() -
static_cast<float>(config::kVirtualWidth));
renderer->getCamera()->setPosition(frostbite2D::Vec2(targetX, 0.0f));
}
+2
View File
@@ -15,9 +15,11 @@ class WhiteboxScene : public frostbite2D::Scene {
public:
void onEnter() override;
void OnUpdate(float deltaTime) override;
bool OnEvent(const frostbite2D::Event& event) override;
void Render() override;
private:
void toggleDebugOverlay();
void updateCamera();
LevelDefinition level_;
+22 -12
View File
@@ -2,6 +2,7 @@
#include "../core/game_config.h"
#include <frostbite2D/graphics/camera.h>
#include <frostbite2D/graphics/renderer.h>
namespace ns_game {
@@ -21,11 +22,12 @@ float worldWidthOf(const LevelDefinition& level) {
void StageRenderer::Render(const LevelDefinition& level,
const DebugFlags& debugFlags) const {
drawBackground(level);
for (const StageLayer& layer : level.layers) {
drawLayer(level, layer);
}
if (debugFlags.showStageGrid) {
drawGrid(level);
}
drawProps(level);
drawPlatforms(level);
}
void StageRenderer::drawBackground(const LevelDefinition& level) const {
@@ -47,18 +49,26 @@ void StageRenderer::drawGrid(const LevelDefinition& level) const {
}
}
void StageRenderer::drawProps(const LevelDefinition& level) const {
for (const auto& prop : level.props) {
drawRect(prop.bounds, prop.color);
void StageRenderer::drawLayer(const LevelDefinition& level,
const StageLayer& layer) const {
(void)level;
frostbite2D::Vec2 parallaxOffset;
if (auto* camera = frostbite2D::Renderer::get().getCamera()) {
const frostbite2D::Vec2 cameraPos = camera->getPosition();
parallaxOffset.x = cameraPos.x * (1.0f - layer.parallax);
parallaxOffset.y = cameraPos.y * (1.0f - layer.parallax);
}
}
void StageRenderer::drawPlatforms(const LevelDefinition& level) const {
for (const auto& platform : level.collision.platforms) {
drawRect(platform, frostbite2D::Color(0.62f, 0.68f, 0.73f, 1.0f));
drawRect(frostbite2D::Rect(platform.left(), platform.top(),
platform.width(), 4.0f),
frostbite2D::Color(0.91f, 0.95f, 0.98f, 1.0f));
for (const StageRect& rect : layer.rects) {
frostbite2D::Rect bounds(rect.bounds.left() + parallaxOffset.x,
rect.bounds.top() + parallaxOffset.y,
rect.bounds.width(), rect.bounds.height());
drawRect(bounds, rect.color);
if (layer.role == StageLayerRole::LevelVisual) {
drawRect(frostbite2D::Rect(bounds.left(), bounds.top(), bounds.width(),
4.0f),
frostbite2D::Color(0.91f, 0.95f, 0.98f, 1.0f));
}
}
}
+1 -2
View File
@@ -12,8 +12,7 @@ public:
private:
void drawBackground(const LevelDefinition& level) const;
void drawGrid(const LevelDefinition& level) const;
void drawProps(const LevelDefinition& level) const;
void drawPlatforms(const LevelDefinition& level) const;
void drawLayer(const LevelDefinition& level, const StageLayer& layer) const;
};
} // namespace ns_game