Sign in to follow this  

Level loading causing lag/stutter

This topic is 483 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

So I am loading a tiled map file using TinyXML but the only problem is, if I load the files in update the map is unplayable due to lag, yet if I wanted multiple levels, this is the only possible way to do it, I can't load the files in setup because it won't update to the new map. Any ideas on how to successfully do this?

 

Level.cpp

bool Level::LoadFromFile(std::string filename)
{
    TiXmlDocument levelFile(filename.c_str());

    if (!levelFile.LoadFile())
    {
        std::cout << "Loading level \"" << filename << "\" failed." << std::endl;
        return false;
    }

    //Map element. This is the root element for the whole file.
    TiXmlElement *map;
    map = levelFile.FirstChildElement("map");

    //Set up misc map properties.
    width = atoi(map->Attribute("width"));
    height = atoi(map->Attribute("height"));
    tileWidth = atoi(map->Attribute("tilewidth"));
    tileHeight = atoi(map->Attribute("tileheight"));

    //Tileset stuff
    TiXmlElement *tilesetElement;
    tilesetElement = map->FirstChildElement("tileset");
    firstTileID = atoi(tilesetElement->Attribute("firstgid"));

    //Tileset image
    TiXmlElement *image;
    image = tilesetElement->FirstChildElement("image");
    std::string imagepath = image->Attribute("source");

    if (!tilesetImage.LoadFromFile(imagepath))//Load the tileset image
    {
        std::cout << "Failed to load tile sheet." << std::endl;
        return false;
    }

    tilesetImage.CreateMaskFromColor(sf::Color(255, 0, 255));
    tilesetImage.SetSmooth(false);

    //Columns and rows (of tileset image)
    int columns = tilesetImage.GetWidth() / tileWidth;
    int rows = tilesetImage.GetHeight() / tileHeight;

    std::vector <sf::Rect<int> > subRects;//container of subrects (to divide the tilesheet image up)

    //tiles/subrects are counted from 0, left to right, top to bottom
    for (int y = 0; y < rows; y++)
    {
        for (int x = 0; x < columns; x++)
        {
            sf::Rect <int> rect;
            rect.Top = y * tileHeight;
            rect.Bottom = y * tileHeight + tileHeight;
            rect.Left = x * tileWidth;
            rect.Right = x * tileWidth + tileWidth;
            subRects.push_back(rect);
        }
    }

    //Layers
    TiXmlElement *layerElement;
    layerElement = map->FirstChildElement("layer");
    while (layerElement)
    {
        Layer layer;
        if (layerElement->Attribute("opacity") != NULL)//check if opacity attribute exists
        {
            float opacity = strtod(layerElement->Attribute("opacity"), NULL);//convert the (string) opacity element to float
            layer.opacity = 255 * opacity;
        }
        else
        {
            layer.opacity = 255;//if the attribute doesnt exist, default to full opacity
        }

        //Tiles
        TiXmlElement *layerDataElement;
        layerDataElement = layerElement->FirstChildElement("data");

        if (layerDataElement == NULL)
        {
            std::cout << "Bad map. No layer information found." << std::endl;
        }

        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;//Work out the subrect ID to 'chop up' the tilesheet image.
            if (subRectToUse >= 0)//we only need to (and only can) create a sprite/tile if there is one to display
            {
                sf::Sprite sprite;//sprite for the tile
                sprite.SetImage(tilesetImage);
                sprite.SetSubRect(subRects[subRectToUse]);
                sprite.SetPosition(x * tileWidth, y * tileHeight);

                sprite.SetColor(sf::Color(255, 255, 255, layer.opacity));//Set opacity of the tile.

                //add tile to layer
                layer.tiles.push_back(sprite);
            }

            tileElement = tileElement->NextSiblingElement("tile");

            //increment x, y
            x++;
            if (x >= width)//if x has "hit" the end (right) of the map, reset it to the start (left)
            {
                x = 0;
                y++;
                if (y >= height)
                {
                    y = 0;
                }
            }
        }

        layers.push_back(layer);

        layerElement = layerElement->NextSiblingElement("layer");
    }

    //Objects
    TiXmlElement *objectGroupElement;
    if (map->FirstChildElement("objectgroup") != NULL)//Check that there is atleast one object layer
    {
        objectGroupElement = map->FirstChildElement("objectgroup");
        while (objectGroupElement)//loop through object layers
        {
            TiXmlElement *objectElement;
            objectElement = objectGroupElement->FirstChildElement("object");
            while (objectElement)//loop through objects
            {
                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 = atoi(objectElement->Attribute("width"));
                int height = atoi(objectElement->Attribute("height"));

                Object object;
                object.name = objectName;
                object.type = objectType;

                sf::Rect <int> objectRect;
                objectRect.Top = y;
                objectRect.Left = x;
                objectRect.Bottom = y + height;
                objectRect.Right = x + width;

                if (objectType == "solid")
                {
                    solidObjects.push_back(objectRect);
                }

                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;
}
Edited by Elit3d

Share this post


Link to post
Share on other sites

Without more information on what kind of game it is, and how the level data is used, it's hard to answer, but I'll take a stab.

 

If you have to load levels on the fly (ie, you can't have a "loading" screen) where you're still actively controlling your character, then you can look at loading them in a separate worker thread.  However, you have to do this well before the user will need the new level data to ensure you don't try and display the level before it's completed loading (ie, the user is a screen away from needing the level data).  Once you've loaded the level, you'll want to notify the main thread it's completed loading and it can be used.

Share this post


Link to post
Share on other sites

Without more information on what kind of game it is, and how the level data is used, it's hard to answer, but I'll take a stab.

 

If you have to load levels on the fly (ie, you can't have a "loading" screen) where you're still actively controlling your character, then you can look at loading them in a separate worker thread.  However, you have to do this well before the user will need the new level data to ensure you don't try and display the level before it's completed loading (ie, the user is a screen away from needing the level data).  Once you've loaded the level, you'll want to notify the main thread it's completed loading and it can be used.

 

I'll try to be more specific, and yes most likely I will have a loading screen(I also tried to load the maps using the loading screen state but that doesn't allow my collision to work). So after level 1 is finished, I would like to then load level 2 but because the setup already calls level 1 to begin with I cant set the next level as its not updating, wouldn't I need to handle the level changing in the update rather than the setup function? However doing this method in the Update function lags my game as it is constantly loading the LoadFromFile(std::string filename) function over and over.

Share this post


Link to post
Share on other sites

This isn't SFML related, so SFML shouldn't be in the title of the thread. It's level-loading related, but more specifically, where you load your level.

 

You should not load your level every frame, you should only load your level when you reach a new level (unless you are streaming a large level, in which case the same logic applies at a different scale: You shouldn't load the same level segment every frame, you should only load a new level segment when you reach the new level segment).

 

Ignoring how you load your level, can you post the code that shows *where* you load you level? That is to say, can you post the entire function that calls "LoadFromFile()" and the entire function that calls that function, and the entire function that calls that function, all the way back to main()?

 

I need to see the complete code for the entire callstack of LoadFromFile(), because that's almost certainly where your problem is.

 

I heavily disagree that you should look into multithreading your program - that'd be way too much over-complication at your level.

Share this post


Link to post
Share on other sites

if I load the files in update the map is unplayable due to lag, yet if I wanted multiple levels, this is the only possible way to do it, I can't load the files in setup because it won't update to the new map.

If you can't load a new map in setup, then your setup is in the wrong place. Perhaps you need two setup stages, one at the start of the application for the shared data, and one for each time you load a level, just for the data relevant to that level.

Share this post


Link to post
Share on other sites

I'll try to be more specific, and yes most likely I will have a loading screen(I also tried to load the maps using the loading screen state but that doesn't allow my collision to work). So after level 1 is finished, I would like to then load level 2 but because the setup already calls level 1 to begin with I cant set the next level as its not updating, wouldn't I need to handle the level changing in the update rather than the setup function? However doing this method in the Update function lags my game as it is constantly loading the LoadFromFile(std::string filename) function over and over.

 

I was under the impression you were trying to load on the fly.  Since it's at an obvious stopping point, I don't understand why you can't move to a loading state, throw up a "loading level" screen and load everything you need right then.  When the level is loaded, you can go back to the playing state and start on the new level.  Multi-threading is definitely not needed in this situation.

Edited by BeerNutts

Share this post


Link to post
Share on other sites

Just wanted to feedback to this thread that I managed to fix my issue, thanks everyone for the help as it allowed me to tackle my problem from another angle. What I decided to do was to load my level file from the load screen state and then I changed how my collision worked. So now it all works without lag. Many thanks everyone for the help!

Share this post


Link to post
Share on other sites

This topic is 483 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this