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

Show Posts

This section allows you to view all posts made by this member. Note that you can only see posts made in areas you currently have access to.


Messages - Discover-

Pages: [1]
1
General discussions / Re: SFML Game Development -- A book on SFML
« on: June 25, 2013, 11:48:45 pm »
Looking really nice judging from the preview on Amazon! Sadly my credit card is having problems this evening so I'll give it a shot when I'm less tired tomorrow - can't wait to read it!

2
Quote
So you're suggesting to save the sf::Sound and sf::SoundBuffer along with the filename (std::string) and then do.. what exactly on Audio::Play?
You should store one sound buffer per audio file, and then as many sounds as you need to play. You may play several sounds that use the same sound buffer at the same time (gun shot, foot step, ...), so it's not a good idea to link the sf::Sound to the sf::SoundBuffer/filename. That's the reason why SFML uses this design with two separate classes.

So you should redesign your whole Audio class, your current design cannot handle playing multiple instances of the same sound at the same time.

I'm not getting the part I bolded in your text. How am I supposed to store several sf::Sound instances and link them all to a single sf::SoundBuffer and std::string? Are we talking something like this:
std::map<std::string, std::pair<sf::SoundBuffer, std::list<sf::Sound> > > sounds;

I'm sorry for the many questions, I'm just having a really hard time understanding the audio system (as you can tell by now).

