Search code examples
audiographicsc++17renderingsfml

SFML audio Visual Studio C++


I am using Visual Studio to run a simple SFML that is a clone of Super Mario. I tried to replicate the issue, and Failed, so just updated this post with actual issue. I understand that this is very big, but have tried to keep all necessary function. Any suggestion to even replicate the issue would also be appreciated.

Issue:
In my game, the The registerAction() function sets each keyboard inputs relevant to scene to a action and once done, the sUserInput() in GameEngine is called in game loop, which check for any valid key inputs and then calls sDoAction() for the relevant scene. And in every game loop for each scene, it calls sMovement() and update the positions of various entities.

Now I want to play audio when player jumps, I have added it under sMovement() when play jump is detected, but the audio wont play unless I have added sleep after that. And adding sleep will make game lag as the game will be froze for few seconds. So any idea How I can add sound without sleep?

Full source code: https://github.com/Shreyas9699/SuperMarioClone

void Scene_Play::init(const std::string& levelPath)
{
    std::cout << "Level Scene init started" << std::endl;
    registerAction(sf::Keyboard::P,      "PAUSE");
    registerAction(sf::Keyboard::Escape, "QUIT");
    registerAction(sf::Keyboard::T,      "TOGGLE_TEXTURE");         // toggle drawring (T)extures
    registerAction(sf::Keyboard::C,      "TOGGLE_COLLISION");       // toggle drawring (T)extures
    registerAction(sf::Keyboard::G,      "TOGGLE_GRID");            // toggle drawring (T)extures
    registerAction(sf::Keyboard::W,      "UP");
    registerAction(sf::Keyboard::A,      "LEFT");
    registerAction(sf::Keyboard::D,      "RIGHT");
    registerAction(sf::Keyboard::Space,  "FIRE");

    m_gridText.setCharacterSize(2);
    m_gridText.setFont(m_game->getAssets().getFont("dogica"));

    // Game Score
    m_scoreText.setCharacterSize(30);
    m_scoreText.setFont(m_game->getAssets().getFont("Mario"));
    m_scoreText.setFillColor(sf::Color::White);

    // Level Timre
    m_timerText.setCharacterSize(30);
    m_timerText.setFont(m_game->getAssets().getFont("Mario"));
    m_timerText.setFillColor(sf::Color::White);

    sf::View view = m_game->window().getView();
    view.setSize(width() / 4.0f, height() / 4.0f);
    m_game->window().setView(view);
    auto start = std::chrono::high_resolution_clock::now();
    loadLevel(levelPath);
    auto end = std::chrono::high_resolution_clock::now();
    std::cout << "Time taken to load Scene and its scene entities : " << std::chrono::duration_cast<std::chrono::milliseconds>(end - start) << std::endl;
}
void Scene_Play::sDoAction(const Action& action)
{
    auto& input = m_player->getComponent<CInput>();

    if (action.type() == "START")
    {
        if      (action.name() == "TOGGLE_TEXTURE")     { m_drawTexture = !m_drawTexture; }
        else if (action.name() == "TOGGLE_COLLISION")   { m_drawCollision = !m_drawCollision; }
        else if (action.name() == "TOGGLE_GRID")        { m_drawGrid = !m_drawGrid; }
        else if (action.name() == "PAUSE")              { m_paused = !m_paused; }
        else if (action.name() == "QUIT")               { onEnd(); }
        else if (action.name() == "UP")                 { input.up = true; }
        else if (action.name() == "DOWN")               { input.down = true; }
        else if (action.name() == "RIGHT")              { input.right = true; }
        else if (action.name() == "LEFT")               { input.left = true; }
        else if (action.name() == "FIRE" && !input.shoot)
        {
            input.shoot = true;
            spawnBullet(m_player); 
        }
    }
    else if (action.type() == "END")
    {
        if      (action.name() == "UP")     { input.up = false; }
        else if (action.name() == "DOWN")   { input.down = false; }
        else if (action.name() == "RIGHT")  { input.right = false; }
        else if (action.name() == "LEFT")   { input.left = false; }
        else if (action.name() == "FIRE")   { input.shoot = false; }
    }
}
void Scene_Play::sMovement()
{
    // Player movement
    auto& input = m_player->getComponent<CInput>();
    auto& transform = m_player->getComponent<CTransform>();

    if (input.up && input.canJump)
    {
        transform.velocity.y -= m_playerConfig.JUMP;
        input.canJump = false;
        sf::Sound m_sound;
        m_sound.setBuffer(m_game->getAssets().getSound("MarioAir"));
        m_sound.play();
        std::this_thread::sleep_for(std::chrono::milliseconds(100));
    }

    float xSpeed = 0.0f;
    if (input.right)
    {
        xSpeed += m_playerConfig.SPEED;
        transform.scale.x = -1.0f;
    }
    if (input.left)
    {
        xSpeed -= m_playerConfig.SPEED;
        transform.scale.x = 1.0f;
    }
    transform.velocity.x = xSpeed;

    if (xSpeed == 0.0f)
    {
        if (m_isSuperMario && m_player->getComponent<CState>().state != "SuperMarioStand")
        {
            m_player->getComponent<CState>().state = "SuperMarioStand";
            m_StateChanged = true;
        }
        else if (!m_isSuperMario &&  m_player->getComponent<CState>().state != "MarioStand")
        {
            m_player->getComponent<CState>().state = "MarioStand";
            m_StateChanged = true;
        }
    }
    else
    {
        if (m_isSuperMario && m_player->getComponent<CState>().state != "SuperMarioRun")
        {
            m_player->getComponent<CState>().state = "SuperMarioRun";
            m_StateChanged = true;
        }
        else if (!m_isSuperMario && m_player->getComponent<CState>().state != "MarioRun")
        {
            m_player->getComponent<CState>().state = "MarioRun";
            m_StateChanged = true;
        }
    }

    auto& gravity = m_player->getComponent<CGravity>();
    transform.velocity.y += gravity.gravity;

    if (transform.velocity.y >= m_playerConfig.MAXSPEED)
    {
        transform.velocity.y = m_playerConfig.MAXSPEED;
    }

    transform.prevPos = transform.pos;
    transform.pos += transform.velocity;
    // rest code for different input handle
}
void GameEngine::sUserInput()
{
    sf::Event event;
    while (m_window.pollEvent(event))
    {
        if (event.type == sf::Event::Closed)
        {
            std::cout << "Exiting the game!\n";
            quit();
        }
        if (event.type == sf::Event::KeyPressed && event.key.code == sf::Keyboard::X)
        {
            std::cout << "trying to save screeshot to " << "test.png" << std::endl;
            sf::Texture t;
            t.create(m_window.getSize().x, m_window.getSize().y);
            t.update(m_window);
            if (t.copyToImage().saveToFile("test.png"))
            {
                std::cout << "screenshot saved to " << "test.png" << std::endl;
            }
        }
        if (event.type == sf::Event::KeyPressed || event.type == sf::Event::KeyReleased)
        {
            if (getCurrentScene()->getActionMap().find(event.key.code) == getCurrentScene()->getActionMap().end())
            {
                continue;
            }
            const std::string actionType = (event.type == sf::Event::KeyPressed) ? "START" : "END";
            getCurrentScene()->sDoAction(Action(getCurrentScene()->getActionMap().at(event.key.code), actionType));
        }
    }
}

Solution

  • It's because the program ends as soon as the sound starts to play, so you don't hear anything. sound.play() launches a new thread to not block code that comes next, but since the main function ends right afterwards, the child thread ends as well. You can try putting a sleep function after sound.play();, but before return 0;