SFML community forums

Help => Audio => Topic started by: WDR on August 15, 2013, 07:35:01 pm

Title: Changing music when Game State changes
Post by: WDR on August 15, 2013, 07:35:01 pm
Hello... I am creating a tiny game with four game states... Menu, Intro, Play, Game Over. Each has its own unique music. The states are given within a switch case condition and the GUI and gameplay part of the switching of game states works perfectly fine. My question is how do I do that with music in such a way that music also changes when game state changes? The game state switching is defined in the while(window.isOpen()) loop. Playing the music within the loop won't work, naturally. How do I do it so that music changes when game state changes? Please tell me. Thank you.
Title: Re: Changing music when Game State changes
Post by: Ixrec on August 15, 2013, 07:38:16 pm
Well, it seems like it'd be extremely simple: whatever part of your code changes the state in the first place should be responsible for changing the music as well.

If you want a real answer you'll have to post complete and minimal code for us to look at.
Title: Re: Changing music when Game State changes
Post by: WDR on August 15, 2013, 08:00:31 pm
I get what you mean. I tried putting the music in a switch condition as well. But that didn't change when the game state changed. Yours WAS a real answer, but that doesn't mean I don't want more information.  ;D Here's my code.

This is outside the window.isOpen() loop:
sf::Music MenuMusic, PlayMusic;

if(!MenuMusic.openFromFile("images/menuscore.wav"))
        return EXIT_FAILURE;

if(!PlayMusic.openFromFile("images/score.wav"))
        return EXIT_FAILURE;

switch(CurrentState)
{
        case MENU:
                MenuMusic.play();
                MenuMusic.setLoop(true);
                break;
        case PLAY:
                PlayMusic.play();
                PlayMusic.setLoop(true);
}

This is within the window loop, where (I think) music can't be played:
switch(CurrentState)
{
case MENU:
        Menu MainMenu;
        MainMenu.Show(window, menu, play, quit, CurrentState);
        break;

case PLAY:
        Play PlayGame;
        PlayGame.Show(window, player, background, enemy, CurrentState);
        break;
}

This is the minimal of it. The completeness of it is kinda messy. But this is sufficient, I think (I hope). How do I do it? Please tell me. Thanks.
Title: Re: Changing music when Game State changes
Post by: Ixrec on August 15, 2013, 08:12:30 pm
It seems like you're calling .play() every single loop, which seems like it would be constantly restarting the music from the beginning every single frame.

Incidentally, when say "complete and minimal" code, the complete part is important too. We want to be able to compile it ourselves so we can actually find the problem instead of blindly guessing based on your description (which probably doesn't include the problem since the whole reason you're here is because you don't know what's causing it).

In particular, where's the code that changes the game state? As I said in my last post that's probably where the music switching code should go.
Title: Re: Changing music when Game State changes
Post by: WDR on August 15, 2013, 08:51:15 pm
I don't get you. Isn't the switch case inside the window loop where the game state changes? OK... Here's the complete and minimal code.

HEADERS.H:
#pragma once
#include<iostream>
#include<math.h>
#include<ctype.h>
#include<SFML/Audio.hpp>
#include<SFML/Graphics.hpp>
#include<SFML/Network.hpp>
#include<SFML/System.hpp>
#include<SFML/Window.hpp>
#include<list>
#include<string>

enum GameStates{MENU, INTRO, PLAY, GAMEOVER};

MENU.H:
#include "headers.h"

class Menu
{
public:
        int Show(sf::RenderWindow &window, sf::Sprite &menu, sf::Sprite &play, sf::Sprite &quit, GameStates &CurrentState);
};

MENU.CPP:
#include "menu.h"

int Menu::Show(sf::RenderWindow &window, sf::Sprite &menu, sf::Sprite &play, sf::Sprite &quit, GameStates &CurrentState)
{
        if(sf::Mouse::isButtonPressed(sf::Mouse::Left))
        {
                sf::FloatRect BoundsOfPlay = play.getGlobalBounds();
                sf::FloatRect BoundsOfExit = quit.getGlobalBounds();

                float mouseX = (float)sf::Mouse::getPosition(window).x;
                float mouseY = (float)sf::Mouse::getPosition(window).y;

                if(BoundsOfPlay.contains(mouseX,mouseY))
                        CurrentState = PLAY;

                if(BoundsOfExit.contains(mouseX, mouseY))
                        window.close();
        }
       
        window.draw(menu);
        window.draw(play);
        window.draw(quit);

        return EXIT_SUCCESS;
}

