Программирование: Создание и управление окном в SFML

» Раздел: C++

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

Создание и управление окном в SFML

Введение

Эта стать описывает,каким образом создавать и управлять окном. Рисование разных вещей находится вне модуля window - оно относится к модулю sfml-graphics. Однако рисовать что-либо без управления окном нельзя и прочтение этой статьи остается обязательным в любом случае.

Создание окна

Окна в SFML определяются в классе sf::Window. Окно может быть создано и открыто следующей конструкцией:
#include <SFML/Window.hpp>

int main()
{
    sf::Window window(sf::VideoMode(800, 600), "My window");

    ...

    return 0;
}
Первый аргумент - video mode, определяет размер окна(внутренний размер,без границ и названия окна). Здесь мы создаем окно 800х600 пикселей.
Класс sf::VideoMode имеет некоторые интересные статичные функции для определения разрешения рабочего стала или списка доступных видео-режимов для полно-экранного режима. Не стесняйтесь почитать об этом в документации.
Второй аргумент - это просто имя для нашего окна.
Конструктор окна поддерживает и третий опциональный аргумент - стиль, который позволяет выбрать какое оформление и возможности для окна вы хотите. Вы можете выбирать любую комбинацию из представленных стилей:
sf::Style::NoneНикакого оформления вообще (используется для заставок, например); этот стиль не может быть скомбинирован с остальными
sf::Style::TitlebarОкно имеет название
sf::Style::ResizeРазмер окна может быть изменен и оно имеет кнопку Развернуть
sf::Style::CloseОкно имеет кнопку Закрыть
sf::Style::FullscreenОкно отображается в полно-экранном режиме; этот стиль не может быть скомбинирован с остальными и требует подходящий video mode
sf::Style::DefaultСтиль по-умолчанию, который совмещает в себе Titlebar\Resize\Close
Также имеется четвертый опциональный аргумент, который определяет специальные опции OpenGL, которые рассматриваются в соответствующей статье.
Если вы хотите показать окно отдельно после его создания или пересоздать с другим video mode или названием, вы можете вызвать функцию отдельно; она принимает такие же аргументы, как и конструктор класса окна.
#include <SFML/Window.hpp>

int main()
{
    sf::Window window;
    window.create(sf::VideoMode(800, 600), "My window");

    ...

    return 0;
}

Придаем жизнь нашему окну

Если вы попробуете запустить код без добавления чего-либо в место где у нас стоит "...", вам будет сложно что-то увидеть. Во-первых, потому что программа заканчивает свое выполнение мгновенно. Во-вторых, потому что у нас нету никакого управления событиями - поэтому если вы добавите бесконечный цикл к этому коду, вы увидите черное окно, которое нельзя и ни переместить, ни масштабировать, ни закрыть.
Поэтому давайте добавим небольшой код, который сделает нашу программу более интересной:
#include <SFML/Window.hpp>

int main()
{
    sf::Window window(sf::VideoMode(800, 600), "My window");

    // программа работает сколь угодно долго,пока открыто наше окно
    while (window.isOpen())
    {
        // проверяем все события окна,которые были запущены после последней итерации цикла
        sf::Event event;
        while (window.pollEvent(event))
        {
            // если произошло событие Закрытие,закрываем наше окно
            if (event.type == sf::Event::Closed)
                window.close();
        }
    }

    return 0;
}
Представленный код создает и открывает окно, уничтожая его, когда пользователь закрывает его. Давайте разберем боле детально, как это работает.
Во-первых, мы добавляем цикл, который гарантирует, что приложение будет обновлено, пока окно не закрыто. Многие(но не все) программы на SFML имеют этот цикл, который иногда называют игровым или главным.
Далее, первая вещь, которую мы должны сделать внутри нашего игрового цикла - это проверить события, которые были запущены. Обратите внимание, что таким образом мы обрабатываем все события, даже если их несколько в очереди. Функция pollEvent возвращает true, если событие в ожидании, или false, если нету событий.
Теперь, когда мы получили событие,мы должны проверить его тип (окно закрыто? нажата клавиша? передвинут курсор? подключен джойстик?...) и реагировать соответственно, если требуется. В нашем коде мы реагируем только на событие Event::Closed, которое запускается,когда пользователь хочет закрыть окно. В этот момент окно все еще открыто и мы закрываем его явно с помощью соответствующей функции. Это дает возможность что-либо сделать перед тем, как окно будет закрыто, например сохранить текущее состояние приложения, или показать сообщение.
Частая ошибка, которую допускают люди - это забывают о цикле событий, потому что они не беспокоятся об управлении событиями(используют ввод в реальном времени). Но без цикла событий, окно не будет отзывчивым; действительно, цикл событий имеет два назначения - вдобавок к обеспечению управления событиями для пользователя, это дает окну возможность обработать свои внутренние события, что требуется, чтобы окно могло реагировать на перемещение или изменения размера.
После этого окно будет закрыто, главный цикл завершен и приложение выполнено.
Вы возможно заметили, что мы пока не говорим о рисовании чего-либо в окне. Как написано в Введении, рисование не относится к модулю sfml-window и вы можете перейти сразу к статьям о модуле sfml-graphics, если вы хотите отобразить спрайты,текст или фигуры.
Для рисования вы также можете использовать напрямую OpenGL и полностью игнорировать модуль sfml-graphics. sf::Window внутренне создает контекст OpenGL и готов дать вам возможность вызывать функции OpenGL. Вы можете узнать поподробнее об этом в соответствующей статье.
Поэтому не ожидайте увидеть что-либо интересное в этом окне - вы можете увидеть цвет заливки (черный или белый), или что-то, что было отображено ранее в приложении, используя OpenGL, или...что-нибудь еще.

