Программирование: Обработка событий SFML

Читай на DevTribe.ru!

» Раздел: C++

Оригинал статьи - официальный туториал
Автор перевода - Nerevar

Обработка событий в SFML

Введение

Эта статья рассказывает о списке оконных событий в SFML. Здесь описываются события, а также то, как нужно и не нужно их использовать.

Тип sf::Event (The sf::Event type)

Перед тем, как будем разбираться с событиями, важно понимать, что за тип sf::Event и как его правильно использовать.sf::Event - это объединение(union), что означает, что только один из его участников(member) действителен в определенный момент (вспомните ваши уроки C++: все участники(member) объединения(union) разделяют одну область памяти). Действительным участником(member) является тот, который соответствует типу событий, например event.key для события KeyPressed. Попытка прочитать значение других участников(member) приведет к неопределенному поведению (самым частым будет случайные или неверные значения). Очень важно не использовать участника(member) event, который не соответствует типу.
Произошедшие sf::Event заполняют pollEvent(или waitEvent) функцию вашего класса окна sf::Window. Только эти две функции могут производить правильные события, любая попытка использовать sf::Event, которая не вернула успешный вызов pollEvent(или waitEvent), приведет к такому же неопределенному поведению, которое было описано выше.
Чтобы не быть голословными, приведу пример типичного цикла событий:
sf::Event event;

// пока есть ожидающие события...
while (window.pollEvent(event))
{
    // проверяем тип события...
    switch (event.type)
    {
        // окно закрыто
        case sf::Event::Closed:
            window.close();
            break;

        // клавиша нажата
        case sf::Event::KeyPressed:
            ...
            break;

        // мы не обрабатываем другие типы событий
        default:
            break;
    }
}
Прочитайте параграф выше еще раз и убедитесь, что вы полностью понимаете его, объединение(union) sf::Event - это причина многих проблем для неопытных программистов
Отлично, теперь мы посмотрим на поддержку событий в SFML, что они значат и как их правильно использовать.

Событие закрытия (The Closed event)

Событие sf::Event::Closed срабатывает, когда пользователь хочет закрыть окно любым возможным способом (кнопкой "Закрыть", кнопкой на клавиатуре, например Esc, и другими способами). Это событие предоставляет только запрос закрытия, окно при этом еще не закрыто, когда событие запущено.
Типичный код просто вызывает функцию window.close() в ответ на это событие, чтобы закрыть окно. Однако, вы также можете сделать что-либо перед этим, например сохранить состояние приложение или спросить пользователя, что делать. Если вы ничего не сделаете, окно так и останется открытым.
Здесь нет участников(member), связанных с этим событием в объединении(union) sf::Union.
if (event.type == sf::Event::Closed)
    window.close();

Событие изменения размеров окна (The Resized event)

Событие sf::Event::Resized срабатывает, когда пользователь изменяет размер окна, либо когда размер изменяется программно, вызовом функции window.setSize.
Вы можете использовать это событие, чтобы управлять настройками отображения: областью просмотра (viewport), если вы используете OpenGL напрямую, или текущим видом, если вы используете sfml-graphics.
Участник(member), связанный с этим событием - это event.size, он содержит новые размеры окна.
if (event.type == sf::Event::Resized)
{
    std::cout << "new width: " << event.size.width << std::endl;
    std::cout << "new height: " << event.size.height << std::endl;
}

События потери и получения фокуса (The LostFocus and GainedFocus events)

События sf::Event::LostFocus и sf::Event::GainedFocus запускаются, когда окно теряет\получает фокус, это случается, когда пользователь переключает текущее активное окно. Когда окно теряет фокус, оно не получает событий нажатия клавиш.
Это событие может быть использовано, если вы хотите остановить вашу игру, когда её окно неактивно.
У этих событий нет соответствующих участников(member) в объединении(union) sf::Event.
if (event.type == sf::Event::LostFocus)
    myGame.pause();

if (event.type == sf::Event::GainedFocus)
    myGame.resume();

Событие ввода текста (The TextEntered event)