Also, when that part (thus the way you're saying it should work) is implemented, wouldn't it be better - in terms of design - to make the Audio class singleton?

3
Don't you feel like your Audio::Load function is similar to the loadSound function of the tutorial? Don't you make exactly the same mistake?
Now that you're saying... Oops.  ;D
So you're suggesting to save the sf::Sound and sf::SoundBuffer along with the filename (std::string) and then do.. what exactly on Audio::Play?

Quote
And what do you think of the previous design of the Sound class I implemented - was that bad, too?
Where can I see it?
https://github.com/Discover-/Platformer-SFML/commit/a6422d51e9ad6fc4b05f85b5ef6689893f6a1ef9


4
Quote
there is no reason to store both, right?

Quote from: Laurent
You must store both the sound buffers and the sounds that you create.

Read the tutorial carefully, this is clearly explained.

I'm sorry but I'm not seeing it. I assume you're pointing at this part of the tutorial?



If that is the part then I don't understand where I'm going wrong.

And what do you think of the previous design of the Sound class I implemented - was that bad, too?

Thanks for all the help, I appreciate it a lot!

Greetings,

Jasper

5
Sorry to say that, but your new design is totally wrong :P

You're always constructing/using/returning temporary sf::Sound objects, how do you expect anything to work permanently? As soon as a sf::Sound is destroyed, everything that you've done on it is lost. All your functions are useless.

You must store both the sound buffers and the sounds that you create.

No need to be sorry, hehe.

I implemented your idea with a small twist (sf::SoundBuffer becoming sf::Sound in the std::map because there is no reason to store both, right?). Sadly, it's still not working.. (I get the same debug error, too)

#include "audio.h"
#include "game.h"
#include <SFML/Audio.hpp>

Audio::Audio(Game* _game)
{
    game = _game;
}

Audio::~Audio()
{

}

bool Audio::Load(std::string filename)
{
    sf::SoundBuffer buffer;

    if (buffer.loadFromFile(filename))
    {
        sounds[filename] = sf::Sound(buffer);
        return true;
    }
    else
        std::cout << "Audio::Load: Could not find audio file '" << filename << "'" << std::endl;

    return false;
}

void Audio::Play(std::string filename, bool loop /* = false */)
{
    std::map<std::string, sf::Sound>::iterator itr = sounds.find(filename);

    if (itr != sounds.end())
    {
        (*itr).second.play();

        if (loop)
            (*itr).second.setLoop(true);

        if (game->IsMusicMuted())
            (*itr).second.setVolume(0.0f);
    }
    else
        std::cout << "Audio::Play: Could not find audio file '" << filename << "'" << std::endl;
}

void Audio::Stop(std::string filename)
{
    if (filename == "all")
    {
        for (std::map<std::string, sf::Sound>::iterator itr = sounds.begin(); itr != sounds.end(); ++itr)
            (*itr).second.stop();
    }
    else
    {
        if (sf::Sound* playingSnd = GetPlayingSound(filename))
            playingSnd->stop();
        else
            std::cout << "Audio::Stop: Could not find audio file '" << filename << "'" << std::endl;
    }
}

void Audio::SetVolume(std::string filename, float volume)
{
    if (filename == "all")
    {
        for (std::map<std::string, sf::Sound>::iterator itr = sounds.begin(); itr != sounds.end(); ++itr)
            (*itr).second.setVolume(volume);
    }
    else
    {
        if (sf::Sound* playingSnd = GetPlayingSound(filename))
            playingSnd->setVolume(volume);
        else
            std::cout << "Audio::SetVolume: Could not find audio file '" << filename << "'" << std::endl;
    }
}

float Audio::GetVolume(std::string filename)
{
    if (sf::Sound* playingSnd = GetPlayingSound(filename))
        return playingSnd->getVolume();
    else
        std::cout << "Audio::GetVolume: Could not find audio file '" << filename << "'" << std::endl;

    return 0.0f;
}

void Audio::SetLoop(std::string filename, bool val)
{
    if (filename == "all")
    {
        for (std::map<std::string, sf::Sound>::iterator itr = sounds.begin(); itr != sounds.end(); ++itr)
            (*itr).second.setLoop(val);
    }
    else
    {
        if (sf::Sound* playingSnd = GetPlayingSound(filename))
            playingSnd->setLoop(val);
        else
            std::cout << "Audio::SetLoop: Could not find audio file '" << filename << "'" << std::endl;
    }
}

bool Audio::IsLooping(std::string filename)
{
    if (sf::Sound* playingSnd = GetPlayingSound(filename))
        return playingSnd->getLoop();
    else
        std::cout << "Audio::IsLooping: Could not find audio file '" << filename << "'" << std::endl;

    return false;
}

sf::SoundSource::Status Audio::GetStatus(std::string filename)
{
    if (sf::Sound* playingSnd = GetPlayingSound(filename))
        return playingSnd->getStatus();
    else
        std::cout << "Audio::GetStatus: Could not find audio file '" << filename << "'" << std::endl;

    return sf::SoundSource::Stopped;
}

sf::Sound* Audio::GetPlayingSound(std::string filename)
{
    std::map<std::string, sf::Sound>::iterator itr = sounds.find(filename);

    if (itr != sounds.end())
        return &(*itr).second;

    return NULL;
}
 

6
Hello everybody,

Today I decided to re-write the design/logic in my Sound class (renamed to Audio) (it used to work before the re-write but the design was bad) by storing a std::string and a sf::SoundBuffer in a std::map in the class and then loading all .wav files into this map on start-up of the application (later on moving this to a separate thread, of course). A  new issue had arisen so I started throwing around breakpoints and came to the conclusion that it for whatever reason seems to not ever load the sfml-audio-d-2.dll, even though it is in place with all other .dll's. Here a screenshot of the error:


(take a look at the Watch window)

Showing that it did in fact find the song we are trying to play (different sound being played):


As for the .dll's:


Now as for the code, it can all be found on this GitHub repository, but I'll paste some code regarding the issue here anyhow.

audio.h
#pragma once

#include <vector>
#include <array>
#include <map>
#include <SFML/Graphics.hpp>
#include <SFML/Audio.hpp>
#include <iostream>

class Game;

class Audio
{
    public:
        Audio(Game* _game);
        ~Audio();

        bool Load(std::string filename);
        void Play(std::string filename, bool loop = false);
        void Stop(std::string filename);
        void SetVolume(std::string filename, float volume);
        float GetVolume(std::string filename);
        void SetLoop(std::string filename, bool val);
        bool IsLooping(std::string filename);
        sf::SoundSource::Status GetStatus(std::string filename);
        sf::Sound* GetPlayingSound(std::string filename);

    private:
        Game* game;
        std::map<std::string, sf::SoundBuffer> soundBuffers;
};
 

audio.cpp
#include "audio.h"
#include "game.h"
#include <SFML/Audio.hpp>

Audio::Audio(Game* _game)
{
    game = _game;
}

Audio::~Audio()
{

}

bool Audio::Load(std::string filename)
{
    sf::SoundBuffer buffer;

    if (buffer.loadFromFile(filename))
    {
        soundBuffers[filename] = buffer;
        return true;
    }
    else
        std::cout << "Audio::Load: Could not find audio file '" << filename << "'" << std::endl;

    return false;
}

void Audio::Play(std::string filename, bool loop /* = false */)
{
    std::map<std::string, sf::SoundBuffer>::iterator itr = soundBuffers.find(filename);

    if (itr != soundBuffers.end())
    {
        sf::Sound playingSound = sf::Sound((*itr).second);

        playingSound.play();

        if (loop)
            playingSound.setLoop(true);

        if (game->IsMusicMuted())
            playingSound.setVolume(0.0f);
    }
    else
        std::cout << "Audio::Play: Could not find audio file '" << filename << "'" << std::endl;
}

void Audio::Stop(std::string filename)
{
    if (filename == "all")
    {
        for (std::map<std::string, sf::SoundBuffer>::iterator itr = soundBuffers.begin(); itr != soundBuffers.end(); ++itr)
            sf::Sound((*itr).second).stop();
    }
    else
    {
        if (sf::Sound* playingSnd = GetPlayingSound(filename))
            playingSnd->stop();
        else
            std::cout << "Audio::Stop: Could not find audio file '" << filename << "'" << std::endl;
    }
}

void Audio::SetVolume(std::string filename, float volume)
{
    if (filename == "all")
    {
        for (std::map<std::string, sf::SoundBuffer>::iterator itr = soundBuffers.begin(); itr != soundBuffers.end(); ++itr)
            sf::Sound((*itr).second).setVolume(volume);
    }
    else
    {
        if (sf::Sound* playingSnd = GetPlayingSound(filename))
            playingSnd->setVolume(volume);
        else
            std::cout << "Audio::SetVolume: Could not find audio file '" << filename << "'" << std::endl;
    }
}

float Audio::GetVolume(std::string filename)
{
    if (sf::Sound* playingSnd = GetPlayingSound(filename))
        return playingSnd->getVolume();
    else
        std::cout << "Audio::GetVolume: Could not find audio file '" << filename << "'" << std::endl;

    return 0.0f;
}

void Audio::SetLoop(std::string filename, bool val)
{
    if (filename == "all")
    {
        for (std::map<std::string, sf::SoundBuffer>::iterator itr = soundBuffers.begin(); itr != soundBuffers.end(); ++itr)
            sf::Sound((*itr).second).setLoop(val);
    }
    else
    {
        if (sf::Sound* playingSnd = GetPlayingSound(filename))
            playingSnd->setLoop(val);
        else
            std::cout << "Audio::SetLoop: Could not find audio file '" << filename << "'" << std::endl;
    }
}

bool Audio::IsLooping(std::string filename)
{
    if (sf::Sound* playingSnd = GetPlayingSound(filename))
        return playingSnd->getLoop();
    else
        std::cout << "Audio::IsLooping: Could not find audio file '" << filename << "'" << std::endl;

    return false;
}

sf::SoundSource::Status Audio::GetStatus(std::string filename)
{
    if (sf::Sound* playingSnd = GetPlayingSound(filename))
        return playingSnd->getStatus();
    else
        std::cout << "Audio::GetStatus: Could not find audio file '" << filename << "'" << std::endl;

    return sf::SoundSource::Stopped;
}

sf::Sound* Audio::GetPlayingSound(std::string filename)
{
    std::map<std::string, sf::SoundBuffer>::iterator itr = soundBuffers.find(filename);

    if (itr != soundBuffers.end())
        return &sf::Sound((*itr).second);

    return NULL;
}

Loading the audio (done using the Dirent library to load all files from a given directory)
void Game::LoadAllAudio()
{
    DIR* dir;
    struct dirent* ent;
    std::stringstream ss;
    audio = new Audio(this);

    if ((dir = opendir("Audio")) != NULL)
    {
        while ((ent = readdir(dir)) != NULL)
        {
            if (ent->d_name[0] != '.') //! These seem to be the only hidden invisible files in there and the dirent library doesn't offer detection for it, so this will work. :)
            {
                ss << "Audio/" << ent->d_name;
                audio->Load(ss.str().c_str());
                ss.str(std::string());
            }
        }

        closedir(dir);
    }
}

Playing the audio (example)
Menu::Menu(Game* _game)
{
    game = _game;
    selectedOption = 1;
    currentMenu = MENU_MAIN;
    newMenu = MENU_NONE;
    movingCurrMenuOut = false;
    movingNewMenuIn = false;
    game->GetAudio()->Play("Audio/menu_music.wav", true);
}

Hope that is enough!

Thanks for reading,

Jasper

7
Graphics / Re: Performance drop when loading map from .txt
« on: June 06, 2013, 02:11:32 am »
I figured out the problem and rewrote the 'system' and not it works again. Thanks :)