PLAY.H:
#include "headers.h"

class Play
{
public:
        int Show(sf::RenderWindow &window, sf::Sprite &menu, sf::Sprite &background, sf::Sprite &player, GameStates &CurrentState);
};

PLAY.CPP:
#include "play.h"

int Play::Show(sf::RenderWindow &window, sf::Sprite &menu, sf::Sprite &background, sf::Sprite &player, GameStates &CurrentState)
{
        window.draw(background);
        window.draw(player);
        return EXIT_SUCCESS;
}

MAIN.CPP:
#include "menu.h"
#include "player.h"

int main()
{
        // Initializing a Game State
        GameStates CurrentState;

        // Setting the Current Game State
        CurrentState = MENU;

        // Creating a Window
        sf::VideoMode resolution;
        sf::RenderWindow window(resolution.getDesktopMode(), "Game");

        // Initializing Textures
        sf::Texture playertex, backgroundtex, enemytex, menutex, playtex, quittex;

        // Initializing Sprites
        sf::Sprite player, background, menu, play, quit;

        // Loading Menu Background Texture
        if(!menutex.loadFromFile("images/menu.png"))
                return EXIT_FAILURE;
       
        // Setting Menu Background Texture to Menu Background Sprite
        menu.setTexture(menutex);
        menutex.setSmooth(true);

        // Getting Bounding Box of the Menu Background Sprite
        sf::FloatRect menubounds = menu.getGlobalBounds();

        // Initializing Menu Background Orientation
        menu.setOrigin(menubounds.width/2, menubounds.height/2);
        menu.setScale(resolution.getDesktopMode().width/menubounds.width, resolution.getDesktopMode().width/menubounds.width);
        menu.setPosition((float)resolution.getDesktopMode().width/2, (float)resolution.getDesktopMode().height/2);

        // Loading Play Button Texture
        if(!playtex.loadFromFile("images/play.png"))
                return EXIT_FAILURE;

        // Setting Play Button Texture to Play Button Sprite
        play.setTexture(playtex);

        // Getting Bounding Box of the Play Button Sprite
        sf::FloatRect playbounds = play.getGlobalBounds();

        // Initializing Play Button Orientation
        play.setOrigin(playbounds.width/2, playbounds.height/2);
        play.setPosition(resolution.getDesktopMode().width/2 - playbounds.width, resolution.getDesktopMode().height/2 + playbounds.height*2);

        // Loading Exit Button Texture
        if(!quittex.loadFromFile("images/quit.png"))
                return EXIT_FAILURE;

        // Setting Exit Button Texture to Exit Button Sprite
        quit.setTexture(quittex);

        // Getting Bounding Box of the Exit Button Sprite
        sf::FloatRect exitbounds = quit.getGlobalBounds();

        // Initializing Exit Button Orientation
        quit.setOrigin(exitbounds.width/2, exitbounds.height/2);
        quit.setPosition(resolution.getDesktopMode().width/2 + exitbounds.width, resolution.getDesktopMode().height/2 + exitbounds.height*2);

        // Loading Player Texture
        if(!playertex.loadFromFile("images/ship.png"))
                return EXIT_FAILURE;

        // Setting Player Texture to Player Sprite
        player.setTexture(playertex);

        // Getting Bounding Box of the Player Sprite
        sf::FloatRect playerbounds = player.getGlobalBounds();

        // Initializing Player Orientation
        player.setOrigin(playerbounds.width/2, playerbounds.height/2);
        player.setPosition((float)resolution.getDesktopMode().width/2, (float)(resolution.getDesktopMode().height - playerbounds.height/2));

        // Loading Play Background Texture
        if(!backgroundtex.loadFromFile("images/space.jpg"))
                return EXIT_FAILURE;

        // Setting Play Background Texture to Play Background Sprite
        background.setTexture(backgroundtex);

        // Getting Bounding Box of the Play Background Sprite
        sf::FloatRect border = background.getGlobalBounds();

        // Initializing Play Background Orientation
        background.setOrigin(border.width/2, border.height/2);
        background.setScale(resolution.getDesktopMode().width/border.width, resolution.getDesktopMode().height/border.height);
        background.setPosition((float)resolution.getDesktopMode().width/2, (float)resolution.getDesktopMode().height/2);

        sf::Music MenuMusic, PlayMusic;

        if(!MenuMusic.openFromFile("images/menuscore.wav"))
                return EXIT_FAILURE;

        if(!PlayMusic.openFromFile("images/score.wav"))
                return EXIT_FAILURE;

        switch(CurrentState)
        {
                case MENU:
                        MenuMusic.play();
                        MenuMusic.setLoop(true);
                        break;
                case PLAY:
                        PlayMusic.play();
                        PlayMusic.setLoop(true);
        }

        while(window.isOpen())
        {              
                float timer = clock.restart().asSeconds();

                sf::Event event;
               
                while(window.pollEvent(event))
                {
                        if(event.type == sf::Event::Closed)
                                window.close();
                       
                        if(event.key.code == sf::Keyboard::Escape)
                                window.close();
                }

                window.clear();

                switch(CurrentState)
                {
                case MENU:
                        Menu MainMenu;
                        MainMenu.Show(window, menu, play, quit, CurrentState);
                        break;

                case PLAY:
                        Play PlayGame;
                        PlayGame.Show(window, background, player, CurrentState);
                        break;
                }

                window.display();
        }
        return 0;
}

