Add stage map tile rendering path
This commit is contained in:
@@ -32,6 +32,21 @@ struct StageRect {
|
||||
StageLayerSource source;
|
||||
};
|
||||
|
||||
struct TileSetDefinition {
|
||||
std::string id;
|
||||
std::string texturePath;
|
||||
float tileWidth = 0.0f;
|
||||
float tileHeight = 0.0f;
|
||||
frostbite2D::Color fallbackColor;
|
||||
};
|
||||
|
||||
struct StageTile {
|
||||
std::string tileSetId;
|
||||
frostbite2D::Rect bounds;
|
||||
frostbite2D::Rect sourceRect;
|
||||
frostbite2D::Color fallbackColor;
|
||||
};
|
||||
|
||||
struct SpawnPoint {
|
||||
std::string id;
|
||||
frostbite2D::Vec2 position;
|
||||
@@ -60,6 +75,7 @@ struct StageLayer {
|
||||
StageLayerRole role = StageLayerRole::LevelVisual;
|
||||
float parallax = 1.0f;
|
||||
std::vector<StageRect> rects;
|
||||
std::vector<StageTile> tiles;
|
||||
};
|
||||
|
||||
struct LevelDefinition {
|
||||
@@ -68,6 +84,7 @@ struct LevelDefinition {
|
||||
frostbite2D::Vec2 playerSpawn;
|
||||
frostbite2D::Rect cameraBounds;
|
||||
PlatformWorld collision;
|
||||
std::vector<TileSetDefinition> tileSets;
|
||||
std::vector<StageLayer> layers;
|
||||
std::vector<BattleZoneDefinition> battleZones;
|
||||
};
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <SDL2/SDL.h>
|
||||
|
||||
#include <charconv>
|
||||
#include <cmath>
|
||||
#include <sstream>
|
||||
|
||||
namespace ns_game {
|
||||
@@ -66,6 +67,16 @@ parseColor(const std::vector<std::string>& tokens, size_t start) {
|
||||
return frostbite2D::Color(*r, *g, *b, *a);
|
||||
}
|
||||
|
||||
std::optional<TileSetDefinition*>
|
||||
findTileSet(ParseContext& context, const std::string& id) {
|
||||
for (TileSetDefinition& tileSet : context.level.tileSets) {
|
||||
if (tileSet.id == id) {
|
||||
return &tileSet;
|
||||
}
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<StageLayerRole> parseLayerRole(const std::string& value) {
|
||||
if (value == "BackgroundFar") {
|
||||
return StageLayerRole::BackgroundFar;
|
||||
@@ -188,6 +199,28 @@ bool parseLine(ParseContext& context, const std::string& path,
|
||||
return true;
|
||||
}
|
||||
|
||||
if (command == "tileset") {
|
||||
if (!ensureNoOpenBlock(context, path, lineNumber) || tokens.size() != 9) {
|
||||
return warnLine(path, lineNumber,
|
||||
"expected: tileset <id> <texture> <tile_w> <tile_h> <r> <g> <b> <a>");
|
||||
}
|
||||
if (findTileSet(context, tokens[1])) {
|
||||
return warnLine(path, lineNumber, "duplicate tileset id: " + tokens[1]);
|
||||
}
|
||||
|
||||
const auto tileWidth = parseFloat(tokens[3]);
|
||||
const auto tileHeight = parseFloat(tokens[4]);
|
||||
const auto fallbackColor = parseColor(tokens, 5);
|
||||
if (!tileWidth || !tileHeight || *tileWidth <= 0.0f ||
|
||||
*tileHeight <= 0.0f || !fallbackColor) {
|
||||
return warnLine(path, lineNumber, "invalid tileset definition");
|
||||
}
|
||||
|
||||
context.level.tileSets.push_back(
|
||||
{tokens[1], tokens[2], *tileWidth, *tileHeight, *fallbackColor});
|
||||
return true;
|
||||
}
|
||||
|
||||
if (command == "layer") {
|
||||
if (!ensureNoOpenBlock(context, path, lineNumber) || tokens.size() != 4) {
|
||||
return warnLine(path, lineNumber,
|
||||
@@ -226,6 +259,59 @@ bool parseLine(ParseContext& context, const std::string& path,
|
||||
return true;
|
||||
}
|
||||
|
||||
if (command == "tile_rect") {
|
||||
if (!context.currentLayer || tokens.size() != 10) {
|
||||
return warnLine(path, lineNumber,
|
||||
"expected in layer: tile_rect <tileset> <x> <y> <columns> <rows> <tile_index> <r> <g> <b>");
|
||||
}
|
||||
|
||||
const auto tileSet = findTileSet(context, tokens[1]);
|
||||
if (!tileSet) {
|
||||
return warnLine(path, lineNumber, "unknown tileset id: " + tokens[1]);
|
||||
}
|
||||
|
||||
const auto x = parseFloat(tokens[2]);
|
||||
const auto y = parseFloat(tokens[3]);
|
||||
const auto columnsFloat = parseFloat(tokens[4]);
|
||||
const auto rowsFloat = parseFloat(tokens[5]);
|
||||
const auto tileIndexFloat = parseFloat(tokens[6]);
|
||||
const auto r = parseFloat(tokens[7]);
|
||||
const auto g = parseFloat(tokens[8]);
|
||||
const auto b = parseFloat(tokens[9]);
|
||||
if (!x || !y || !columnsFloat || !rowsFloat || !tileIndexFloat || !r ||
|
||||
!g || !b) {
|
||||
return warnLine(path, lineNumber, "invalid tile_rect values");
|
||||
}
|
||||
|
||||
const int columns = static_cast<int>(*columnsFloat);
|
||||
const int rows = static_cast<int>(*rowsFloat);
|
||||
const int tileIndex = static_cast<int>(*tileIndexFloat);
|
||||
if (columns <= 0 || rows <= 0 || tileIndex < 0 ||
|
||||
std::floor(*columnsFloat) != *columnsFloat ||
|
||||
std::floor(*rowsFloat) != *rowsFloat ||
|
||||
std::floor(*tileIndexFloat) != *tileIndexFloat) {
|
||||
return warnLine(path, lineNumber,
|
||||
"tile_rect columns, rows and tile_index must be non-negative integers");
|
||||
}
|
||||
|
||||
const TileSetDefinition& definition = **tileSet;
|
||||
const frostbite2D::Color fallbackColor(*r, *g, *b, 1.0f);
|
||||
for (int row = 0; row < rows; ++row) {
|
||||
for (int column = 0; column < columns; ++column) {
|
||||
const frostbite2D::Rect bounds(
|
||||
*x + static_cast<float>(column) * definition.tileWidth,
|
||||
*y + static_cast<float>(row) * definition.tileHeight,
|
||||
definition.tileWidth, definition.tileHeight);
|
||||
const frostbite2D::Rect sourceRect(
|
||||
static_cast<float>(tileIndex) * definition.tileWidth, 0.0f,
|
||||
definition.tileWidth, definition.tileHeight);
|
||||
context.currentLayer->tiles.push_back(
|
||||
{definition.id, bounds, sourceRect, fallbackColor});
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (command == "rect_sprite" || command == "rect_sprite_src") {
|
||||
if (!context.currentLayer) {
|
||||
return warnLine(path, lineNumber,
|
||||
@@ -386,9 +472,14 @@ std::optional<LevelDefinition> LoadStageMap(const std::string& path) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
SDL_Log("StageMapLoader: loaded %s from %s (%zu platforms, %zu layers, %zu zones)",
|
||||
size_t tileCount = 0;
|
||||
for (const StageLayer& layer : level->layers) {
|
||||
tileCount += layer.tiles.size();
|
||||
}
|
||||
|
||||
SDL_Log("StageMapLoader: loaded %s from %s (%zu platforms, %zu layers, %zu tiles, %zu zones)",
|
||||
resource->path.c_str(), sourceName(resource->source),
|
||||
level->collision.platforms.size(), level->layers.size(),
|
||||
level->collision.platforms.size(), level->layers.size(), tileCount,
|
||||
level->battleZones.size());
|
||||
return level;
|
||||
}
|
||||
|
||||
@@ -38,6 +38,16 @@ float worldWidthOf(const LevelDefinition& level) {
|
||||
return level.collision.levelRight - level.collision.levelLeft;
|
||||
}
|
||||
|
||||
const TileSetDefinition* findTileSet(const LevelDefinition& level,
|
||||
const std::string& id) {
|
||||
for (const TileSetDefinition& tileSet : level.tileSets) {
|
||||
if (tileSet.id == id) {
|
||||
return &tileSet;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void StageRenderer::Render(const LevelDefinition& level,
|
||||
@@ -102,6 +112,13 @@ void StageRenderer::drawLayer(const LevelDefinition& level,
|
||||
frostbite2D::Color(0.91f, 0.95f, 0.98f, 1.0f));
|
||||
}
|
||||
}
|
||||
|
||||
for (const StageTile& tile : layer.tiles) {
|
||||
frostbite2D::Rect bounds(tile.bounds.left() + parallaxOffset.x,
|
||||
tile.bounds.top() + parallaxOffset.y,
|
||||
tile.bounds.width(), tile.bounds.height());
|
||||
drawStageTile(level, tile, bounds);
|
||||
}
|
||||
}
|
||||
|
||||
void StageRenderer::drawStageRect(const StageRect& rect,
|
||||
@@ -130,6 +147,26 @@ void StageRenderer::drawStageRect(const StageRect& rect,
|
||||
texture, rect.source.tint);
|
||||
}
|
||||
|
||||
void StageRenderer::drawStageTile(const LevelDefinition& level,
|
||||
const StageTile& tile,
|
||||
const frostbite2D::Rect& bounds) const {
|
||||
const TileSetDefinition* tileSet = findTileSet(level, tile.tileSetId);
|
||||
if (!tileSet || tileSet->texturePath.empty()) {
|
||||
drawRect(bounds, tile.fallbackColor);
|
||||
return;
|
||||
}
|
||||
|
||||
frostbite2D::Ptr<frostbite2D::Texture> texture =
|
||||
getTexture(tileSet->texturePath);
|
||||
if (!texture) {
|
||||
drawRect(bounds, tile.fallbackColor);
|
||||
return;
|
||||
}
|
||||
|
||||
drawTexture(bounds, tile.sourceRect, texture,
|
||||
frostbite2D::Color(1.0f, 1.0f, 1.0f, 1.0f));
|
||||
}
|
||||
|
||||
frostbite2D::Ptr<frostbite2D::Texture>
|
||||
StageRenderer::getTexture(const std::string& texturePath) const {
|
||||
const auto existing = textureCache_.find(texturePath);
|
||||
|
||||
@@ -20,6 +20,8 @@ private:
|
||||
void drawLayer(const LevelDefinition& level, const StageLayer& layer) const;
|
||||
void drawStageRect(const StageRect& rect,
|
||||
const frostbite2D::Rect& bounds) const;
|
||||
void drawStageTile(const LevelDefinition& level, const StageTile& tile,
|
||||
const frostbite2D::Rect& bounds) const;
|
||||
frostbite2D::Ptr<frostbite2D::Texture>
|
||||
getTexture(const std::string& texturePath) const;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user