Welcome, Guest. Please login or register. Did you miss your activation email?

Author Topic: SFML GameDevelopment LoadingState  (Read 4086 times)

0 Members and 1 Guest are viewing this topic.

SFMLNewGuy

  • Jr. Member
  • **
  • Posts: 65
    • View Profile
SFML GameDevelopment LoadingState
« on: May 31, 2020, 08:15:30 pm »
Hello,

So I just had some help on a super barebone example on a loading state using a thread. I'm reading SFML Game Development still and I've come to a halt to try to understand how they incorporate the loading state so that it is actually reading the files instead of the mock loading they do.

I could just do what I learned in my previous thread, but I'd like to learn how to incorporate it using the book's design. The loading state is called when you click "Play" from the main menu. I'm just trying to understand when and where I would pass the fonts/texture data into loading and where?

Any help would be greatly appreciated. Here is the basics of what it offers.

#include "ParallelTask.h"


ParallelTask::ParallelTask()
: m_thread(&ParallelTask::runTask, this)
, m_finished(false)
{
}

void ParallelTask::execute()
{
        m_finished = false;
        m_elapsedTime.restart();
        m_thread.launch();
}

bool ParallelTask::isFinished()
{
        sf::Lock lock(m_mutex);
        return m_finished;
}

float ParallelTask::getCompletion()
{
        sf::Lock lock(m_mutex);

        // 100% at 10 seconds of elapsed time
        return m_elapsedTime.getElapsedTime().asSeconds() / 10.f;
}

void ParallelTask::runTask()
{
        // Dummy task - stall 10 seconds
        bool ended = false;
        while (!ended)
        {
                sf::Lock lock(m_mutex); // Protect the clock
                if (m_elapsedTime.getElapsedTime().asSeconds() >= 10.f)
                        ended = true;
        }

        { // mFinished may be accessed from multiple threads, protect it
                sf::Lock lock(m_mutex);
                m_finished = true;
        }      
}

#include "LoadingState.h"
#include "Utils/Helpers.h"
#include"ResourceHolder.h"

#include <SFML/Graphics/RenderWindow.hpp>
#include <SFML/Graphics/View.hpp>


LoadingState::LoadingState(StateStack& stack, Context context)
: State(stack, context)
{
        sf::RenderWindow& window = *getContext().window;
        sf::Font& font = context.fonts->get(Fonts::Sansation);
        sf::Vector2f viewSize = window.getView().getSize();

        m_loadingText.setFont(font);
        m_loadingText.setString("Loading Resources");
        Helpers::position::centerOrigin(m_loadingText);
        m_loadingText.setPosition(viewSize.x / 2.f, viewSize.y / 2.f + 50.f);

        m_progressBarBackground.setFillColor(sf::Color::White);
        m_progressBarBackground.setSize(sf::Vector2f(viewSize.x - 20, 10));
        m_progressBarBackground.setPosition(10, m_loadingText.getPosition().y + 40);

        m_progressBar.setFillColor(sf::Color(32,170,14));
        m_progressBar.setSize(sf::Vector2f(200, 10));
        m_progressBar.setPosition(10, m_loadingText.getPosition().y + 40);

        setCompletion(0.f);

        m_loadingTask.execute();
}


bool LoadingState::handleEvent(const sf::Event& event) {
        return true;
}

bool LoadingState::update(sf::Time) {
        // Update the progress bar from the remote task or finish it
        if (m_loadingTask.isFinished()) {
                requestStackPop();
                requestStackPush(States::Game);
        }
        else {
                setCompletion(m_loadingTask.getCompletion());
        }
        return true;
}

void LoadingState::draw()
{
        sf::RenderWindow& window = *getContext().window;

        window.setView(window.getDefaultView());

        window.draw(m_loadingText);
        window.draw(m_progressBarBackground);
        window.draw(m_progressBar);
}


void LoadingState::setCompletion(float percent)
{
        if (percent > 1.f) // clamp
                percent = 1.f;

        m_progressBar.setSize(sf::Vector2f(m_progressBarBackground.getSize().x * percent, m_progressBar.getSize().y));
}
 