I apologize if the code is too long. And I know it is. But to just copy/paste it and test it right away, this is what I could give you. Again, apologies. Now, where might the problem be? Please tell me. Thank you.
Title: Re: Changing music when Game State changes
Post by: OniLinkPlus on August 15, 2013, 09:17:02 pm
If there's one thing that code is not, it's minimal. Minimal means one source file, with ALL code removed except for what is ABSOLUTELY necessary to demonstrate the problem.
Title: Re: Changing music when Game State changes
Post by: WDR on August 15, 2013, 09:47:44 pm
OK... I have absolutely no idea what to reply to that. If the tone of this statement conveys rudeness, my apologies, but it is not. It's confusion. One person says minimal and complete is copy-paste-execute code. Another person says minimal should only be the problematic part. If that is the case, then my second post was minimal. Anyways, anything I say at this point may offend people, which I think already happened. So... Minimal.

#include<iostream>
#include<SFML/Audio.hpp>
#include<SFML/Graphics.hpp>
#include<SFML/Network.hpp>
#include<SFML/System.hpp>
#include<SFML/Window.hpp>

int main()
{
        // Initializing a Game State
        GameStates CurrentState;

        // Setting the Current Game State
        CurrentState = MENU;

        // Creating a Window
        sf::VideoMode resolution;
        sf::RenderWindow window(resolution.getDesktopMode(), "Game", sf::Style::Fullscreen);

        sf::Music MenuMusic, PlayMusic;

        if(!MenuMusic.openFromFile("images/menuscore.wav"))
                return EXIT_FAILURE;

        if(!PlayMusic.openFromFile("images/score.wav"))
                return EXIT_FAILURE;

        switch(CurrentState)
        {
                case MENU:
                        MenuMusic.play();
                        MenuMusic.setLoop(true);
                        break;
                case PLAY:
                        PlayMusic.play();
                        PlayMusic.setLoop(true);
        }

        while(window.isOpen())
        {
                window.clear();

                switch(CurrentState)
                {
                case MENU:
                        Menu MainMenu;
                        MainMenu.Show(window, menu, play, quit, CurrentState);
                        break;

                case PLAY:
                        Play PlayGame;
                        PlayGame.Show(window, background, player, CurrentState);
                        break;
                }

                window.display();
        }
        return 0;
}

Once again... If any of that seemed impolite, my apologies. No offence intended. Confusion intended.
Title: Re: Changing music when Game State changes
Post by: OniLinkPlus on August 15, 2013, 10:02:42 pm
What? Why do you think anybody was offended? Your code was complete before, but not minimal.

Complete - You can copy, paste, and execute it with no modification.

Minimal - The only code there is is the code that relates to the problem, AND there is ideally only one source file.

So now, your code is minimal, but it is not complete. We need complete and minimal code in order to give you the best advice we can.

