We (school) are developping a game in C with SFML. The game is a fight game, where we need to play little sounds when the player gets hit for exemple.
I'm attempting to play a sf::Sound in a loop. I know we should not call the play() method of sf::Sound in a loop, but as the SFML apps all run in while loops, I have no other choices. Or at least, I don't know any other way to do it.
I tried to use a sound manager, I read multiple posts about it but I found nothing working.
Here is a sample code :
#include <SFML/Audio.hpp>
#include <vector>
#include <iostream>
int main() {
int FPS = 60;
// sf::Music music;
// if(!music.openFromFile("resources/audio/fight_theme.ogg")) {
// std::cout << "Music was not found" << std::endl;
// }
// music.setVolume(10.f);
// music.play();
// Create the main window
sf::VideoMode desktopMode = sf::VideoMode::getDesktopMode();
sf::RenderWindow app(
sf::VideoMode(
desktopMode.width,
desktopMode.height,
desktopMode.bitsPerPixel),
"SkyddaForStackOverflow",
sf::Style::Fullscreen
);
app.setFramerateLimit(FPS);
//Main loop
while (app.isOpen()) {
/*In the actual code, we have two player instances.
When the user press S or keyDown button, we call the attack function.
In the function, we deal the damages to the ennemy, then we call playSound
to play the hitting sound
Please note this is a sample code to help understanding the case.
In the actual code, everything is delegated to the Player class, to a SoundLoader (which is basically playSound() here)
The problem is, even with delegation, even if the playSound method is not situated in the main loop,
it is called in the main loop so the location of the code does not make any difference : it won't play since it won't
be called outside of a loop as we do for the background music (see commented code after main() {..., the background music
works fine.*/
if(player.attack(ennemy)) {
playSound();
}
}
return EXIT_SUCCESS;
}
void playSound() {
sf::SoundBuffer buffer;
if(!buffer.loadFromFile(path)) {
std::cout << "Sound was not found at " << path << std::endl;
}
sf::Sound sound(buffer);
sound.play();
}
Don't hesitate if you have additionnal questions. Thanks !
CodePudding user response:
The issue is not with "being called outside the loop"; the issue is that your sf::Sound
object is destroyed at the end of the playSound
function!
First, define two global (or class-) variables:
std::map<std::string, sf::SoundBuffer> buffers;
std::map<std::string, sf::Sound> sounds;
You can now define playSound
as follows:
void playSound(std::string path) {
if (auto it = sounds.find(path); it == sounds.end()) {
bool ok = buffers[path].loadFromFile(path);
// do something if ok is false
sounds[path] = sf::Sound{buffers[path]};
}
sounds[path].play();
}
This will keep your sf::Sound
objects (and associated sf::SoundBuffer
s) alive. The first call will load the file from disk, the subsequent calls will just restart the existing sound.
Note that this code is slightly suboptimal in favor of understandability: it looks up sounds[path]
twice. As an exercise to you, get rid of the second lookup by reusing it
in the play
call.