The previous thread (as in my post) a fine gentleman helped me learned how to accomplish a loading screen like this (was a barebone main.cpp example)

THIS IS NOT PART OF THE BOOK CODE, BUT AN EXAMPLE BEFORE LOOKING AT BOOK CODE
// Still in main thread
std::vector<std::shared_ptr<Tile>> tilesVector;
tilesVector.reserve(Map_Height * Map_Width); // This creates the tiles with default constructor ( Tile() )

std::atomic<bool> done(false);

// Create thread to execute the bellow code while main thread continues normal execution
std::thread t([&done, &tilesVector] {
// Tile creation IS the heavy duty work
for(auto y = 0; y < Map_Height; ++y)
{
    for (auto x = 0; x < Map_Width; ++x)
    {
       Tile * tile = tilesVector[y * Map_Width + x].get(); // Get a pointer to tile to change its attributes
       tile->spr.setSize(TILE_SIZE);
       tile->spr.setFillColor({ 0,255,0,15 });
       tile->spr.setOutlineColor(sf::Color::White);
       tile->spr.setOutlineThickness(1.f);
       tile->spr.setPosition(x * TILE_SIZE.x, y * TILE_SIZE.y);
       tile->spr.setOrigin(TILE_SIZE.x / 2.f, TILE_SIZE.y / 2.f);

       if (y >= 240 && y <= 260 && x >= 240 && x <= 260)
       {
             tile->spr.setFillColor({ 200,25,22 });
       }
                               
       // tiles.emplace_back(std::move(tile)); This line no longer needed, we already have our tile vector
    }
}

// After building the tiles we set variable to true to signal that our work
// (tile creation) on this separate thread is done.
done = true;

});

Thank you, and have a nice day.
« Last Edit: May 31, 2020, 09:20:43 pm by SFMLNewGuy »

unlight

  • Newbie
  • *
  • Posts: 33
    • View Profile
    • Email
Re: SFML GameDevelopment LoadingState
« Reply #1 on: June 01, 2020, 02:18:50 am »
There are many, many different ways that you can acheive this. One really simple way to do this would be to have a vector of textures, sounds, etc as members of the Parallel Task class. The constructor might take in a vector of strings that represent the file paths to those resources. Then in your execute function, you iterate over the strings, load resources into their vectors. Once the thread is finished, you can get the resource vectors from the Parallel Task object via getter functions. As for the "getCompletion", that could return the percentage of resources that have been loaded.

One thing to consider though, is that your resource loading will be so fast that it wont be worth the overhead of creating a thread. But for educational purposes, it is still nice to explore.

What do you think of this solution? Can you see any potential problems? Can you see a better way of doing it that might be more generic?

SFMLNewGuy

  • Jr. Member
  • **
  • Posts: 65
    • View Profile
Re: SFML GameDevelopment LoadingState
« Reply #2 on: June 01, 2020, 12:01:32 pm »
Thanks for the response and information. I was thinking the same thing, but it just seems weird to load it all in there. I'll have to do something so it doesn't constantly try to reload it every time I leave and go back.

As for it loading so fast, it won't matter, I completely understand that. I was planning on loading like 500 textures to at least see if it's working. Like you said this is more for the learning purposes because there are a few books that go over this concept and I've always been so lost. Learning about threads from my last concept example opened my eyes.

Mastering SFML does a complete version of this, but of course, it's implemented totally differently than this book code. Which is something I struggle with because I learn how to do X concept, but have a difficult time applying it to say the next little engine I work with? So I'm trying to get better at that and understand it good enough to translate it anywhere.

Any other examples or actual source code implemented from the book are very welcome. In fact, I wish I could find someone who went through the book and added a lot more. I'll most likely add a vector of strings and using the resource manager load them all up in the loading state and run it in the execute command. Maybe put a bool to determine that it's all been loaded and doesn't need to be put in again unless the game has exited. Perhaps, even getting access to the Application class and calling the load function from there.

Wish me luck!