You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于SDL2瓦片索引构建网格地图用于移动过滤的技术问询

基础游戏引擎网格地图工具开发问题与方向指引

我和同学在课程项目中开发一款基础游戏引擎,目前正实现用于移动过滤逻辑(基于SOLID、EMPTY等瓦片类型)调试的网格地图工具。我们使用Tiled编辑器导出瓦片集与地图信息的.csv和.json文件,已成功完成瓦片地图的加载、构建与渲染,并实现了瓦片索引的缓存。下一步需基于从Tiled获取的瓦片索引,加载、构建并渲染每个瓦片对应4×4的网格地图。

我梳理了以下逻辑,盼指正:

  • 动态确定用户/开发者可能使用的各类瓦片集的瓦片类型
  • 解析瓦片索引构建瓦片地图时,为每个瓦片生成4×4网格元素,记录每个网格元素关联的瓦片索引、在瓦片内的位置及类型
  • 生成用于覆盖在实际瓦片上的“实心”网格通用纹理(红边框、透明中心)
  • 将步骤3的纹理渲染在现有瓦片地图之上

核心问题

  • Tiled是否有未发现的设置可基于瓦片索引动态生成瓦片类型,以实现步骤1并支持任意瓦片集?若没有,是否只能手动梳理瓦片集并编写索引与类型的映射表(如30个唯一瓦片需手动配置)?
  • 关于步骤2,课堂上提到过通过解析瓦片像素、提取颜色占比判断网格区域是否可碰撞的示例,我认为这是糟糕的解决方案,盼确认该判断是否正确?
  • 渲染方面,若已将瓦片地图渲染到窗口表面,是否应使用SDL_RenderCopy绘制实心网格的边框?

瓦片地图类定义

#define MAX_WIDTH 192
#define MAX_HEIGHT 336

typedef unsigned short Dim;
typedef short Index;
typedef unsigned char byte;

typedef struct {
    Dim x, y;
} Tile;

class TileMap {
public:
    explicit TileMap(const std::string& mapPath);

    std::vector<std::string> tilesetPaths;
    std::vector<std::string> csvLayers;
    std::map <Index, std::pair <unsigned short, unsigned  short> > preCachedIndices;
    std::vector<Index> indices;

    unsigned short tilesetHeight{}, tilesetWidth{}; // number of tile rows/columns
    unsigned short tileHeight, tileWidth{};
    unsigned short mapWidth{}, mapHeight{};

    Tile GetTile(Dim row, Dim col);

    void WriteTextMap(const Json::Value& data, const std::string& fp);

    bool preCacheIndices(const std::string &inputMapPath);

    void renderMap(SDL_Window *window, SDL_Rect &cameraOffset);

    void printMapInfo();

    void initMapInfo(const std::string& path);
};

解析与缓存逻辑

bool TileMap::preCacheIndices(const std::string &inputMapPath) {
    std::ifstream file;
    std::string line;

    file.open(inputMapPath.c_str(), std::ios::in);

    if (!file) {
        std::cerr << "Error opening file: " << std::strerror(errno) << "\n";
        return false;
    }

    while (std::getline(file, line)) {
        std::stringstream ss(line);
        std::string item;

        while (std::getline(ss, item, ',')) {
            Index index = std::stoi(item);
            indices.push_back(index);

            // apply memoization to speed the process up (O(1) access time for already existing keys)
            if (preCachedIndices.find(index) == preCachedIndices.end()) { // index has not been cached yet
                unsigned short row = index / tilesetWidth;
                unsigned short col = index % tilesetWidth;
                std::pair<unsigned short, unsigned short> p = std::pair<unsigned short, unsigned short>(row, col);
                preCachedIndices.insert(std::pair<Index, std::pair<unsigned short, unsigned short>>{index, p});
            } else {
                continue;
            }
        }
    }

    file.close();

    return true;
}

瓦片地图渲染逻辑

void TileMap::renderMap(SDL_Window *window, SDL_Rect &cameraOffset) {
    SDL_Surface *screenSurface, *tileset;

    screenSurface = SDL_GetWindowSurface(window);
    if (!screenSurface) {
        printf("Could not initialize screen surface. Error message: %s", SDL_GetError());
    }

    tileset = IMG_Load(tilesetPaths.at(0).c_str()); // load tileset bitmap

    SDL_FillRect(screenSurface, nullptr, SDL_MapRGB(screenSurface->format, 0xFF, 0xFF, 0xFF));

    int row = 0;
    int col = 0;

    for (const auto &index: indices) {
        SDL_Rect src, dest;

        src.x = preCachedIndices.at(index).second * tileWidth;
        src.y = preCachedIndices.at(index).first * tileHeight;
        src.w = tileWidth;  //tileManager.tilemapWidth;
        src.h = tileHeight; //tileManager.tilemapHeight;

        dest.x = col * tileWidth + cameraOffset.x;
        dest.y = row * tileHeight - cameraOffset.y;
        dest.w = tileWidth;
        dest.h = tileHeight;

        SDL_BlitSurface(tileset, &src, screenSurface, &dest);

        if (col == mapWidth - 1) {
            col = 0;
            row++;
        } else {
            col++;
        }

    }

    SDL_FreeSurface(tileset);
    SDL_UpdateWindowSurface(window);
}

网格地图类定义

typedef enum {
    ThinAir,
    LeftSolid,
    RightSolid,
    TopSolid,
    BottomSolid,
    Ground,
    Floating,
} Masks;

typedef struct {
    int x, y;
} Position;

//Operator overloading so std::map can understand how to handle Position structs
inline bool operator<(Position const &a, Position const &b) {
    return std::tie(a.x, a.y) < std::tie(b.x, b.y);
}

typedef struct {
    Index index;
    Masks mask;
} GridTile;

using GridIndex = byte;

class GridMap {
private:
   TileMap *mapManager;
   std::map<Position, GridTile> gridTiles;
   unsigned short gridTileWidth, gridTileHeight;

public:
    explicit GridMap(TileMap *mapManager);

    void printGridTiles();

    void precacheGridTiles();

};

网格瓦片尺寸计算

gridTileHeight = mapManager->tileHeight / 4;
gridTileWidth = mapManager->tileWidth / 4;

网格地图预缓存逻辑(当前实现)

void GridMap::precacheGridTiles() {
    SDL_Surface *tileset;

    //todo: This should change for layering (at(0) part)
    tileset = IMG_Load(mapManager->tilesetPaths.at(0).c_str());

    Index i = 0;

    int row = 0, col = 0;
    for (const auto &index: mapManager->indices) {
        for (int j = 0; j < gridTileWidth * gridTileHeight; j++) {
            GridTile gridTile;
            gridTile.index = i;

            //todo: Find way to determine mask
            if(index == 61) {
               //todo: check based on current position if the tile is top,left,right,bottom side

            } else {
                gridTile.mask = ThinAir;
            }

            Position position;
            position.x = col + (j * gridTileWidth);
            position.y = row + (j * gridTileHeight);

            gridTiles[position] = gridTile;
        }

        if (col < mapManager->mapWidth) {
            col += gridTileWidth;
        } else {
            col = 0;
            row += gridTileHeight;
        }
    }
}

我们希望得到方向指引,而非直接的问题解决方案,感谢提供建议。

内容的提问来源于stack exchange,提问作者Stelios Papamichail

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.04 09:10:37