Hello,
I am following to the book SFML Game Development, by Artur Moreira and Artur Vogelius Jan Haller,
who's sources can be found on GitHub:
https://github.com/SFML/SFML-Game-Development-Book/tree/master/05_StatesI am at chapter 5, and trying to achieve the loading screen (classes LoadingState and ParallelTask).
From what I understood in the migration guide, I need to replace old sf::thread by std thread and old sf::mutex by std::mutex.
When the thread terminate, I am experiencing a crash without much informations beside "Abord has been called".
With my average C++ skills, I am not used to using threads, I might have done something wrong in the ParallelTask class or the LoadingState class.
Here are the versions I wrote:
ParallelTask class:
#include <thread>
#include <mutex>
#include <SFML/System/Clock.hpp>
class ParallelTask
{
private:
std::thread mThread;
bool mFinished;
sf::Clock mElapsedTime;
std::mutex mMutex;
private:
void runTask();
public:
ParallelTask();
void execute();
bool isFinished();
float getCompletion();
};
#include <SFML/System/Time.hpp>
#include "ParallelTask.h"
void ParallelTask::runTask()
{
// Dummy task - stall 10 seconds
bool ended = false;
while (!ended)
{
mMutex.lock(); // Protect the clock
if (mElapsedTime.getElapsedTime().asSeconds() >= 10.f)
ended = true;
mMutex.unlock();
}
{ // mFinished may be accessed from multiple threads, protect it
mMutex.lock();
mFinished = true;
mMutex.unlock();
}
}
ParallelTask::ParallelTask() :
mElapsedTime{},
mFinished{ false },
mMutex{},
mThread{}
{
}
void ParallelTask::execute()
{
mFinished = false;
mElapsedTime.restart();
mThread = std::thread(&ParallelTask::runTask, this);
}
bool ParallelTask::isFinished()
{
mMutex.lock();
bool isFinished{ mFinished };
mMutex.unlock();
return isFinished;
}
float ParallelTask::getCompletion()
{
mMutex.lock();
float elapsedTime{ mElapsedTime.getElapsedTime().asSeconds() / 10.f };
mMutex.unlock();
return elapsedTime;
}
LoadingState class:
#pragma once
#include <optional>
#include <SFML/System/Time.hpp>
#include <SFML/Window/Event.hpp>
#include <SFML/Graphics/Text.hpp>
#include <SFML/Graphics/RectangleShape.hpp>
#include "StateStack.h"
#include "State.h"
#include "ParallelTask.h"
class LoadingState :
public State
{
private:
sf::Text mLoadingText;
sf::RectangleShape mProgressBarBackground;
sf::RectangleShape mProgressBar;
ParallelTask mLoadingTask;
private:
void setCompletion(float percent);
public:
LoadingState(StateStack& stack, Context context);
void draw() override;
bool update(sf::Time dt) override;
bool handleEvent(std::optional<sf::Event> const& event) override;
};
#include <SFML/Graphics/RenderWindow.hpp>
#include "LoadingState.h"
#include "Fonts.h"
#include "Utilities.h"
void LoadingState::setCompletion(float percent)
{
if (percent > 1.f) // clamp
percent = 1.f;
mProgressBar.setSize(sf::Vector2f(mProgressBarBackground.getSize().x * percent, mProgressBar.getSize().y));
}
LoadingState::LoadingState(StateStack& stack, Context context) :
State{ stack, context },
mLoadingText{ context.fonts->get(Fonts::ID::Sansation), "Loading ressources"},
mProgressBarBackground{},
mProgressBar{},
mLoadingTask{}
{
sf::RenderWindow& window{ *getContext().window };
sf::Vector2f viewSize{ window.getView().getSize() };
mLoadingText.setCharacterSize(20);
centerOrigin(mLoadingText);
mLoadingText.setPosition(sf::Vector2f{ viewSize.x / 2.f, viewSize.y / 2.f + 50.f });
mProgressBarBackground.setFillColor(sf::Color::White);
mProgressBarBackground.setSize(sf::Vector2f{ viewSize.x - 20.f, 10.f });
mProgressBarBackground.setPosition(sf::Vector2f{ 10.f, mLoadingText.getPosition().y + 40.f });
mProgressBar.setFillColor(sf::Color(100, 100, 100));
mProgressBar.setSize(sf::Vector2f{ 200.f, 10.f });
mProgressBar.setPosition(sf::Vector2f{ 10.f, mLoadingText.getPosition().y + 40.f });
setCompletion(0.f);
mLoadingTask.execute();
}
void LoadingState::draw()
{
sf::RenderWindow& window{ *getContext().window };
window.setView(window.getDefaultView());
window.draw(mProgressBarBackground);
window.draw(mProgressBar);
window.draw(mLoadingText);
}
bool LoadingState::update(sf::Time dt)
{
if (mLoadingTask.isFinished())
{
requestStackPop();
requestStackPush(States::ID::Title);
}
else
setCompletion(mLoadingTask.getCompletion());
return true;
}
bool LoadingState::handleEvent(std::optional<sf::Event> const& event)
{
return true;
}
Any tought ?
Thanks for reply.