Anyways, your problem here is that you only tell the music to play once - in a switch statement before the while loop. Your code will never go back to that switch statement, so there is no way for the music to change to the new track. I have an idea of how to fix this, but I don't know the internals of the classes you use.
Title: Re: Changing music when Game State changes
Post by: WDR on August 15, 2013, 10:23:55 pm
What? Why do you think anybody was offended?

I don't know, dude! (Or, if you're a woman... ma'am!) It's late, in the middle of the night, I'm tired, I'm sloppy and cranky. I'm not able to do things properly. So, when I don't get my expected answer (even though it was my information lacking questions in the first place), I get irritated, I say something offensive, people get offended, they criticize my noobishness, they don't respond anymore. It is my need, so I gotta keep my ass shut and get my job done. And people just love to pick on noobs, so...   :-X

Anyways, back on topic... The classes I use. If you mean the two classes Menu and Play, they only have a single function Show() which draws the sprites. That's it. I can post them here, but they're already there in my previous post. Menu.h, Menu.cpp, Play.h, Play.cpp. Thanks for responding, by the way.
Title: Re: Changing music when Game State changes
Post by: Ixrec on August 15, 2013, 10:32:20 pm
Code: [Select]
[quote author=WDR link=topic=12659.msg88485#msg88485 date=1376592675]
I don't get you. Isn't the switch case inside the window loop where the game state changes?

The window loop is just code that gets executed as long as the window is open. Each iteration of the loop represents a frame.  The "game state" is something you've defined, which has no inherent connection to that loop.

If you define the game state as something which changes with every iteration of the isOpen loop, you're saying your game changes state every single frame.  Since your states are "play" and "menu," I'm pretty sure that's not actually what you want at all.

Now there should be a switch statement to make the appropriate draw() calls, since drawing has to happen every frame, but everything else should probably be functions outside the isOpen loop that get called when the state is supposed to change.  If the escape key opens menus, then your isOpen loop will contain an even loop checking for keypressed events, and if it sees Escape it will call switchToMenuState() (or maybe Menu.switchTo() or something), and part of that function will be setting the new music. That's the kind of thing I was suggesting.
Title: Re: Changing music when Game State changes
Post by: WDR on August 15, 2013, 10:57:18 pm
OK... But even if I define the functions outside, won't they be looping when they are called? Still confused.  :-\
Title: Re: Changing music when Game State changes
Post by: Ixrec on August 15, 2013, 11:36:26 pm
Not if you call them when an event occurs.  You may be looping the checks for the events, but the events themselves (mouse clicks, button presses, etc) don't loop, with the exception of holding down a key to generate multiple key presses.
Title: Re: Changing music when Game State changes
Post by: WDR on August 16, 2013, 02:47:33 pm
OK, OK... Now I understand what you mean. But I am not able to implement it practically. It sucks not knowing stuff even after reading and practicing it so many times.  :'(

Stupid amateur teachers. Not you guys... My college teachers!  >:(
Title: Re: Changing music when Game State changes
Post by: Ixrec on August 16, 2013, 04:25:21 pm
http://sfml-dev.org/tutorials/2.1/window-events.php ?
Title: Re: Changing music when Game State changes
Post by: WDR on August 19, 2013, 07:23:55 pm
OK... I kinda got it figured out... Not quite... But still... Here's what I got so far.

sf::Music MenuMusic, PlayMusic;

if(!MenuMusic.openFromFile("images/menuscore.wav"))
        return EXIT_FAILURE;

if(!PlayMusic.openFromFile("images/score.wav"))
        return EXIT_FAILURE;

MenuMusic.play();
MenuMusic.setLoop(true);

while(window.isOpen())
{              
        sf::Event event;

        while(window.pollEvent(event))
        {
                if(event.type == sf::Event::Closed)
                        window.close();
                       
                if(event.key.code == sf::Keyboard::Escape)
                        window.close();

                if(event.type == sf::Event::MouseButtonPressed)
                {
                        if(CurrentState == PLAY)
                        {
                                MenuMusic.stop();
                                PlayMusic.play();
                                PlayMusic.setLoop(true);
                        }
                }
        }
}

This works! Thankfully! But... The event for changing from MENU state to PLAY state is caused by a mouse click, so it works. But the change from PLAY to GAME OVER occurs due to collision between player and enemy sprites. sf::Event::MouseButtonPressed is a pre-defined event, so that worked. Can I define my own events for other state changes? Also... One mouse click goes to PLAY state. And after going to PLAY state, if I click mouse button again, it is distorting the music. Why is that? Please help.
Title: Re: Changing music when Game State changes
Post by: Ixrec on August 19, 2013, 07:42:29 pm
There's no point using events for that.  Write a function that detects collisions, call that in the window loop (since that's something you actually do want happening constantly), and when it does detect a collision then it will change the state (possibly by calling another function).

For the music distortion, try using your debugger to see what audio functions are getting called when you click again.  Also, did you mean to write if(CurrentState == PLAY)? Because that looks like it should be a !=.
Title: Re: Changing music when Game State changes
Post by: WDR on August 19, 2013, 08:09:16 pm
Yes, I did write a function for game state change when collisions occur. And it executes as desired. My actual question was... How do I implement music in that function? Do I need an event for that too? Sorry for not phrasing that correctly before.  ;D

And the if(CurrentState == PLAY) condition... If I execute the code without using that condition, it changes music when I click the mouse anywhere on the screen, even if there is no state change between MENU and PLAY. So, I wrote that to play music only when the state changes. Should I maybe use if(CurrentState != MENU)? But then, there are three other states that are != MENU. Please clarify these for me. Thanks.
Title: Re: Changing music when Game State changes
Post by: Ixrec on August 19, 2013, 09:38:03 pm
...you just call the same audio functions you did before? Like play()?  If you're asking about variable scope, then just make a global variable for your game's music so you can change it from anywhere.  If you know C++ that should go without saying.

You may have heard that global variables are evil, but for a little toy program like this it's fine.  As you develop your game eventually you'll hit a point where the downsides of globals become annoying and it makes sense to put the global music object inside a class or something.


For the condition, that's also making it sound like you don't know C++.  Obviously, the code under if(event.type == sf::Event::MouseButtonPressed) gets executed every time a mouse click happens, so if you have no condition at all, obviously the music changes every time you click.  If you want it to change when the game is in the PLAY state and you click, then use an if(CurrentState == PLAY) statement.  But since that music changing code stops the menu music and starts the play music, it looks like you intend it to be executed when the game switches to PLAY mode.  Whatever you intend it to do, that's what you should write.  I only asked about that line because it didn't appear to match what you said you wanted.
Title: Re: Changing music when Game State changes
Post by: WDR on August 19, 2013, 11:10:13 pm
OK... Yeah, I got what you said. I am familiar with those terms, only not in detail. I'm still a learner. But, I understand what you said. Still at the learning stage, so... baby steps... baby steps...

I will go into detail on what you said and implement it. Thank you. :)
Title: Re: Changing music when Game State changes
Post by: Ixrec on August 20, 2013, 01:22:09 am
Incidentally, based on the code snippets I saw before--and assuming you want your actual game to have similar code organization after you put some real content into it--it occurred to me a class like this might be handy.

class GameState {
  Menu MainMenu;
  Play PlayGame;
  typedef enum {MENU, PLAY} state_type;
  state_type state;
  std::map<state_type, sf::Music> musics;

public:

  GameState() {
    //load all the musics, do whatever you need to do to setup the Menu and Play objects properly
    state = MENU;
    musics[MENU].play();
  }

  draw_me() {
    switch(state) {
    case MENU:
      MainMenu.Show(window, menu, play, quit, CurrentState);
      break;
    case PLAY:
      PlayGame.Show(window, background, player, CurrentState);
      break;
    }
  }

  switch_state(state_type new_state) {
    switch(new_state) {
    case MENU:
      musics[state].stop();
      state = MENU;
      break;
    case PLAY:
      musics[state].stop();
      state = PLAY;
      break;
    }
  }

};
 

Just spent a minute scribbling this down, no testing or anything, so no guarantees.
Title: Re: Changing music when Game State changes
Post by: WDR on August 20, 2013, 04:36:30 pm
Oh... Thank you for that. I'll definitely check it out. My code is kinda messy now. I do have to make it OO. This could help when I do that. Thanks, again. This topic actually cleared quite a lot of my doubts.  :)