§4 Обработка событий

Обработка событий мыши

Библиотека содержит специальный класс sf::Mouse. Этот класс позволяет запросить напрямую текущее состояние мыши, не связывая это состояние с самим окном или его событиями. Для изменения или получения текущей позиции указателя мыши используются функции setPosition и getPosition. Функция обеспечивающая обработку события нажатия кнопки мыши:

static bool isButtonPressed (Button button)

где button – одна из нажатых клавиш: Left, Right, Middle (возможна также обработка дополнительных клавиш).
Помните нашего Тукса с предыдущего урока? Используя эти функции заставим Тукса появиться в указанной кликом мыши новой позиции. Заметим, что удерживая левую клавишу мыши мы можем перетащить Тукса куда захотим.
Программа sf.4.1

#include <SFML/Graphics.hpp>
using namespace sf;
int main() {
    RenderWindow window(VideoMode(500, 500), "lesson-4-1");
    Texture texture;
    if (!texture.loadFromFile("./pic/tux.png"))
        return -1;

    Sprite sprite;
    sprite.setTexture(texture);

    while (window.isOpen()) {
        Event event;
        while (window.pollEvent(event)) {
            if (event.type == Event::Closed)
                window.close();
        }
        if (Mouse::isButtonPressed(Mouse::Left)) {
        	sprite.setPosition(Mouse::getPosition(window).x,
        					   Mouse::getPosition(window).y);
        }
        window.clear();
        window.draw(sprite);
        window.display();
    }
    return 0;
}

Чтобы сделать перемещение Тукса анимированным необходимо использовать таймер. В следующей программе Тукс перемещается к указанной (левым кликом мыши) точке с некоторой скоростью, а не “мгновенно”.
Программа sf.4.2

#include <SFML/Graphics.hpp>
#include <cmath>
using namespace sf;
int main() {
    RenderWindow window(VideoMode(500, 500), "lesson-4-2");
    Texture texture;
    if (!texture.loadFromFile("./pic/tux.png"))
        return -1;

    Sprite sprite;
    sprite.setTexture(texture);
    Clock clock;
    int X0 {}, Y0 {}, X, Y;
    float speed = 1000.f;
    float distance;
    bool mspr = false;
    sprite.setPosition(X0, Y0);

    while (window.isOpen()) {
    	float delta = clock.getElapsedTime().asMicroseconds();
		clock.restart();
		delta = delta / speed;
        Event event;
        if (Mouse::isButtonPressed(Mouse::Left)) {
        	Vector2i pos = Mouse::getPosition(window);
            X = pos.x;
            Y = pos.y;
            distance = hypot(X-X0, Y-Y0);
            mspr = true;
        }
        while (window.pollEvent(event)) {
            if (event.type == Event::Closed)
                window.close();
        }
        if (mspr) {
        	X0 += delta * (X - X0) / distance;
        	Y0 += delta * (Y - Y0) / distance;
        	sprite.setPosition(X0, Y0);
        }
        window.clear();
        window.draw(sprite);
        window.display();
    }
    return 0;
}

Если нажата клавиша мыши, то мы определяем координаты нажатия – X и Y, и длину пути – distance (по теореме Пифагора с помощью функции hypot библиотеки cmath). Анимация будет осуществляться путем приращения к начальным координатам длин отрезков, которые определяются с помощью временного интервала и несложной геометрии треугольника. Поскольку окончательного обнуления приращения произойти не может, то в конце пути он будет вести себя “неадекватно”. Попробуйте исправить эту ситуацию самостоятельно.

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

Для обработки событий клавиатуры библиотека содержит класс sf::Keyboard. Метод проверяющий состояние клавиш:

static bool isKeyPressed (Key key)

где key – имя клавиши.
В следующей программе мы будем перемещать спрайт (стрелка) в горизонтальном (используемые клавиши Left и Right) и вертикальном направлении (используемые клавиши Up и Down). При этом стрелка должна поворачиваться в направлении (по умолчанию стрелка направлена влево) движения спрайта.
Программа sf.4.3

#include <SFML/Graphics.hpp>
using namespace sf;
int main() {
    RenderWindow window(VideoMode(500, 500), "lesson-4-3");
    Texture texture1;
    Texture texture2;
    Texture texture3;
    Texture texture4;
    if (!texture1.loadFromFile("./pic/arrow1.png") ||
    	!texture2.loadFromFile("./pic/arrow2.png") ||
		!texture3.loadFromFile("./pic/arrow3.png") ||
		!texture4.loadFromFile("./pic/arrow4.png"))
        return -1;

    Sprite sprite;
    sprite.setTexture(texture1);
    int X0 {200}, Y0 {200};
    float speed = 8.f;
    sprite.setPosition(X0, Y0);

    while (window.isOpen()) {
        Event event;

        while (window.pollEvent(event)) {
            if (event.type == Event::Closed)
                window.close();
        }

		if (Keyboard::isKeyPressed(Keyboard::Left)) {
			sprite.setTexture(texture1);
			sprite.move(-0.1 * speed, 0);
		}
		if (Keyboard::isKeyPressed(Keyboard::Right)) {
			sprite.setTexture(texture2);
			sprite.move(0.1 * speed, 0);
		}
		if (Keyboard::isKeyPressed(Keyboard::Up)) {
			sprite.setTexture(texture3);
			sprite.move(0, -0.1 * speed);
		}
		if (Keyboard::isKeyPressed(Keyboard::Down)) {
			sprite.setTexture(texture4);
			sprite.move(0, 0.1 * speed);
		}
		window.clear();
		window.draw(sprite);
        window.display();
    }
    return 0;
}

