Большие проблемы с загрузкой tmx-файлов

325
17 июля 2017, 16:01

Здравствуйте. Необходимо загрузить и отрисовать карту в tmx-формате. Пытаюсь сделать это с помощью tinyXML. Каждый раз при попытке загрузить из файла выкидывает одно и то же исключение. Как с этим бороться? Объясните пожалуйста подробно и просто как исправить?

код level.h

 #ifndef LEVEL_H
#define LEVEL_H
#pragma comment(lib,"sfml-graphics.lib")
#pragma comment(lib,"sfml-window.lib")
#pragma comment(lib,"sfml-system.lib")
#include <string>
#include <vector>
#include <map>
#include <SFML/Graphics.hpp>
struct Object
{
    int GetPropertyInt(std::string name);
    float GetPropertyFloat(std::string name);
    std::string GetPropertyString(std::string name);
    std::string name;
    std::string type;
    sf::Rect<int> rect;
    std::map<std::string, std::string> properties;
    sf::Sprite sprite;
}; 
struct Layer
{
    int opacity;
    std::vector<sf::Sprite> tiles;
};
class Level
{
public:
    bool LoadFromFile(std::string filename);
    Object GetObject(std::string name);
    std::vector<Object> GetObjects(std::string name);
    void Draw(sf::RenderWindow &window);
    sf::Vector2i GetTileSize();
private:
    int width, height, tileWidth, tileHeight;
    int firstTileID;
    sf::Rect<float> drawingBounds;
    sf::Texture tilesetImage;
    std::vector<Object> objects;
    std::vector<Layer> layers;
};
#endif

код level.cpp

