Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
SFML Game Development.pdf
Скачиваний:
194
Добавлен:
28.03.2016
Размер:
4.19 Mб
Скачать

Waiting and Maintenance Area – Menus

Updating the menu

So, let's see our code in actual use. Do you remember MenuState from the previous chapter? There we implemented some menu logic, so you could choose between two options. Now this can be cleaned up a lot; as a result the menu state shrinks drastically.

MenuState::MenuState(StateStack& stack, Context context) : State(stack, context)

, mGUIContainer()

{

...

auto playButton = std::make_shared<GUI::Button>( *context.fonts, *context.textures);

playButton->setPosition(100, 250); playButton->setText("Play"); playButton->setCallback([this] ()

{

requestStackPop();

requestStackPush(States::Game);

});

mGUIContainer.pack(playButton);

}

The constructor initializes the buttons and packs them into the Container. As you can see, the lambda expression we give to the Button is the place where we actually describe the action we want the button to do. The rest of the functions are changed to use the GUI container to render and handle events.

void MenuState::draw()

{

sf::RenderWindow& window = *getContext().window; window.setView(window.getDefaultView()); window.draw(mBackgroundSprite); window.draw(mGUIContainer);

}

bool MenuState::update(sf::Time)

{

return true;

}

bool MenuState::handleEvent(const sf::Event& event)

{

mGUIContainer.handleEvent(event); return false;

}

[ 146 ]

www.it-ebooks.info

Chapter 6

This gives us much cleaner code than what we had in the previous chapter. Now we also don't have any risk of code duplication if we would like to have a menu in another place, such as the pause state. The pause menu looks very similar to this code, so we won't go through that as well.

The promised key bindings

We implement a new state; the SettingsState which you access through a button on the menu. What is the purpose for the SettingsState in our example? Currentlywe have the key bindings inside there. Everything we need has already been implemented with the GUI, so we only have to define the actual state.

The constructor of the SettingsState class one is pretty big so we snip it.

SettingsState::SettingsState(StateStack& stack, Context context) : State(stack, context)

, mGUIContainer()

{

mBackgroundSprite.setTexture( context.textures->get(Textures::TitleScreen));

mBindingButtons[Player::MoveLeft] = std::make_shared<GUI::Button>(...);

mBindingLabels[Player::MoveLeft] = std::make_shared<GUI::Label>(...);

... // More buttons and labels updateLabels();

auto backButton = std::make_shared<GUI::Button>(...); backButton->setPosition(100, 375); backButton->setText("Back"); backButton->setCallback([this] ()

{

requestStackPop();

});

mGUIContainer.pack(mBindingButtons[Player::MoveLeft]);

mGUIContainer.pack(mBindingLabels[Player::MoveLeft]);

...

mGUIContainer.pack(backButton);

}

[ 147 ]

www.it-ebooks.info

Waiting and Maintenance Area – Menus

We have put all the binding buttons and the associated labels in a static array, and associate to them the action they bind for. This means we don't have to duplicate a lot of callbacks, which you may have notice are missing. Instead, we have marked the button as one that toggles. We also have an updateLabels() function call. It's a helper function to make sure the labels are writing out the correct name for the key.

void SettingsState::updateLabels()

{

Player& player = *getContext().player;

for (std::size_t i = 0; i < Player::ActionCount; ++i)

{

sf::Keyboard::Key key = player.getAssignedKey(static_cast<Player::Action>(i));

mBindingLabels[i]->setText(toString(key));

}

}

Thanks to having it in an array, it's easy to loop through the labels and update them. Now moving on to the handleEvent() function, where we actually do something with the buttons.

bool SettingsState::handleEvent(const sf::Event& event)

{

bool isKeyBinding = false;

for (std::size_t action = 0; action < Player::ActionCount; ++action)

{

if (mBindingButtons[action]->isActive())

{

isKeyBinding = true;

if (event.type == sf::Event::KeyReleased)

{

getContext().player->assignKey (static_cast<Player::Action>(action), event.key.code);

mBindingButtons[action]->deactivate();

}

break;

}

}

if (isKeyBinding) updateLabels();

else

mGUIContainer.handleEvent(event); return false;

}

[ 148 ]

www.it-ebooks.info

Chapter 6

So how do we do this? If a button is pressed, then it will be activated, and since it's a button that toggles, it will stay active until we tell it to deactivate. So we loop through the array of buttons and check if anyone of them is active. If they are, then we are currently binding a key. When we get a key released event, then we change that binding on the player to the new key with the specified action before we deactivate the button again. After the loop, we update the labels, such that they have the correct name for everything. If no button is active, then we are not currently trying to bind a key, and should pass the event to the GUI container instead.

When we are done, the result should look like, as shown in the following screenshot:

[ 149 ]

www.it-ebooks.info

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]