【问题标题】:How do I only play a sound once using sfml inside the main game loop如何在主游戏循环中使用 sfml 只播放一次声音
【发布时间】:2019-09-05 09:56:24
【问题描述】:

我有 2 个类,一个完全是菜单设置和渲染,另一个是在 main() 中调用的主游戏类。我制作了一种获取用户鼠标位置的方法,如果它位于其中一个菜单文本上,它将“选择”菜单项并播放声音。现在的问题是,除非您将鼠标悬停在任何文本上,否则声音不会播放一次而是不断重复。

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

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

这是在主类中运行程序的代码,在 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 行调用的函数。它在名为 Menu 的第二个类中。

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() 函数。再次在 Menu 类中。

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

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

【问题讨论】:

  • 使用one-shot-if :) 不是真的:您需要为相关的 ui 小部件实现(鼠标)on_enter 和 on_exit 方法
  • 您可能还想考虑为您的游戏命名,而不是 terraria,该名称的游戏已经存在。
  • 我知道它已经存在,我正在尝试用 c++ 重新创建整个游戏,以便以 64 位编译它。不用担心我不会分发它,它只是一个学习项目。
  • 尝试使用 C++ 实现 mod 支持会很麻烦。我还认为 Terraria 长期支持 x64 位 <.>
  • 唯一的 64 位启动器是与原来的 64 位 tmodloader 分开的 tmodloader (forums.terraria.org/index.php?threads/…)。原始的 tmodloader 和游戏本身都是 32 位的,除非你在 linux 或 mac 上玩,不记得哪个版本只有 64 位。

标签: c++ sfml


【解决方案1】:

执行此操作的一个非常简单的方法是检查当前选定的 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;
}

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

但是,虽然这可行,但我强烈建议改为使用正确的 MouseEnter 和 MouseLeave 事件。还使用一堆 if/else 语句进行选择并将按钮传递给选择函数并不是很好。 Select() 应该是您的按钮的成员。然后,您只需编写一个函数,让您获得鼠标悬停的按钮,并可以在其上调用 button.select()。从长远来看,这会使您的代码更加简洁。

【讨论】:

  • 这是否意味着我必须为主菜单按钮创建一个单独的类并在菜单构造函数中实例化它?
  • 您不必“必须”,但这可能是个好主意。当您选择一个按钮时发生的逻辑属于该按钮而不属于其他任何东西。还可以想象当您的菜单增加时会发生什么。大约有 20 个按钮,将所有按钮的所有选择逻辑组合在一个功能中会造成巨大的混乱。此外,您根本不必更改在添加新按钮时选择事物的代码。您可以将所有可点击元素存储在列表中并循环遍历它们。要添加新按钮,您只需创建一个新按钮对象并将其添加到该列表中。
  • 非常感谢!我实现了鼠标事件,现在声音完全按照我的意愿播放。我也会考虑按钮类。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-08-01
  • 1970-01-01
  • 1970-01-01
  • 2010-09-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多