如何在主游戏循环中使用 sfml 只播放一次声音

How do I only play a sound once using sfml inside the main game loop

我有 2 个 classes,一个完全是菜单设置和渲染,另一个是在 main() 中调用的主游戏 class。我制作了一种获取用户鼠标位置的方法,如果它位于菜单文本之一上,它将 "select" 菜单项并播放声音。现在的问题是声音不会播放一次,而是会一直重复,除非您将鼠标悬停在远离任何文本的地方。

我试过使用我在网上找到的另一个人的时钟,但它似乎并没有完全按照预期的方式工作,因为我有他分成 2 classes 的东西而不是全部都在 1 个地方。

这就是我所说的解决方案:https://en.sfml-dev.org/forums/index.php?topic=15297.0

这是 运行 main class 内程序的代码,它在 main.cpp.

内的 main() 中被调用
void Terraria::Run()
{
    Menu Menu(mTerraria, mTerraria.getSize().x, mTerraria.getSize().y);
    Menu.mMainTheme.play();


    while (mTerraria.isOpen())
    {
        sf::Event event;
        Menu.GetMousePosition(mTerraria);

        while (mTerraria.pollEvent(event))
        {
            switch (event.type)
            {
            case sf::Event::MouseButtonReleased:
            {
                    switch (event.key.code)
                    {
                    case sf::Mouse::Left:
                        switch (Menu.GetPressedItem())
                        {
                        case 0:
                        {
                            break;
                        }
                        case 1:
                        {
                            break;
                        }
                        case 2:
                        {
                            break;
                        }
                        case 3:
                        {
                            break;
                        }
                        case 4:
                        {
                            Menu.mMenuOpen.play();
                            mTerraria.close();
                            break;
                        }
                        }
                    }
                }
            default:
                HandlePlayerInput(event.key.code);
                break;

            case sf::Event::Closed:
            {
                mTerraria.close();
            }
            }

            mTerraria.clear();
            Menu.Render(mTerraria);
            mTerraria.display();
            }
        }


}

这是它在 while 循环的第 10 行调用的函数。它位于第二个 class 菜单内。

void Menu::GetMousePosition(sf::RenderWindow& mTerraria)
{
    UnSelect();

    if (mMenu[0].getGlobalBounds().contains(sf::Mouse::getPosition(mTerraria).x, sf::Mouse::getPosition(mTerraria).y))
    {
        Select(mMenu[0]);
        OnSP = true;
        OnMP = false;
        OnAchievements = false;
        OnOptions = false;
        OnExit = false;
        SelectedItemIndex = 0;
    }

    else if (mMenu[1].getGlobalBounds().contains(sf::Mouse::getPosition(mTerraria).x, sf::Mouse::getPosition(mTerraria).y))
    {
        Select(mMenu[1]);
        OnSP = false;
        OnMP = true;
        OnAchievements = false;
        OnOptions = false;
        OnExit = false;
        SelectedItemIndex = 1;
    }

    else if (mMenu[2].getGlobalBounds().contains(sf::Mouse::getPosition(mTerraria).x, sf::Mouse::getPosition(mTerraria).y))
    {
        Select(mMenu[2]);
        OnSP = false;
        OnMP = false;
        OnAchievements = true;
        OnOptions = false;
        OnExit = false;
        SelectedItemIndex = 2;
    }

    else if (mMenu[3].getGlobalBounds().contains(sf::Mouse::getPosition(mTerraria).x, sf::Mouse::getPosition(mTerraria).y))
    {
        Select(mMenu[3]);
        OnSP = false;
        OnMP = false;
        OnAchievements = false;
        OnOptions = true;
        OnExit = false;
        SelectedItemIndex = 3;
    }
    else if (mMenu[4].getGlobalBounds().contains(sf::Mouse::getPosition(mTerraria).x, sf::Mouse::getPosition(mTerraria).y))
    {
        Select(mMenu[4]);
        OnSP = false;
        OnMP = false;
        OnAchievements = false;
        OnOptions = false;
        OnExit = true;
        SelectedItemIndex = 4;
    }
    else
    {
        OnSP = false;
        OnMP = false;
        OnAchievements = false;
        OnOptions = false;
        OnExit = false;
    }
}

这是每次鼠标悬停在任何文本上时调用的 Select() 函数。再次进入菜单 class.

void Menu::Select(sf::Text &mMenu)
{
    mMenu.setColor(sf::Color::Yellow);
    mMenu.setScale(sf::Vector2f(1.1f, 1.1f));
    mMenuTick.play();
}

我只需要在您将鼠标悬停在任何文本上时播放一次声音,然后直到您将鼠标悬停并悬停在下一个文本上或悬停在下一个文本上时才播放。我知道由于 while 循环,它被调用了数百万次。

一个非常简单的方法是检查当前 selected gui 元素的 id,并且仅当您的鼠标悬停在不是目前 select 一个。

您的 GetMousePosition 检查将变为如下所示:

if (mMenu[0].getGlobalBounds().contains(sf::Mouse::getPosition(mTerraria).x, sf::Mouse::getPosition(mTerraria).y))
{
    if SelectedItemIndex == 0 then return;        

    Select(mMenu[0]);
    OnSP = true;
    OnMP = false;
    OnAchievements = false;
    OnOptions = false;
    OnExit = false;
    SelectedItemIndex = 0;
}

这样,当您的鼠标悬停在已经 select 的按钮上时,您只需退出整个功能。

然而,虽然这有效,但我强烈建议改为使用适当的 MouseEnter 和 MouseLeave 事件。还用一堆 if/else 语句执行 select 并将按钮传递给 select 函数不是很好。 Select() 应该是您按钮的成员。然后,您只需编写一个函数,让您获得鼠标悬停的按钮,并可以在其上调用 button.select() 。这使您的代码在 运行.

中更清晰