От переводчика: данная статья является девятой в цикле переводов официального руководства по библиотеке SFML. Прошлую статью можно найти тут. Данный цикл статей ставит своей целью предоставить людям, не знающим язык оригинала, возможность ознакомится с этой библиотекой. SFML — это простая и кроссплатформенная мультимедиа библиотека. SFML обеспечивает простой интерфейс для разработки игр и прочих мультимедийных приложений. Оригинальную статью можно найти тут. Посвящается Еве Вайт. С днем рождения;). Начнем.
1. Приступая к работе
- SFML и Visual Studio
- SFML и Code::Blocks (MinGW)
- SFML и Linux
- SFML и Xcode (Mac OS X)
- Компиляция SFML с помощью CMake
2. Модуль System
3. Модуль Window
- Открытие и управление окнами
- Обработка событий
- Работа с клавиатурой, мышью и джойстиками
- Использование OpenGL
4. Модуль Graphics
- Рисование 2D объектов
- Спрайты и текстуры
- Текст и шрифты
- Формы
- Проектирование ваших собственных объектов с помощью массивов вершин
- Позиция, вращение, масштаб: преобразование объектов
- Добавление специальных эффектов с шейдерами
- Контроль 2D камеры и вида
5. Модуль Audio
- Проигрывание звуков и музыки
- Запись аудио
- Пользовательские потоки аудио
- Спатиализация: звуки в 3D
6. Модуль Network
- Коммуникация с использованием сокетов
- Использование и расширение пакетов
- Веб-запросы с помощью HTTP
- Передача файлов с помощью FTP
Вступление
Эта статья объясняет, как открыть и управлять окном. Рисование в окне выходит за пределы задач, решаемых модулем sfml-window
; эту задачу решает модуль sfml-graphics
. Однако управление окном с использование модуля sfml-graphics
выполняется также, так что чтение данной статьи важно в любом случае.
Открытие окна
В SFML окна представлены классом sf::Window. Окно может быть создано напрямую из конструктора данного класса:
#include <SFML/Window.hpp>
int main()
{
sf::Window window(sf::VideoMode(800, 600), "My window");
...
return 0;
}
Первый аргумент — видео режим, определяет размер создаваемого окна (размер области окна, без заголовка окна и границ). Тут мы создали окно размером 800x600 пикселей.
Класс sf::VideoMode имеет интересные статические функции, с помощью которых можно получить разрешение рабочего стола или доступные видео режимы. Советуем посмотреть документацию по этому классу.
Второй аргумент — имя создаваемого окна.
Конструктор класса sf::Window может принять и третий аргумент: стиль, который позволяет вам выбрать, какие украшения и функции вы хотите использовать.
Вы можете использовать любую комбинацию следующих стилей:
sf::Style::None |
Никаких украшений (используется, например, для загрузочных экранов); этот стиль не может быть использован с другими |
sf::Style::Titlebar |
У окна есть заголовок |
sf::Style::Resize |
Можно изменить размер окна и появляется кнопка открытие на весь экран |
sf::Style::Close |
У окна появляется кнопка закрытия |
sf::Style::Fullscreen |
Окно открывается в полноэкранном режиме; этот стиль не может быть скомбинирован с другими и требует доступный видео режим |
sf::Style::Default |
Стандартный стиль, который является сокращение Titlebar | Resize | Close |
Также есть четвертый аргумент, определяющий специфичные опции OpenGL. Эта тема рассматривается более подробно в статье по OpenGL.
Если вы хотите создать окно после создания экземпляра sf::Window или создать окно заново, с другим видео режимом или названием, вы можете использовать функцию create
. Она принимает те же аргументы, что и конструктор класса.
#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 программы будут иметь схожую конструкцию; иногда этот цикл называется main loop или game loop.
Затем, первое, что мы хотим сделать внутри нашего цикла — проверить наличие необработанных событий. Обратите внимание, что мы используем цикл while
так, чтобы обработать все ожидающие обработки события. Функция pollEvent
возвращает true
, если есть события, ожидающие обработки, или false
, если их нет.
Всякий раз, когда происходит событие, мы должны проверить его тип (закрытие окна? нажатие клавиши? перемещение мыши? подключение джойстика? ...), и, если мы заинтересованы в этом, отреагировать соответствующим образом. Здесь мы проверяем только то, произошло ли событие Event::Closed
, которое срабатывает, если пользователь попытается закрыть окно. В этот момент окно еще открыто и мы должны закрыть его явным образом с помощью следующих функций. Это позволяет вам сделать что-то перед закрытием окна, например, сохранить текущее состояние приложения или отобразить сообщение.
Люди часто забывают о цикле обработки событий, потому что им не нужно обрабатывать события (вместо этого они используют ввод с клавиатуры). Без цикла обработки событий, окно перестанет отвечать на запросы. Важно помнить, что цикл обработки событий имеет две роли: в дополнение к обработке пользовательских событий, это еще и обработка внутренних событий окна, которые могут быть вызваны перемещением или изменением размера окна.
После того, как окно закрылось, основной цикл завершается и программа завершает свое выполнение.
В этот момент, вы, наверное, заметили, что мы не говорили о рисовании чего-то в окне. Как указывалось во введении, это не задача модуля sfml-window
. Рисование объектов (спрайтов, текста или фигур) будет обсуждаться в статьях по sfml-graphics
.
Чтобы что-то рисовать, вы также можете использовать OpenGL напрямую и игнорировать модуль sfml-graphics
. sf::Window создает OpenGL контекст и готова к принятию вызовов к 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.
В случае, если вам действительно нужны расширенный функционал для вашего окна, вы можете создать одно окно с помощью другой библиотекой и внедрить его в SFML. Чтобы это сделать, вы можете использовать другой конструктор или создать функцию, которая передает sf::Window дескриптор существующего окна. В этом случае, SFML будет создавать контекст рисования внутри переданного окна и отлавливать все события без вмешательства в родительский менеджер окна.
sf::WindowHandle handle = /* указание на то, что вы делаете и какую библиотеку вы используете */;
sf::Window window(handle);
Если вам нужна дополнительная, очень специфичная функция, вы можете сделать иначе: создать окно SFML, получить его дескриптор и сделать то, чего не позволяет SFML.
sf::Window window(sf::VideoMode(800, 600), "SFML window");
sf::WindowHandle handle = window.getSystemHandle();
// теперь вы можете использовать функции, специфичные для данной ОС
Интеграция SFML с другими библиотеками требует работы и не будет описана здесь, но вы можете обратиться к другим статьям, примерам или постам на форумах.
Контроль кадровой частоты
Иногда, когда приложение работает быстро, вы можете заметить визуальные артефакты. Причина в том, что частота обновления вашего приложения не синхронизирована с кадровой разверткой вашего монитора, и, как результат, нижняя часть предыдущего кадра смешивается с верхом следующего.
Решением этой проблемы является активизация вертикальной синхронизации. Синхронизация производится автоматически видеокартой и может быть просто включена или выключена с помощью функции setVerticalSyncEnabled
:
window.setVerticalSyncEnabled(true); // вызовите эту функцию единожды, после создания окна
После этого вызова, приложение будет выполняться с определенной частой, равной частоте обновления монитора.
Иногда вызов setVerticalSyncEnabled
не дает никакого эффекта: скорее всего, потому что вертикальная синхронизация выключена в настройках драйвера вашей видеокарты. Вы должны включить опцию «controlled by application» в настройках видеодрайвера.
Вы можете захотеть, чтобы ваше приложение работало на заданной кадровой частоте, вместо частоты обновления монитора. Это может быть достигнуто с помощью вызова setFramerateLimit
:
window.setFramerateLimit(60); // вызовите эту функцию единожды, после создания окна
В отличие от setVerticalSyncEnabled
, эта функция реализована SFML и использует комбинация sf::Clock и sf::sleep
. Важно отметить, что эта функция не является надежной на 100%, особенно для высокой кадровой частоты: возможности sf::sleep
зависят от ОС и оборудования; минимальное время приостановки составляет 10-15 миллисекунд. Не полагайтесь на эту функцию, если вы создаете приложение, сильно зависящее от времени.
Никогда не используйте setVerticalSyncEnabled
и setFramerateLimit
вместе. Это может вызвать плохие последствия.
Вещи, которые нужно знать об окнах
Ниже приведен список того, что вы можете и чего не можете делать с окнами SFML.
Вы можете создавать множество окон
SFML позволяет создавать множество окон, а так же обрабатывать их события и управлять ими в главном потоке или в множестве потоков (но… смотрите ниже). В этом случае, не забудьте создать цикл обработки событий для каждого из них.
Пока что, множество мониторов не поддерживается
SFML неявным образом управляет несколькими мониторами. По этой причине, вы не можете выбрать, на каком мониторе появится окно, и вы не можете создать более одного полноэкранного окна. Это должно быть исправлено в будущих версиях.
События окна должны быть обработаны в потоке окна
Это является серьезным сдерживающим фактором для большинства операционных систем: цикл обработки событий (точнее, функция pollEvent
или waitEvent
) должен вызываться в том же потоке, в котором было создано окно. Это означает, что если вы хотите создать выделенный поток для обработки событий, вы должны убедиться, что окно создается в том же потоке. Если вы действительно хотите разделить задачи между потоками, удобнее обрабатывать события в главном потоке, и производить операции с окнами (например, отрисовку графики) в отдельном потоке. Эта конфигурация также будет совместима с других ограничений, описанными ниже.
На Mac OS X обслуживание окон и обработка событий должны производиться в главном потоке
Ага, это правда. Mac OS X не позволит вам создать окно или обработать события в потоке, отличном от главного.
На Windows окно, размером больше рабочего стола, не обрабатывается корректно
По какой-то причине, Windows не позволяет созвать окна, размером больше, чем рабочий стол. Это затрагивает окна, созданные с помощью VideoMode::getDesktopMode()
: если вы будете использовать оформление окон (границы и заголовок), вы получите окно, которое будет немного больше, чем рабочий стол.
Следующая статья: Обработка событий.
Автор: HighMem