Is it possible to make SFML 1.6 handle end of music by itself? Currently I have this:
//in music.cpp
music.Play()
//in main.cpp
//on every frame check for end of music
if(music.getStatus() == Sound::Stopped)
loadNextMusicFile();
Isn't there a way in SFML to just say, "Play until music stopped, then load the next," without implementing this yourself? Or at least a more "elegant" way of noticing when the music stopped (like an OnStopped event)?
If you look at the code from Music.cpp
bool Music::OnGetData(SoundStream::Chunk& data)
{
Lock lock(myMutex);
// Fill the chunk parameters
data.Samples = &mySamples[0];
data.NbSamples = myFile->Read(&mySamples[0], mySamples.size());
// Check if we have reached the end of the audio file
return data.NbSamples == mySamples.size();
}
You see that it will return false when its at the end of the file.
So what you want to do is subclass sf::Music. e.g.
class MyMusic : public sf::Music
{
bool OnGetData(SoundStream::Chunk& data)
{
bool running = sf::Music::OnGetData(data);
if(!running)
OnMusicEnd();
return running;
}
public:
void OnMusicEnd()
{
// ...
}
};
Related
I'm writing a game where player picks coins. Picking a coin there is supposed to be a sound, I use PlaySound to play *.wav file. It works. But sometimes (7/10 attempts) app just crushes. I have no idea why sometimes is works and sometimes it doesn't.
The interesting thing is when I run the app in debug mode it works perfectly
What can cause the problem and how can it be fixed?
And also can you recommend another way to play wav? (As simple as that (Using one-two functions))
I've tried to use PlaySoundA, sndPlaySound all of the variations - the same result
tried to read wav directly from file and save it in memory - the same result
//This sample checks if there a coin on the right
void Player::step_right() {
if (check_right()) {
sndPlaySound(TEXT("D:\\projects\\s.wav"), SND_FILENAME | SND_ASYNC);
//PlaySoundA(TEXT("D:\\projects\\s.wav"), nullptr, SND_FILENAME | SND_ASYNC);
//sndPlaySound(buffer, SND_MEMORY || SND_ASYNC);
netWorth++;
steps+=10;
}
cur->X++;
}
upd:
I use MinGW
Console output after crush: Process finished with exit code -1073741819 (0xC0000005)
Here is the Player class specification:
class Player {
private:
Point* cur; // Current coords
Point post; // Last coords
int steps; // Remaining steps
int netWorth; // Picked coins
void (Player::*m[4])(); // Array of movement functions pointers
// Movement
void step_right();
void step_left();
void step_up();
void step_down();
// Coins checking
bool check_right();
bool check_left();
bool check_up();
bool check_down();
public:
Player();
Point* Getcur() { return cur; }
void Move(void (Player::*t)());
bool isOutofSteps();
auto ReturnSomeArray() { return m; }
int GetNet(); //Networth
void display_player_info();
};
I also have Action_Listener class which has
map<int, void (Player::*)()> m;
Depending on a keycode I send a function pointer to void Player::Move(void (Player::*t)())
here:
if (m.find(event) != m.end() && !player->isOutofSteps()) {
void (Player::*t)() = m[event];
player->Move(t);
unsigned int (Drawer::*y)() = drawhandlers[event];
drawer->Draw_Hero(y);
player->display_player_info();
} else if(player->isOutofSteps()){
terminal_print(2, 2, "You're out of steps");
return;
}
Move function:
void Player ::Move(void (Player::*t)()) {
post = *cur;
(this->*t)();
steps--;
}
I'm working on a new project and an implementing a basic scene change. I have the different scenes setup as their own classes, with the intialisation function being used to create and reposition different SFML objects. I saw this answer and have written my scene switcher similarly:
// Create scene monitoring variable
int scene[2];
scene[0] = 0; // Set current scene to menu
scene[1] = 0; // Set scene change to no
...
// Check for scene change
if(scene[1] == 0) {
// Run tick function based on current scene
switch(scene[0]) {
case 0:
// Main menu - run tick function
menu.tick();
}
}
if(scene[1] == 1) {
// Reset scene that you've changed to
switch(scene[0]) {
case 0:
// Main menu - reset it
menu = Menu(window, scene); // <-- Reinitialise menu here
}
// Set change variable to 0
scene[1] = 0;
}
You can see the full code on the github repository.
However, this doesn't seem to work properly - as soon as a scene change is made, the screen goes blank. The class is reintialised (I added a cout to check), the draw function is still run and mouse clicks are still processed, yet nothing appears in the window.
Am I doing something wrong here?
Doing things that way can lead into leak memory errors. I suggest you a different approach: the StateStack
How this works?
The basics of having a StateStack object is store each possible state of your game/app into a stack. This way, you can process each one in the stack order.
What is an State?
An State is something that can be updated, drawn and handle events. We can make an interface or an abstract class to make our screens behave like a State.
Which are the advantages?
With a stack structure, you can easily control how your different scenes are going to handle the three different processing methods. For instance. If you have a mouse click while you're in a pause menu, you won't that click event to reach the menu state or the "game" state. To achieve this, the solution is really easy, simply return false in your handleEvent method if you don't want the event go further this particular state. Note that this idea is also expandable to draw or update methods. In your pause menu, you won't update your "game" state. In your "game" state you won't draw tour menu state.
Example
With this points in mind, this is one possible way of implementation. First, the State interface:
class State{
public:
virtual bool update() = 0;
virtual bool draw(sf::RenderTarget& target) const = 0;
// We will use a vector instead a stack because we can iterate vectors (for drawing, update, etc)
virtual bool handleEvent(sf::Event e, std::vector<State*> &stack) = 0;
};
Following this interface we can have a example MenuState and PauseState:
MenuState
class MenuState : public State{
public:
MenuState(){
m_count = 0;
m_font.loadFromFile("Roboto-Regular.ttf");
m_text.setFont(m_font);
m_text.setString("MenuState: " + std::to_string(m_count));
m_text.setPosition(10, 10);
m_text.setFillColor(sf::Color::White);
}
virtual bool update() {
m_count++;
m_text.setString("MenuState: " + std::to_string(m_count));
return true;
}
virtual bool draw(sf::RenderTarget &target) const{
target.draw(m_text);
return true;
}
virtual bool handleEvent(sf::Event e, std::vector<State*> &stack){
if (e.type == sf::Event::KeyPressed){
if (e.key.code == sf::Keyboard::P){
stack.push_back(new PauseState());
return true;
}
}
return true;
}
private:
sf::Font m_font;
sf::Text m_text;
unsigned int m_count;
};
PauseState
class PauseState : public State{
public:
PauseState(){
sf::Font f;
m_font.loadFromFile("Roboto-Regular.ttf");
m_text.setFont(m_font);
m_text.setString("PauseState");
m_text.setPosition(10, 10);
m_text.setFillColor(sf::Color::White);
}
virtual bool update() {
// By returning false, we prevent States UNDER Pause to update too
return false;
}
virtual bool draw(sf::RenderTarget &target) const{
target.draw(m_text);
// By returning false, we prevent States UNDER Pause to draw too
return false;
}
virtual bool handleEvent(sf::Event e, std::vector<State*> &stack){
if (e.type == sf::Event::KeyPressed){
if (e.key.code == sf::Keyboard::Escape){
stack.pop_back();
return true;
}
}
return false;
}
private:
sf::Font m_font;
sf::Text m_text;
};
By the way, while I was doing this, I notice that you must have the fonts as an attribute of the class in order to keep the reference. If not, when your text is drawn, its font is lost ant then it fails. Another way to face this is using a resource holder, which is much more efficient and robust.
Said this, our main will look like:
Main
int main() {
// Create window object
sf::RenderWindow window(sf::VideoMode(720, 720), "OpenTMS");
// Set window frame rate
window.setFramerateLimit(60);
std::vector<State*> stack;
// Create menu
stack.push_back(new MenuState());
// Main window loops
while (window.isOpen()) {
// Create events object
sf::Event event;
// Loop through events
while (window.pollEvent(event)) {
// Close window
if (event.type == sf::Event::Closed) {
window.close();
}
handleEventStack(event, stack);
}
updateStack(stack);
// Clear window
window.clear(sf::Color::Black);
drawStack(window, stack);
// Display window contents
window.display();
}
return 0;
}
The stack functions are simple for-loop but, with the detail that iterate the vector backwards. This is the way to imitate that stack behavior, starting from top (size-1 index) and ending at 0.
Stack functions
void handleEventStack(sf::Event e, std::vector<State*> &stack){
for (int i = stack.size()-1; i >=0; --i){
if (!stack[i]->handleEvent(e, stack)){
break;
}
}
}
void updateStack(std::vector<State*> &stack){
for (int i = stack.size() - 1; i >= 0; --i){
if (!stack[i]->update()){
break;
}
}
}
void drawStack(sf::RenderTarget &target, std::vector<State*> &stack){
for (int i = stack.size() - 1; i >= 0; --i){
if (!stack[i]->draw(target)){
break;
}
}
}
You can learn more about StateStacks and gamedev in general with this book
My question is:
I am trying to implement basic state management in my project and i stuck at changing states.
I have all my states in std::stack<State*> container, and push/pop them directly from Application class or from State class.
Problem is when i change current state from State class, it can be destroyed before render method called, whitch results in exeption. So how do i avoid this?
PS sorry for my english and please say me if something in my problem/code isn clear
Application class:
void Application::pushState(State* state)
{
this->m_states.push(state);
this->m_states.top()->open();//enter state
}
void Application::popState()
{
if (!this->m_states.empty())
{
this->m_states.top()->close();//leave state
delete this->m_states.top();
}
if (!this->m_states.empty())
this->m_states.pop();
}
void Application::changeState(State* state)
{
if (!this->m_states.empty())
popState();
pushState(state);
}
State* Application::peekState()
{
if (this->m_states.empty()) return nullptr;
return this->m_states.top();
}
void Application::mainLoop()
{
sf::Clock clock;
while (this->m_window.isOpen())
{
sf::Time elapsed = clock.restart();
float delta = elapsed.asSeconds();
if (this->peekState() == nullptr)
this->m_window.close();
this->peekState()->update(delta)//if i change state in State.update(), it may be that code below will now point to not existing state
if (this->peekState() == nullptr)
this->m_window.close();
this->peekState()->render(delta);
}
}
State class:
void EditorState::update(const float delta)
{
sf::Event event;
while (this->m_application->m_window.pollEvent(event))
{
if (event.type == sf::Event::Closed)
{
this->m_application->popState();
return;
}
}
}
Okay maybe this is not really a problem, but something like "how to" question. As you can see in my code, i update and render states in mainLoop() method. What im tying to figure out is how to manage those updates, asuming that state can be changed from state itself, not only from stateManager (in my case Application class)
Ok, so I'm guessing this is for a game (but it doesn't have to be). Instead of doing what you're doing for switching between states, I use an enum.
enum class GameState {
MENU, PLAY, PAUSE
}
Then, in your main header
GameState m_gameState = GameState::MENU;
In your main loop, you can check what the current state is by simply doing
if (m_gameState == GameState::MENU)
{
...
}
or you can use a switch statement
switch (m_gameState)
{
case GameState::MENU:
...
break;
case GameState::PLAY:
...
break;
case GameState::PAUSE:
...
break;
}
And, if you ever want to switch the state, you can just do
m_gameState = GameState::PAUSE;
Hope this answered your question :D
If not, I must have misunderstood (sorry).
I'm currently stuck on some issue and don't know how to fix it.
I started working on a simple 2D engine using SFML for rendering stuff and Lua as my scripting language.
The engine starts with a splash screen first before Lua code is loaded ...
My problem is I don't know how to write a "good" draw loop for my Lua objects.
Maybe you'll understand when we take a look on my code:
Main.cpp:
...
int draw_stuff()
{
sf::RenderWindow window(sf::VideoMode(640, 480), TITLE);
while (window.isOpen()) {
sf::Event event;
while (window.pollEvent(event))
{
if (event.type == sf::Event::Closed)
window.close();
}
window.clear();
if (!success) {
window.draw(sp_splash_screen);
}
if (clock.getElapsedTime().asSeconds() > 2) {
for (static bool first = true; first; first = false)
{
std::thread thr(&lua_module);
thr.detach();
success = true;
}
}
for (sf::CircleShape obj : CircleDrawList) {
window.draw(obj);
//window.draw(CircleDrawList.front());
}
}
return 0;
}
My CircleShape wrapper class for Lua:
/////shapes.h
extern std::list<sf::CircleShape> CircleDrawList;
class Circle
{
public:
Circle();
...
void draw();
private:
sf::CircleShape circleShape;
protected:
//~Circle();();
};
/////shapes.cpp
std::list<sf::CircleShape> CircleDrawList;
...
void Circle::draw()
{
//std::cout << "DRAW IN LIST" << std::endl;
CircleDrawList.push_front(circleShape);
//if (drawableList.size() == 4)
//drawableList.pop_front();
}
...
int luaopen_shapes(lua_State *L)
{
luabridge::getGlobalNamespace(L)
.beginClass<Circle>("Circle")
.addConstructor <void(*) (void)>()
... "other stuff to register" ...
.addFunction("draw", &Circle::draw)
.endClass();
return 1;
}
and finally (if needed) my lua script:
local ball = Circle()
ball:setColor(255,0,0,255)
while true do
ball:draw()
end
Result when the Lua Circle is moving by increasing one of the Vector2 values:
Hope you can help and I described it good :x
Thanks :)
-- Updated code in main.cpp
I'm not sure what is happening, but I see some problems in the code.
First: you don't remove "old" CircleShapes from CircleDrawList.
In the function Circle::draw() you are pushing object to the front of the list with CircleDrawList.push_front(circleShape); but you never remove anything from it. Using CircleDrawList.front() gives you access to the first element but you have to use method pop_front() to remove it.
Second thing. Look at the code:
//iterating through whole list with range-for:
for (sf::CircleShape obj : CircleDrawList)
{
//why do you use CircleDrawList.front()?
//You should probably use obj here!
window.draw(CircleDrawList.front());
}
As for now this code draws first element of the list n times where n is the length of the CircleDrawList. Do you really want to do this?
Moreover you are drawing splash every frame. If you wish to display splash only at the beginning, then the line window.draw(splash_screen); // splash screen before Lua script is loaded should probably be in some sort of conditional instruction.
I don't know what is between drawing splash_screen and drawing circles. It could have some impact on what we see on the screen.
And one more thing: it is strongly recommended to avoid using global variables. CircleDrawList is a global variable and my advice would be to put it at least in some namespace, or even better to enclose it in some class.
I'm writing a game in C++ using the Windows API which has a Splash Screen at the start, before gameplay begins, and can be paused.
I store the state of the game in an enum, game_state {PAUSED, PLAYING, SPLASHSCREEN}, and rely on Keyboard input to control the game.
The game was working properly, switching between paused and playing, but when I tried to add a splashscreen to begin the game on, the pause functionality stopped working, and I'm not sure why...
if(Keyboard.GetKey(VK_RETURN) && game_state == SPLASHSCREEN)
{
game_state = PLAYING;
Keyboard.SetKey(VK_RETURN, false);
}
if(Keyboard.GetKey(VK_RETURN))
{
if(game_state == PAUSED)
{
game_state = PLAYING;
}
else
{
game_state = PAUSED;
}
Keyboard.SetKey(VK_RETURN, false);
}
//If Paused, go to Pause Screen
if(game_state == PAUSED)
{
pauseScreen();
}
//If Splash Screen, go to Splash Screen
if(game_state == SPLASHSCREEN)
{
splashScreen();
}
//If not paused, do game processing
if(game_state == PLAYING)
{
gamePlay();
}
GetKey() returns true if the key is held down.
game_state is an enum global containing the current state of the game.
SetKey() sets the specified key as down (true) or up (false)
Oh, and all splashScreen() pauseScreen() and gamePlay() do are display sprites representing each state (at the moment)
SetKey
void Keyboard::SetKey(WPARAM key, bool key_down)
{
if(key_down)
{
m_keys[key] = true;
}
else
{
m_keys[key] = false;
}
}
GetKey
bool Keyboard::GetKey(WPARAM key)
{
if(m_keys[key])
{
m_keys[key] = false;
return true;
}
else
{
return false;
}
}
Remove m_keys[key] = false; from the Keyboard::GetKey method. As it is being set to false in the first check, it prevents the next check from seeing that it was pressed.
Calling GetKey() sets the key as released - since it checks to see if the key is pressed and the state is splashscreen before checking anything else - the key will always be released when checking it again.
Alter GetKey or alter the way the code is written.
My best guess is that your splash screen has the focus and it will take over the message loop, then you don't get the key event. Just a guess, can't really know without seeing the window creation/registration code of your splash and main windows.