#include "level.h"
#include <iostream>
#include "tinyxml.h"
int Object::GetPropertyInt(std::string name)
{
    return atoi(properties[name].c_str());
}
float Object::GetPropertyFloat(std::string name)
{
    return strtod(properties[name].c_str(), NULL);
}
std::string Object::GetPropertyString(std::string name)
{
    return properties[name];
}
bool Level::LoadFromFile(std::string filename)
{
    TiXmlDocument levelFile(filename.c_str());
    // Загружаем XML-карту
    if (!levelFile.LoadFile())
    {
        std::cout << "Loading level \"" << filename << "\" failed." << std::endl;
        return false;
    }
    // Работаем с контейнером map
    TiXmlElement *map;
    map = levelFile.FirstChildElement("map");
    // Пример карты: <map version="1.0" orientation="orthogonal"
    // width="10" height="10" tilewidth="34" tileheight="34">
    width = atoi(map->Attribute("width"));
    height = atoi(map->Attribute("height"));
    tileWidth = atoi(map->Attribute("tilewidth"));
    tileHeight = atoi(map->Attribute("tileheight"));
    // Берем описание тайлсета и идентификатор первого тайла
    TiXmlElement *tilesetElement;
    tilesetElement = map->FirstChildElement("tileset");
    firstTileID = atoi(tilesetElement->Attribute("firstgid"));
    // source - путь до картинки в контейнере image
    TiXmlElement *image;
    image = tilesetElement->FirstChildElement("image");
    std::string imagepath = image->Attribute("source");
    // Пытаемся загрузить тайлсет
    sf::Image img;
    if (!img.loadFromFile(imagepath))
    {
        std::cout << "Failed to load tile sheet." << std::endl;
        return false;
    }
    // Очищаем карту от света (109, 159, 185)
    // Вообще-то в тайлсете может быть фон любого цвета, но я не нашел решения, как 16-ричную строку
    // вроде "6d9fb9" преобразовать в цвет
    img.createMaskFromColor(sf::Color(109, 159, 185));
    // Грузим текстуру из изображения
    tilesetImage.loadFromImage(img);
    // Расплывчатость запрещена
    tilesetImage.setSmooth(false);
    // Получаем количество столбцов и строк тайлсета
    int columns = tilesetImage.getSize().x / tileWidth;
    int rows = tilesetImage.getSize().y / tileHeight;
    // Вектор из прямоугольников изображений (TextureRect)
    std::vector<sf::Rect<int>> subRects;
    for (int y = 0; y < rows; y++)
        for (int x = 0; x < columns; x++)
        {
            sf::Rect<int> rect;
            rect.top = y * tileHeight;
            rect.height = tileHeight;
            rect.left = x * tileWidth;
            rect.width = tileWidth;
            subRects.push_back(rect);
        }
    // Работа со слоями
    TiXmlElement *layerElement;
    layerElement = map->FirstChildElement("layer");
    while (layerElement)
    {
        Layer layer;
        // Если присутствует opacity, то задаем прозрачность слоя, иначе он полностью непрозрачен
        if (layerElement->Attribute("opacity") != NULL)
        {
            float opacity = strtod(layerElement->Attribute("opacity"), NULL);
            layer.opacity = 255 * opacity;
        }
        else
        {
            layer.opacity = 255;
        }
        // Контейнер <data>
        TiXmlElement *layerDataElement;
        layerDataElement = layerElement->FirstChildElement("data");
        if (layerDataElement == NULL)
        {
            std::cout << "Bad map. No layer information found." << std::endl;
        }
        // Контейнер <tile> - описание тайлов каждого слоя
        TiXmlElement *tileElement;
        tileElement = layerDataElement->FirstChildElement("tile");
        if (tileElement == NULL)
        {
            std::cout << "Bad map. No tile information found." << std::endl;
            return false;
        }
        int x = 0;
        int y = 0;
        while (tileElement)
        {
            int tileGID = atoi(tileElement->Attribute("gid"));
            int subRectToUse = tileGID - firstTileID;
            // Устанавливаем TextureRect каждого тайла
            if (subRectToUse >= 0)
            {
                sf::Sprite sprite;
                sprite.setTexture(tilesetImage);
                sprite.setTextureRect(subRects[subRectToUse]);
                sprite.setPosition(x * tileWidth, y * tileHeight);
                sprite.setColor(sf::Color(255, 255, 255, layer.opacity));
                layer.tiles.push_back(sprite);
            }
            tileElement = tileElement->NextSiblingElement("tile");
            x++;
            if (x >= width)
            {
                x = 0;
                y++;
                if (y >= height)
                    y = 0;
            }
        }
        layers.push_back(layer);
        layerElement = layerElement->NextSiblingElement("layer");
    }
    // Работа с объектами
    TiXmlElement *objectGroupElement;
    // Если есть слои объектов
    if (map->FirstChildElement("objectgroup") != NULL)
    {
        objectGroupElement = map->FirstChildElement("objectgroup");
        while (objectGroupElement)
        {
            // Контейнер <object>
            TiXmlElement *objectElement;
            objectElement = objectGroupElement->FirstChildElement("object");
            while (objectElement)
            {
                // Получаем все данные - тип, имя, позиция, etc
                std::string objectType;
                if (objectElement->Attribute("type") != NULL)
                {
                    objectType = objectElement->Attribute("type");
                }
                std::string objectName;
                if (objectElement->Attribute("name") != NULL)
                {
                    objectName = objectElement->Attribute("name");
                }
                int x = atoi(objectElement->Attribute("x"));
                int y = atoi(objectElement->Attribute("y"));
                int width, height;
                sf::Sprite sprite;
                sprite.setTexture(tilesetImage);
                sprite.setTextureRect(sf::Rect<int>(0, 0, 0, 0));
                sprite.setPosition(x, y);
                if (objectElement->Attribute("width") != NULL)
                {
                    width = atoi(objectElement->Attribute("width"));
                    height = atoi(objectElement->Attribute("height"));
                }
                else
                {
                    width = subRects[atoi(objectElement->Attribute("gid")) - firstTileID].width;
                    height = subRects[atoi(objectElement->Attribute("gid")) - firstTileID].height;
                    sprite.setTextureRect(subRects[atoi(objectElement->Attribute("gid")) - firstTileID]);
                }
                // Экземпляр объекта
                Object object;
                object.name = objectName;
                object.type = objectType;
                object.sprite = sprite;
                sf::Rect <int> objectRect;
                objectRect.top = y;
                objectRect.left = x;
                objectRect.height = height;
                objectRect.width = width;
                object.rect = objectRect;
                // "Переменные" объекта
                TiXmlElement *properties;
                properties = objectElement->FirstChildElement("properties");
                if (properties != NULL)
                {
                    TiXmlElement *prop;
                    prop = properties->FirstChildElement("property");
                    if (prop != NULL)
                    {
                        while (prop)
                        {
                            std::string propertyName = prop->Attribute("name");
                            std::string propertyValue = prop->Attribute("value");
                            object.properties[propertyName] = propertyValue;
                            prop = prop->NextSiblingElement("property");
                        }
                    }
                }
                // Пихаем объект в вектор
                objects.push_back(object);
                objectElement = objectElement->NextSiblingElement("object");
            }
            objectGroupElement = objectGroupElement->NextSiblingElement("objectgroup");
        }
    }
    else
    {
        std::cout << "No object layers found..." << std::endl;
    }
    return true;
}
Object Level::GetObject(std::string name)
{
    // Только первый объект с заданным именем
    for (int i = 0; i < objects.size(); i++)
        if (objects[i].name == name)
            return objects[i];
}
std::vector<Object> Level::GetObjects(std::string name)
{
    // Все объекты с заданным именем
    std::vector<Object> vec;
    for (int i = 0; i < objects.size(); i++)
        if (objects[i].name == name)
            vec.push_back(objects[i]);
    return vec;
}
sf::Vector2i Level::GetTileSize()
{
    return sf::Vector2i(tileWidth, tileHeight);
}
void Level::Draw(sf::RenderWindow &window)
{
    // Рисуем все тайлы (объекты НЕ рисуем!)
    for (int layer = 0; layer < layers.size(); layer++)
        for (int tile = 0; tile < layers[layer].tiles.size(); tile++)
            window.draw(layers[layer].tiles[tile]);
}