Событие sf::Event::TextEntered срабатывает, когда вводятся символы. Его не следует путать с событием KeyPressed : TextEntered интерпретирует ввод пользователя и создает соответствующий печатный символ. Например, нажатие '^', затем 'e' на английской раскладке создаст два события KeyPressed, но одиночное событие TextEntered будет содержать символ 'e'. Это работает со всеми методами ввода, предоставляемыми операционными системами, даже самыми специфичными или сложными.
Это событие обычно используется для отлова пользовательского ввода в текстовом поле.
Участник(member), связанный с этим событием - это event.text, он содержит Юникодовое значение введенного символа. Вы также можете сохранить это значение в sf::String, или преобразовать в тип char, после того, как удостоверитесь, что символ в области ASCII (0-127).
if (event.type == sf::Event::TextEntered)
{
    if (event.text.unicode < 128)
        std::cout << "ASCII character typed: " << static_cast<char>(event.text.unicode) << std::endl;
}
Обратите внимание, что, поскольку они являются частью стандарта Юникод, некоторые непечатные символы, такие, как пробел, генерируются этим событием. В большинстве случаев вам нужно фильтровать их.
Многие программисты используют событие KeyPressed для отлова пользовательского ввода и начинают создавать безумные алгоритмы для отлова всех возможных комбинаций клавиш для корректной обработки символов. Не делайте этого!

Событие нажатия и отпускания клавиш (The KeyPressed and KeyReleased events)

События sf::Event::KeyPressed и sf::Event::KeyReleased срабатывают, когда клавиша нажимается\отпускается.
Если клавиша удерживается, создается несколько событий KeyPressed. Чтобы отключить повторные события Keypressed, вы можете использовать функцию window.setKeyRepeatEnabled (false). Очевидно, что событие KeyReleased никогда не может сработать повторно.
Это событие - единственное для использования, если вы хотите запустить событие, когда клавиша один раз нажата или отпущена, например заставить персонажа прыгать при нажатии пробела, или выход откуда-либо при нажатии Escape.
!!Иногда люди пытаются отлавливать событие KeyPressed, чтобы напрямую запускать плавное движение. Движение в этом направлении не даст ожидаемого результата, так как, когда вы удерживаете клавишу, это запускает всего несколько событий (из-за задержки между вводом в операционной системе). Для достижения плавного движения с помощью событий, вы должны использовать логическую переменную, которая будет истинной, когда сработало событие KeyPressed и ложной, когда сработало событие KeyReleased, затем вы можете двигать (не зависимо от событий) так долго, как логическая переменная будет истинной.
Другое (более простое) решение - это использование отлова ввода пользователя в реальном времени с помощью sf::Keyboard.!!
Участник(member), соответствующий этим событиям - event.key, он содержит код нажатой\отпущенной клавиши, а также текущие состояния клавиш-модификаторов (alt, control, shift, system).
if (event.type == sf::Event::KeyPressed)
{
    if (event.key.code == sf::Keyboard::Escape)
    {
        std::cout << "the escape key was pressed" << std::endl;
        std::cout << "control:" << event.key.control << std::endl;
        std::cout << "alt:" << event.key.alt << std::endl;
        std::cout << "shift:" << event.key.shift << std::endl;
        std::cout << "system:" << event.key.system << std::endl;
    }
}
Обратите внимание, что некоторые клавиши имеют особое значение для операционной системы, и приводят к неожиданному поведению.Примером может послужить нажатие клавиши F10 в Windows, которая "забирает" фокус окна, или клавишу F12, которая запускает отладчик при использовании Visual Studio. Это, возможно, будет решено в будущей версии SFML.

Событие движения колесика мыши (The MouseWheelMoved event)

Событие sf::Event::MouseWheelMoved срабатывает, когда колесико мыши прокручивается вперед или назад.
Участник(member), соответствующий этому событию - event.mouseWheel, он содержит количество тиков перемещения колесика мыши, а также текущее положение курсора мыши.
if (event.type == sf::Event::MouseWheelMoved)
{
    std::cout << "wheel movement: " << event.mouseWheel.delta << std::endl;
    std::cout << "mouse x: " << event.mouseWheel.x << std::endl;
    std::cout << "mouse y: " << event.mouseWheel.y << std::endl;
}

Событие нажатия и отпускания кнопки мыши (The MouseButtonPressed and MouseButtonReleased events)

События sf::Event::MouseButtonPressed и sf::Event::MouseButtonReleased срабатывают, когда кнопка мыши нажата\отпущена.
SFML поддерживает 5 кнопок мыши : левую, правую, среднюю (колесико), дополнительную 1 и дополнительную 2 (боковые кнопки).
Участник(member), соответствующий этому событию - event.mouseButton, он содержит код нажатой\отпущенной кнопки, а также текущее положение курсора мыши.
if (event.type == sf::Event::MouseButtonPressed)
{
    if (event.mouseButton.button == sf::Mouse::Right)
    {
        std::cout << "the right button was pressed" << std::endl;
        std::cout << "mouse x: " << event.mouseButton.x << std::endl;
        std::cout << "mouse y: " << event.mouseButton.y << std::endl;
    }
}

