使用sfml绘制平铺地图

drawing a tile map with sfml

本文关键字:地图 绘制 sfml 使用      更新时间:2023-10-16

所以,我一直在尝试创建一个"Map"类来处理在窗口上绘制平铺贴图,但我在将所有平铺绘制到屏幕上时遇到了问题。

这就是我的问题:假设我有一个10*10瓷砖的地图,有5层,总计为10*10*5=500瓷砖(当然,在实时情况下,可能会有10倍多的瓷砖,情况会更糟)。。。它存储在一个3d数组[layer][row][col]或chars中,每个chars代表我的sprite瓦片表上的一个索引所以我创建了一个"SpriteSheet"类来处理我的精灵表,并通过索引spriteSheet->GetSubSprite(idx)从表中获取每个精灵。所以最后我有一个类似Draw(spriteSheet->GetSubSprite((int)mapArray[i][j][k]))的东西,应该为每个精灵、每个游戏循环调用它,所以假设我的游戏以60帧/秒的速度运行,我有500个瓦片要画:游戏的目标是每秒画500*60=30000(!)个瓦片。。。

正如你所看到的,我在这里有点问题。。。?有人知道如何在速度方面充分提高这一点吗?(当然,对我目前的结构进行任何形式的改进都是非常幸运的)。

所以,以防这是我的SpriteSheet.cppMap.cpp文件,我知道我有很多设计错误,但请关注我的问题,设计还远未完成。


#include "Map.h"
sz::Map::Map(std::string path, std::string name, std::string image) : Tmx::Map() {
    ParseFile(path);
    if (HasError()) {
        printf("error code: %dn", GetErrorCode());
        printf("error text: %sn", GetErrorText().c_str());
        system("PAUSE");
    } 
    else {
        mapArray = new unsigned char**[GetNumLayers()];
        for(int i = 0; i < GetNumLayers(); i++) {
            mapArray[i] = new unsigned char*[GetLayer(i)->GetHeight()];
            for(int j=0; j<GetLayer(i)->GetHeight(); j++) {
                mapArray[i][j] = new unsigned char[GetLayer(i)->GetWidth()];
                // [layer][row][col]
            }
        }
        //load the array
        for (int i = 0; i < GetNumLayers(); ++i) {
            // Get a layer.
            const Tmx::Layer *layer = GetLayer(i);
            for (int y = 0; y < layer->GetHeight(); ++y) {
                for (int x = 0; x < layer->GetWidth(); ++x) {
                    // Get a tile global id.
                    mapArray[i][x][y] = (char)layer->GetTileGid(x, y);
                    //printf("%3d", mapArray[i][x][y]);
                    /* need to fix later on
                    /****************************
                    // Find a tileset for that id.
                    const Tmx::Tileset *tileset = FindTileset(layer->GetTileGid(x, y));
                    if (layer->IsTileFlippedHorizontally(x, y)){
                        printf("h");
                    }else{
                        printf(" ");
                    }
                    if (layer->IsTileFlippedVertically(x, y)){
                        printf("v");
                    }else{
                        printf(" ");
                    }
                    if (layer->IsTileFlippedDiagonally(x, y)){
                        printf("d ");
                    } else {
                        printf("  ");
                    }
                    ****************************/
                }
                //printf("n");
            }
            //printf("nn");
        }
    }
    tiles = new sz::SpriteSheet(name, image, 33, 33);
}
void sz::Map::Draw(sf::RenderWindow *rw) {
    // dont know what to do T_T
}

#include "SpriteSheet.h"
sz::SpriteSheet::SpriteSheet(std::string name, std::string path, int tileW, int tileH) : sz::GameObject(name, path) {
    this->tileH = tileH;
    this->tileW = tileW;
    numOfTiles = ((this->GetImage()->GetHeight()) / tileH) * ((this->GetImage()->GetWidth()) / tileW);
}
int sz::SpriteSheet::GetTileWidth() { return tileW; }
int sz::SpriteSheet::GetTileHeight() { return tileH; }
int sz::SpriteSheet::GetNumOfTiles() { return numOfTiles; }
sf::Sprite sz::SpriteSheet::GetSubSprite(int idx) {
    if(idx < 1 || idx > numOfTiles) {
        std::cout << "Incorrect index!" << std::endl;
        // need return value
    }
    int row=0, col=0, tilesEachRow = (GetImage()->GetWidth() / tileW);
    while(idx > tilesEachRow) {
        idx -= tilesEachRow;
        col++;
    }
    row = idx-1;
    sz::GameObject *sub = new sz::GameObject(name, path);
    /*std::cout << "tileW: " << tileW << std::endl;
    std::cout << "tileH: " << tileH << std::endl;
    std::cout << "row: " << row << std::endl;
    std::cout << "col: " << col << std::endl;
    std::cout << "tiles per row: " << tilesEachRow << std::endl;
    std::cout << "(" << row*tileW << ", " << col*tileH << ", " << (row+1)*tileW << ", " << (col+1)*tileH << ")" << std::endl;*/
    sub->SetSubRect(sf::IntRect(row*tileW, col*tileH, (row+1)*tileW, (col+1)*tileH));
    return *sub;
}

您真的遇到速度问题了吗?虽然每秒30000个瓦片听起来可能很多,但就使用更现代的PC进行计算而言,这可能不是什么大问题。

话虽如此,如果输出一个瓦片有一定的开销,我肯定能想出一种方法来优化你所拥有的(例如,2个瓦片比1个瓦片的输出时间更长,1个瓦片是两个瓦片的大小之和)。我声称对SFML了解不多,但我认为有某种表面/画布系统。你可以写一个方法,一次将静态瓷砖绘制到曲面上。然后在游戏的每一帧中输出一次该曲面。这意味着不需要以逐块的方式在每帧重复输出块。您只需输出所有静态平铺组合的单个图像。

我想你甚至可以动态地使用这个系统。如果瓷砖需要更改,只需使用更新的瓷砖数据覆盖此曲面的相关部分即可。换言之,您将所有静态的东西都放在一边,只针对需要更新的区域。

我同意Alex Z的观点,即使用现代/主流硬件渲染5个屏幕的瓷砖应该不会太慢。然而,唯一确定的方法是测试它

此外,你需要意识到,即使你的地图有1000x1000个瓦片,你每帧也只能渲染10x10个瓦片,因为其余的瓦片都在屏幕之外。

如果你想让你的当前结构更加优化,你应该把你的精灵表中的每个精灵存储为数组中的单独精灵。这样,您就不需要每次访问单个磁贴时都调用SetSubRect。