Перейти к содержимому

Уроки · 43

Прямоугольные кнопки

В уроке «Круглые кнопки» попадание мыши в круг проверялось одной формулой — расстояние меньше радиуса. С прямоугольником так не выйдет: у него нет одного числа вроде радиуса, которое отвечало бы сразу за форму целиком. Нужно проверить x и y мыши отдельно.

Раньше struct Circle описывала круг. Здесь нужен новый тип — специально для прямоугольников, со своими полями:

struct Rectangle {
x: f32,
y: f32,
width: f32,
height: f32,
color: Color,
}

x, y — левый верхний угол, width и height — размер, всё как в уроке «Прямоугольник», только теперь это не отдельные переменные, а поля одной структуры — так же, как раньше x, y и radius были полями Circle.

Мышь внутри прямоугольника, если одновременно верны четыре вещи: её x не левее левого края, её x не правее правого края, её y не выше верхнего края, её y не ниже нижнего края. Для этого нужны два новых знака сравнения, >= и <= — то же самое “больше” и “меньше” из урока «Радиус обнаружения», только включающее ещё и равенство: “больше или равно” и “меньше или равно”. Мышь на самом краю прямоугольника должна по-прежнему считаться внутри, а не снаружи — поэтому именно >=/<=, а не строгие >/<. Каждую из четырёх проверок можно сохранить в свою переменную:

let after_left = mouse_x >= button.x;
let before_right = mouse_x <= button.x + button.width;
let after_top = mouse_y >= button.y;
let before_bottom = mouse_y <= button.y + button.height;
let inside = after_left && before_right && after_top && before_bottom;

&& здесь связывает не два условия, а сразу четыре — работает ровно так же, как в уроке «Круглые кнопки», просто цепочка подлиннее: результат — true, только если верны все четыре, а не только первые два.

Раз каждая переменная используется всего один раз, промежуточные имена можно и не заводить — то же самое условие целиком:

let inside = mouse_x >= button.x
&& mouse_x <= button.x + button.width
&& mouse_y >= button.y
&& mouse_y <= button.y + button.height;

Логика та же самая, просто без лишних имён по дороге — именно так это и будет в коде ниже.

Это работает только для прямоугольников, стороны которых идут строго по горизонтали и вертикали экрана, не повёрнутых ни на какой угол. Стоит прямоугольник повернуть — проверка перестанет работать, но с поворотами мы пока не знакомы, это тема для другого урока.

Замени содержимое src/main.rs на это:

src/main.rs
use macroquad::prelude::*;
// Один прямоугольник-кнопка: положение, размер и обычный цвет.
struct Rectangle {
x: f32,
y: f32,
width: f32,
height: f32,
color: Color,
}
#[macroquad::main("Прямоугольные кнопки")]
async fn main() {
loop {
clear_background(BLACK);
let w = screen_width();
let h = screen_height();
// Четыре кнопки в ряд — у каждой своя доля ширины экрана и своя ширина.
let buttons = [
Rectangle { x: w * 0.05, y: h * 0.35, width: w * 0.15, height: h * 0.3, color: YELLOW },
Rectangle { x: w * 0.28, y: h * 0.35, width: w * 0.22, height: h * 0.3, color: RED },
Rectangle { x: w * 0.58, y: h * 0.35, width: w * 0.15, height: h * 0.3, color: GREEN },
Rectangle { x: w * 0.80, y: h * 0.35, width: w * 0.18, height: h * 0.3, color: PURPLE },
];
let (mouse_x, mouse_y) = mouse_position();
// Кнопка мыши зажата прямо сейчас.
let mouse_down = is_mouse_button_down(MouseButton::Left);
for button in &buttons {
// Мышь внутри прямоугольника по обеим осям сразу.
let inside = mouse_x >= button.x
&& mouse_x <= button.x + button.width
&& mouse_y >= button.y
&& mouse_y <= button.y + button.height;
// Кнопка нажата, только если мышь зажата и попадает внутрь.
let pressed = mouse_down && inside;
if pressed {
draw_rectangle(button.x, button.y, button.width, button.height, BLUE);
} else {
draw_rectangle(button.x, button.y, button.width, button.height, button.color);
}
}
next_frame().await;
}
}

Запусти как обычно:

Окно терминала
cargo run

Зажми кнопку мыши над любой из четырёх кнопок — она посинеет. Отпусти — вернётся к своему цвету. Попробуй прямо здесь:

  • struct Rectangle { x, y, width, height, color } — свой тип, описывающий именно прямоугольник, отдельный от Circle: разные формы — разные наборы полей.
  • mouse_x >= button.x && mouse_x <= button.x + button.width — проверка по горизонтали: мышь не левее и не правее прямоугольника.
  • mouse_y >= button.y && mouse_y <= button.y + button.height — та же проверка по вертикали.
  • Все четыре условия соединены через && в одно: inside становится true, только если мышь проходит проверку и по x, и по y одновременно — а по каждой оси ещё и сразу с двух сторон.
  • pressed = mouse_down && inside — то же самое объединение, что и в уроке «Круглые кнопки»: нажатой кнопка считается, только если мышь и зажата, и находится внутри.