Событие движения мыши (The MouseMoved event)

Событие sf::Event::MouseMoved срабатывает, когда курсор мыши двигается в пределах окна.
Это событие срабатывает даже когда окно не находится в фокусе. Однако, оно срабатывает только тогда, когда курсор мыши двигается внутри области окна, но не при движении за пределами названия окна или его границ.
Участник(member), соответствующий этому событию - event.mouseMove, он содержит текущее положение курсора мыши относительно окна.
if (event.type == sf::Event::MouseMoved)
{
    std::cout << "new mouse x: " << event.mouseMove.x << std::endl;
    std::cout << "new mouse y: " << event.mouseMove.y << std::endl;
}

Событие входа\выхода курсора мыши в пределы окна (The MouseEntered and MouseLeft event)

События sf::Event::MouseEntered и sf::Event::MouseLeft запускаются, когда курсор мыши входит\покидает пределы окна.
У этих событий нет соответствующего участника(member) в объединении(union) sf::Event.
if (event.type == sf::Event::MouseEntered)
    std::cout << "the mouse cursor has entered the window" << std::endl;

if (event.type == sf::Event::MouseLeft)
    std::cout << "the mouse cursor has left the window" << std::endl;

Событие нажатия и отпускания кнопок джойстика (The JoystickButtonPressed and JoystickButtonReleased events)

События sf::Event::JoystickButtonPressed и sf::Event::JoystickButtonReleased срабатывают, когда кнопки джойстика нажимаются\отпускаются пользователем.
SFML поддерживает до 8 джойстиков и до 32 кнопок.
Участник(member), соответствующий этому событию - event.joystickButton, он содержит идентификатор джойстика и индекс нажатой\отпущенной кнопки.
if (event.type == sf::Event::JoystickButtonPressed)
{
    std::cout << "joystick button pressed!" << std::endl;
    std::cout << "joystick id: " << event.joystickButton.joystickId << std::endl;
    std::cout << "button: " << event.joystickButton.button << std::endl;
}

Событие движения осей джойстика (The JoystickMoved event)

Событие sf::Event::JoystickMoved срабатывает, когда двигаются оси джойстика.
Оси джойстика обычно очень чувствительно, поэтому SFML использует порог отлова для избежания замусоривания цикла событий из-за большого количества событий JoystickMoved. Этот порог может быть изменен с помощью функции Window::setJoystickThreshold, если вы хотите получить больше или меньше событий движения осей джойстика.
SFML поддерживает 8 осей джойстика: X, Y, Z, R, U, V, POV X и POV Y. Как они отображаются на вашем джойстике зависит от драйвера.
Участник(member), соответствующий этому событию - event.joystickMove, он содержит идентификатор джойстика, имя оси и текущее положение (в промежутке от -100 до 100).
if (event.type == sf::Event::JoystickMoved)
{
    if (event.joystickMove.axis == sf::Joystick::X)
    {
        std::cout << "X axis moved!" << std::endl;
        std::cout << "joystick id: " << event.joystickMove.joystickId << std::endl;
        std::cout << "new position: " << event.joystickMove.position << std::endl;
    }
}

Событие подсоединения и отсоединения джойстика (The JoystickConnected and JoystickDisconnected events)

События sf::Event::JoystickConnected и sf::Event::JoystickDisconnected срабатывают, когда джойстик подключается\отключается.
Участник(member), соответствующий этому событию - event.joystickConnect, он содержит идентификатор подсоединенного\отсоединенного джойстика.
if (event.type == sf::Event::JoystickConnected)
    std::cout << "joystick connected: " << event.joystickConnect.joystickId << std::endl;

if (event.type == sf::Event::JoystickDisconnected)
    std::cout << "joystick disconnected: " << event.joystickConnect.joystickId << std::endl;

Просмотров: 4 614

ScorpioT1000 #1 - 3 года назад (отредактировано ) 2
чем Qt не угодил? кресты сейчас едва ли в моде)
alexprey #2 - 3 года назад 2
Перенес статью в раздел программирования
Kozinaka #3 - 3 года назад 2
ScorpioT1000:
кресты сейчас едва ли в моде)
Давно уже не в моде, но по-прежнему все жирные игры делают на них.
Мода не слишком быстро перетекает в реальную эксплуатацию.