基于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
相关产品推荐
相关产品推荐

