рисование тайловой карты с помощью sfml

Итак, я пытался создать класс «Карта» для обработки рисования моих карт тайлов в окне, но у меня возникла проблема с рисованием всех тайлов на экране.

Это моя проблема: скажем, у меня есть карта 10 * 10 плиток с 5 слоями, что в сумме дает 10 * 10 * 5 = 500 плиток (конечно, в реальном времени будет в 10 раз больше плиток, что было бы хуже )... и он хранится в трехмерном массиве [layer][row][col] или chars, каждый char представляет индекс на моем листе спрайтов плиток, поэтому я создал класс SpriteSheet для обработки моих листов спрайтов и получения каждый спрайт с листа по индексу: spriteSheet->GetSubSprite(idx). так что в итоге у меня есть что-то вроде Draw(spriteSheet->GetSubSprite((int)mapArray[i][j][k])), которое должно вызываться для каждого спрайта, КАЖДЫЙ ЦИКЛ ИГРЫ, поэтому скажем, моя игра работает со скоростью 60 кадров в секунду, и мне нужно отрисовать 500 плиток: игра стремится отрисовать 500 * 60 = 30 000 (!) тайлы каждую секунду...

так что, как вы можете видеть, у меня есть небольшая проблема здесь ...? кто-нибудь знает, как достаточно ускорить это? (и, конечно, любое улучшение моей нынешней структуры было бы очень кстати).

Итак, на всякий случай вот мои файлы SpriteSheet.cpp и мои Map.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: %d\n", GetErrorCode());
        printf("error text: %s\n", 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("\n\n");
        }
    }
    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;
}

person Slava Zoref    schedule 11.03.2012    source источник


Ответы (2)


Вы действительно испытываете какие-либо проблемы со скоростью? Хотя 30 000 фрагментов в секунду могут показаться большим количеством, с точки зрения вычислений на более современном ПК это может не быть большой проблемой.

Сказав это, я определенно могу придумать способ оптимизировать то, что у вас есть, если вывод одной плитки связан с определенными накладными расходами (например, 2 плитки занимают больше времени для вывода, чем 1 плитка, которая представляет собой размер обеих плиток вместе взятых). Я не утверждаю, что много знаю о SFML, но я предполагаю, что существует какая-то система поверхностей/холстов. Что вы могли бы сделать, так это написать метод, который рисует статические плитки на поверхности один раз. Затем выводите эту поверхность один раз в каждом кадре игры. Это означает, что нет необходимости повторно выводить тайлы каждый кадр в режиме тайл за тайлом. Вы просто выводите одно изображение всех статических плиток вместе взятых.

Я думаю, вы могли бы даже использовать эту систему динамически. Если плитку необходимо изменить, просто перезапишите соответствующую часть этой поверхности обновленными данными плитки. Другими словами, вы оставляете все статичное и выбираете только те области, которые требуют обновления.

person Alex Z    schedule 12.03.2012

Я согласен с Alex Z в том, что с современным/основным аппаратным обеспечением рендеринг тайлов на 5 экранов не должен быть слишком медленным. Однако единственный способ узнать наверняка — это проверить.

Также вы должны понимать, что даже если ваша карта имеет тайлы 1000x1000, вы будете рендерить только тайлы размером 10x10 на кадр, потому что остальные тайлы находятся за пределами экрана.

Если вы хотите сделать вашу текущую структуру более оптимизированной, вы должны хранить каждый из спрайтов на листе спрайтов как отдельные спрайты в массиве. Таким образом, вам не нужно будет вызывать SetSubRect каждый раз, когда вам нужно получить доступ к одной плитке.

person priomsrb    schedule 14.03.2012