Играемся с окном

Конечно же, SFML дает вам возможность немного поиграть с вашими окнами. Базовые функции для окна,такие как изменение его размера, позиции, названия или иконки, но в отличие от специализированных GUI библиотек (Qt, wxWidgets), SFML не дает дополнительных возможностей. Окна SFML только дают основу для рисования с помощью OpenGL или самого SFML.
Ниже приведены функции управления окном:
// изменяет позиции окна(относительно рабочего стола)
window.setPosition(sf::Vector2i(10, 50));

// изменяет размер окна
window.setSize(sf::Vector2u(640, 480));

// изменяет название окна
window.setTitle("SFML window");

// получаем размер окна
sf::Vector2u size = window.getSize();
unsigned int width = size.x;
unsigned int height = size.y;

...
Вы можете обратиться к API документации для полного списка функций sf::Window.
В случае, если вам действительно нужны дополнительный возможности для вашего окна, вы можете создать GUI, используя другую библиотеку, и встроить SFML в неё. Чтобы так сделать, вам придется использовать другой конструктор, или создать функцию для sf::Window, которая примет специфичный для операционной системы handle окна. В этом случае, SFML создаст контекст рисования внутри переданного окна и будет управлять всеми его событиями, не нарушая изначальное управление окном.
sf::WindowHandle handle = /* отдельно для того что вы делаете и для библиотеки, которую вы используете */;
sf::Window window(handle);
Если вы просто хотите дополнительную, очень специфичную возможность, вы можете также добавить её другим путем: создать окно SFML, получить специфичный для операционной системы handle окна и имлементировать вещи, которые SFML не поддерживает.
sf::Window window(sf::VideoMode(800, 600), "SFML window");
sf::WindowHandle handle = window.getSystemHandle();

// теперь вы можете использовать handle со специфичными для операционной системы функциями
Интеграция SFML с другими библиотеками требует некоторых затрат и не будет описана здесь, но вы можете обсудить это в комментариях к статье.

Контролируем частоту кадров

Иногда, когда ваше приложение запускается быстро, вы можете заметить визуальные артефакты, такие как рывки при показе изображения. Причиной этому служит то, что скорость обновления вашего приложения не синхронизирована с вертикальной частотой вашего монитора, и, как результат, низ предыдущего кадра смешивается с верхом следующего.
Решение этой проблемы - использовать вертикальную синхронизацию. Это автоматически управляется видео-картой, и может быть легко включено или выключено с помощью функции setVerticalSyncEnabled:
window.setVerticalSyncEnabled(true); // запустите это один раз, после создания окна
После вызова этой функции, ваше приложение будет запускаться с частотой отображения монитора, зачастую это 60 кадров в секунду.
Иногда вызов setVerticalSyncEnable не дает никакого эффекта, это потому что вертикальная синхронизация выключена в настройках драйвера вашей видеокарты. В настройках драйвера установите в режим "выбирается приложением".
В других ситуациях, вы также можете установить, чтобы ваше приложение запускалось с установленной вами частотой кадров, отличной от частоты монитора. Этого можно достигнуть, используя функцию setFramerateLimit:
window.setFramerateLimit(30); // запустите это один раз, после создания окна
В отличие от setVerticalSyncEnabled, эта функция реализуется самим SFML, используя комбинацию sf::Clock и sf::sleep. Важным моментом является то, что эта функция не на 100% надежна, особенно для высокой частоты кадров: минимальное время для sf::sleep зависит от используемой операционной системы и может быть не ниже 10 или 15 миллисекунд. Не надейтесь на эту функцию в реализации точного контроля временем.
Никогда не используйте вместе setVerticalSyncEnabled и setFramerateLimit. Это сочетание ни к чему хорошему не приведет.
Вещи, которые следует знать об окнах
Ниже список того, что можно и нельзя делать с окнами в SFML.

Вы можете создавать несколько окон

SFML дает возможность создавать несколько окон, управлять ими всеми в главном потоке или в любом другом собственном для окна потоке.В этом случае не забудьте о цикле событий для каждого окна.

Несколько мониторов все еще не имеют корректной поддержки

SFML не управляет несколькими окнами. Как результат, вы не сможете выбрать, на каком мониторе будет отображено окно, и у вас не будет возможности создать более одного полно-экранного окна. Это должно быть улучшено в следующих версиях.

События должны быть обработаны в потоке окна

Это важное ограничение многих операционных систем: цикл событий должен быть вызван в том же потоке, в котором создано окно. Это значит что если вы захотите создать поток для управления событиями, вам нужно быть уверенным что окно будет создано в том же потоке. Если вы на самом деле имеете необходимость разделить вещи между потоками, удобнее будет хранить управление событиями в главном потоке, а остальное переместить в другие потоки (рендеринг, физику, логику...). Это решение будет также совместимо с остальными ограничениями, описанными ниже.

В OS X окна и события должны быть обработаны в главном потоке

Да, это правда.Mac OS X выдаст ошибку, если вы попробуете создать окно или обрабатывать события не в главном потоке.

В Windows окно, которое больше рабочего стола, не отображается корректно

По некоторым причинам Windows не любит окна, которые больше рабочего стола. Это включает в себя окна, созданные с помощью VideoMode::getDesktopMode(): вместе с добавленными границами и именем окна, вы получаете окно, которое немного больше чем рабочий стол.

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

5 комментариев удалено