рисование карты тайлов с помощью sfml - PullRequest
1 голос
/ 12 марта 2012

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

Это моя проблема: допустим, у меня есть карта из 10 * 10 плиток с 5 слоями, которая суммирует до 10 * 10 * 5 = 500 плиток (конечно, в режиме реального времени с большей вероятностью будет в 10 раз больше плиток, что будет хуже) ... и он хранится в трехмерном массиве [layer] [row] [col] или символах, каждый символ представляет индекс на моем листе листов спрайтов, поэтому я создал класс 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;
}

Ответы [ 2 ]

4 голосов
/ 12 марта 2012

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

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

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

1 голос
/ 15 марта 2012

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

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

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

...