Файлы стрелок:
Down, Up, Left, Right
В этой программе используются четыре разных изображения. Но SFML имеет все инструменты, чтобы ограничится одной картинкой. Составьте программу, в которой используется только один файл со стрелками и проконтролируйте выход за пределы окна.

Чтобы не использовать четыре картинки, их нужно объединить в одну и выводить только нужный фрагмент. Этого можно добиться, если использовать класс sf::Rect: sprite.setTextureRect(IntRect(X,Y,W,H))

Класс sf::Event

Рассмотренные выше обработчики событий использовались нами вне связи с окном программы. Класс sf::Event содержит всю информацию о системном событии, которое только что произошло, но теперь события извлекаются с использованием функций sf::Window::pollEvent и sf::Window::waitEvent. Экземпляр класса sf::Event содержит тип события (мышь перемещена, клавиша нажата, окно закрыто и т. п.), а также сведения об этом конкретном событии. Следует обратить внимание на то, что параметры событий определены в объединении (union). В результате, только один член (соответствующий типу события) будет заполнен должным образом. Все остальные члены будут иметь неопределенные значения.
Составим программу в которой будут обрабатываться события нажатия на клавишу мыши и клавишу клавиатуры. Выведем нашу пульсирующую окружность в место, где был осуществлен левый клик мышью. Закроем окно программы по нажатию клавиши Escape. Вынесем код прорисовки окружности в функцию drawCir().
Программа sf.4.4

#include  <SFML/Graphics.hpp>
using namespace sf;

// Функция рисования пульсирующей окружности
void drawCir(RenderWindow &win, CircleShape &Cir, Clock &clk,
			bool &w, float &max, float &min, float &sp) {
	Cir.setOutlineColor(Color(206, 24, 33, 255));
	Cir.setFillColor(Color(255, 194, 14, 255));
	float r = Cir.getRadius();
	if (r > max) { w = 0; clk.restart(); }
	if (r < min) { w = 1; clk.restart(); }
	float delta = clk.getElapsedTime().asMicroseconds();
	float R = delta / sp;
	if (w) {
		Cir.setRadius(min + R);
		Cir.setOutlineThickness(3 + R * 0.1);
	} else {
		Cir.setRadius(max - R);
		Cir.setOutlineThickness(5 - R * 0.1);
	}
	Cir.setOrigin(r, r);
	win.draw(Cir);
}

int main() {
	int W = 500;
	int H = 500;
	RenderWindow window(VideoMode(W, H), "lesson-4-4");
	CircleShape Pls;
	Clock clock;
	int X, Y;
	float speed = 20000.f;
	bool way {};
	float Max = 15;
	float Min = 3;

    while (window.isOpen()) {
    	Event event;
    	// пока есть события, будем их обрабатывать
        while (window.pollEvent(event)) {
            if (event.type == Event::Closed)
                window.close();
            // обнаружили, что была нажата клавиша мыши
            if (event.type == Event::MouseButtonPressed)
            	// определяем какая именно
            	if (event.mouseButton.button == Mouse::Left) {
            		// сохраняем координаты клика
            		X = Mouse::getPosition(window).x;
            		Y = Mouse::getPosition(window).y;
                }
            if (event.type == Event::KeyPressed)
            	if (event.key.code == Keyboard::Escape)
            		window.close();
        }
        window.clear();
        Pls.setPosition(X, Y);
        drawCir(window, Pls, clock, way, Max, Min, speed);
        window.display();
    }
    return 0;
}
В программе 3.3 мы рассчитывали позицию “в ручную”, но SFML содержит метод setOrigin(), который позволяет этот процесс автоматизировать и установить спрайт относительно левого верхнего угла на расстоянии, которое указывается в параметрах. В данном случае, относительно центра спрайта.

Обработчики событий можно группировать с помощью switch:

switch (event.type) {
	case sf::Event::Closed:
		window.close(); 
		break;
	case sf::Event::KeyPressed:
		// Инструкции
		break;
	// Другие типы событий не обрабатываются
	default:
		break;
}

Полный список обработчиков событий можно посмотреть на странице документации.

Задания для самостоятельной работы

  • Создайте программу, в которой окружность прорисовывается в месте клика мыши, а перемещается с помощью клавиш клавиатуры.
  • Измените программу 2.4 так, чтобы по клику мыши (в произвольной позиции окна программы) гистограмма была бы перерисована, при этом высоты столбов и их цвет должны изменяться
Оцените материал
1 Звезда2 Звезды3 Звезды4 Звезды5 Звезд (2 оценок, среднее: 5,00 из 5)
Загрузка...

Print Friendly, PDF & Email

Comments are closed.