Код main:

#include <SFML/Graphics.hpp>
#include "main_header.hpp"
#include <list>
#include "level.h"
int main() {
    Level lvl;
    lvl.LoadFromFile("11.tmx");
    sf::RenderWindow window(sf::VideoMode(640, 480), "SummerSchool");




                                        /*Main cicle*/
    while (window.isOpen())
    {
        sf::Event event;
        while (window.pollEvent(event))
        {
            if (event.type == sf::Event::Closed)
            {
                window.close();
                exit(0);
            }
        }
        window.clear();
        lvl.Draw(window);
        window.display();

    }
}

Пожалуйста помогите, все изгуглил и не понимаю в чем дело.

Answer 1

У вас же открыт отладчик. Нужно было просто пройти по стеку и посмотреть, где в вашем коде происходит ошибка. Судя по скриншоту, в xml отсутствует элемент "image", и вы обращаетесь по указателю на этот элемент без проверки:

TiXmlElement *image;
image = tilesetElement->FirstChildElement("image"); // Что, если вернёт nullptr?
std::string imagepath = image->Attribute("source"); // Тут тоже может быть nullptr, кстати. std::string не может быть инициализирована нулевым указателем.
READ ALSO
выброс исключения без имени

выброс исключения без имени

Почему не отлавливается исключение?

289
Написание программы под Linux (Orange Pi)

Написание программы под Linux (Orange Pi)

Добрый деньНеобходимо написать программу, которая будет запускаться в Debian на устройстве Orange Pi

371
Плагин JQuery для события pan

Плагин JQuery для события pan

Приветствую! Подскажите пожалуйста, какой плагин нужен для JQuery события Pan-х (перетаскивание объекта в горизонтальной плоскости) и как его...

199