8
Graphics / Performance drop when loading map from .txt
« on: June 05, 2013, 04:51:49 pm »
Hello everybody,

I've started working on my own platformer game with the help of SFML after getting slightly tired of SDL and I've ran into my first 'big' problem that I have had no success solving on my own so far. When I woke up this morning I decided I wanted to implement at least a proper Level class which loads and draws the map based on the level it's loaded from (which is a .txt file). The problem is that the FPS is now at max 8 FPS because I load all images, set the texture of the sprite to this image, set their position AND draw it every update call unlike I did previously (only drawing in the game loop). The issue, however, is that I want to not hardcode the amount in an array like I did in the Game class but store it in a vector properly but I have no idea how to properly do this.

The level (.txt) I am testing:
0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
9 1 1 1 9 9 9 9 9 9 2 2 2 2 2 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9
9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 2 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9
9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 2 1 2 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9
2 2 2 2 2 2 2 2 2 2 9 9 9 9 9 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 9 9 9 9 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 9 9 9 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 9 9 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1

How the level looks:


The current code using to load the levels:
void Level::LoadMap(const char* filename)
{
    std::ifstream openfile(filename);
    std::string line;
    std::vector<int> tempVector;

    while (std::getline(openfile, line))
    {
        for (int i = 0; i < line.length(); i++)
        {
            if (line[i] != ' ')
            {
                char value[1] = { line[i] };
                tempVector.push_back(atoi(value));
            }
        }

        mapVector.push_back(tempVector);
        tempVector.clear();
    }

    for (int i = 0; i < mapVector.size(); i++)
    {
        for (int j = 0; j < mapVector[i].size(); j++)
        {
            std::string randNumb = std::to_string(long double(urand(0, 1)));

            switch (mapVector[i][j])
            {
                case 9:
                    fileArray[i][j] = "Graphics/Tiles/sky_3.png"; //! Empty sky block
                    break;
                case 0:
                    fileArray[i][j] = "Graphics/Tiles/sky_" + std::to_string(long double(urand(0, 20) < 16 ? 3 : urand(0, 2))) + ".png";
                    break;
                case 1:
                    fileArray[i][j] = "Graphics/Tiles/dirt_" + randNumb + ".png";
                    break;
                case 2:
                    fileArray[i][j] = "Graphics/Tiles/dirt_rock_" + randNumb + ".png";
                    break;
                case 3:
                    fileArray[i][j] = "Graphics/Tiles/grass_" + randNumb + ".png";
                    break;
                case 4:
                    fileArray[i][j] = "Graphics/Tiles/grass_ontop_" + randNumb + ".png";
                    break;
                case 5:
                    fileArray[i][j] = "Graphics/Tiles/ground_" + randNumb + ".png";
                    break;
                case 6:
                    fileArray[i][j] = "Graphics/Tiles/sand_" + randNumb + ".png";
                    break;
                default:
                    return;
            }
        }
    }
}

Code to draw the map (called right after window.clear() in Game class' loop:
void Level::DrawMap(sf::RenderWindow &window)
{
    sf::Sprite sprite;
    sf::Texture image;
    Player* player = game->GetPlayer();

    for (int i = 0; i < mapVector.size(); i++)
    {
        for (int j = 0; j < mapVector[i].size(); j++)
        {
            //! ONLY draw the images if the player is within visibility distance, else there's no point in wasting performance.
            if (IsInRange(player->GetPositionX(), j * 50.0f, player->GetPositionY(), i * 50.0f, 600.0f))
            {
                // Here is the problem
                image.loadFromFile(fileArray[i][j]);
                sprite.setTexture(image);
                sprite.setPosition(j * 50.0f, i * 50.0f);
                window.draw(sprite);
            }
        }
    }
}



So that's it for the current way of initializing and drawing the tiles. The previous way - which was NOT having ANY performance issues at ALL - is as following:

int Game::Update()
{
    ...
    sf::Texture imageDirt[2];
    sf::Texture imageGrass[2];
    sf::Texture imageGround[2];
    sf::Texture imageSky[4];
    sf::Sprite spriteDirt[15][60];
    sf::Sprite spriteGrass[12][60];
    sf::Sprite spriteGround[12][60];
    sf::Sprite spriteSky[12][60];

    for (int i = 0; i < 4; ++i)
    {
        std::string numberInStr = std::to_string(long double(i));

        if (i < 2)
        {
            imageDirt[i].loadFromFile("Graphics/Tiles/dirt_" + numberInStr + ".png");
            imageGrass[i].loadFromFile("Graphics/Tiles/grass_" + numberInStr + ".png");
            imageGround[i].loadFromFile("Graphics/Tiles/ground_" + numberInStr + ".png");
        }

        imageSky[i].loadFromFile("Graphics/Tiles/sky_" + numberInStr + ".png");
    }

    for (int i = 7; i < 9; ++i)
        for (int j = 0; j < 60; ++j)
            spriteGrass[i][j].setTexture(imageGrass[urand(0, 1)]);

    for (int i = 9; i < 12; ++i)
        for (int j = 0; j < 60; ++j)
            spriteGround[i][j].setTexture(imageGround[urand(0, 1)]);

    for (int i = 12; i < 15; ++i)
        for (int j = 0; j < 60; ++j)
            spriteDirt[i][j].setTexture(imageDirt[urand(0, 1)]);

    for (int i = 0; i < 12; ++i)
        for (int j = 0; j < 60; ++j)
            spriteSky[i][j].setTexture(imageSky[(i > 7 || urand(0, 20) < 16) ? 3 : urand(0, 2)]);

    float boxX = 0.0f, boxY = 0.0f;

    //! Filling up skybox
    for (int i = 0; i < 12; ++i)
    {
        for (int j = 0; j < 60; ++j)
        {
            spriteSky[i][j].setPosition(boxX, boxY);
            boxX += 50.0f;
        }

        boxX = 0.0f;
        boxY += 50.0f;
    }

    //! Filling up grass
    boxX = 0.0f;
    boxY = 250.0f;

    for (int i = 7; i < 9; ++i)
    {
        for (int j = 0; j < 60; ++j)
        {
            spriteGrass[i][j].setPosition(boxX, boxY);
            boxX += 50.0f;
        }

        boxX = 0.0f;
        boxY += 50.0f;
    }

    ... etc.

    //! Sky blocks are not collidable.
    for (int i = 0; i < 12; ++i)
        for (int j = 0; j < 60; ++j)
            gameObjects.push_back(spriteSky[i][j]);

    for (int i = 9; i < 12; ++i)
    {
        for (int j = 0; j < 60; ++j)
        {
            gameObjects.push_back(spriteGround[i][j]);
            gameObjectsCollidable.push_back(spriteGround[i][j]);
        }
    }

    ...etc
    ...

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

        for (int i = 0; i < 12; ++i)
            for (int j = 0; j < 60; ++j)
                window.draw(spriteSky[i][j]);

        for (int i = 9; i < 12; ++i)
            for (int j = 0; j < 60; ++j)
                window.draw(spriteGround[i][j]);

        for (int i = 7; i < 9; ++i)
            for (int j = 0; j < 60; ++j)
                window.draw(spriteGrass[i][j]);

        for (int i = 12; i < 15; ++i)
            for (int j = 0; j < 60; ++j)
                window.draw(spriteDirt[i][j]);

        ...

        window.setView(view);

        float fps = 1 / fpsClock.getElapsedTime().asSeconds();

        sf::Text text("FPS: " + std::to_string(long double(int(fps))), font, 15);
        text.setColor(sf::Color::Black);
        text.setPosition(436 + player->GetPositionX(), 15.0f);
        window.draw(text);

        window.display();
    }

    return 0;
}





I hope you guys are able to help me out here. If you wish to see the entire code, it's all on GitHub here: https://github.com/Discover-/Platformer-SFML/commits/.

Thanks in advance!

Greetings,

Discover- / Jasper

P.S. It's a shame there's no spoiler tags, they'd be really useful in cases like this post! :)

Pages: [1]