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

Уроки · 63

Зона обнаружения

В уроке «Радиус обнаружения» турель смотрела только на расстояние. В уроке «Сектор обнаружения» — только на угол, а сам сектор был неограничен по дальности, уходил за край экрана. На деле обычно нужно и то, и другое сразу: игрок обнаружен, если он и достаточно близко, и в пределах нужного направления.

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

let inside = distance < radius && relative <= sector_span;

distance и radius — из урока «Радиус обнаружения», relative и sector_span — из урока «Сектор обнаружения». Турель срабатывает, только когда курсор проходит обе проверки одновременно.

Раз сектор теперь ограничен дальностью, длинные лучи за край экрана больше ни к чему — зону обнаружения можно нарисовать как она есть: кусок круга радиусом radius. Как раз для этого в уроке «Дуга» и разбирали draw_arc:

draw_arc(
center_x,
center_y,
60,
radius,
sector_start.to_degrees(),
3.0,
sector_span.to_degrees(),
GRAY,
);

Это единственное место во всём уроке, где нужен перевод в градусы — draw_arc принимает только их, как и предупреждали в уроке «Дуга», хотя весь остальной код здесь по-прежнему в радианах.

Самой дуги мало — получится только изогнутый край клина, без боковых сторон. Их дорисовываем двумя обычными draw_line, от центра до каждого конца дуги, длиной ровно radius:

let start_x = center_x + sector_start.cos() * radius;
let start_y = center_y + sector_start.sin() * radius;
draw_line(center_x, center_y, start_x, start_y, 3.0, GRAY);
let end_x = center_x + sector_end.cos() * radius;
let end_y = center_y + sector_end.sin() * radius;
draw_line(center_x, center_y, end_x, end_y, 3.0, GRAY);

Дуга и два прямых отрезка вместе — ровно контур зоны обнаружения, в точности то, что проверяет inside.

Турель красная, только когда курсор одновременно и ближе radius, и внутри сектора между start и end (оба в радианах). Контур зоны — дуга и два прямых края. Подвигай поля:

src/main.rs
use macroquad::prelude::*;
use std::f32::consts::PI;
#[macroquad::main("Зона обнаружения")]
async fn main() {
loop {
clear_background(BLACK);
let radius = 250.0;
// Первый луч сектора — начало.
let sector_start = 1.57;
// Второй луч сектора — конец. От первого до второго — по
// часовой стрелке.
let sector_end = 3.14;
let center_x = screen_width() / 2.0;
let center_y = screen_height() / 2.0;
let (mouse_x, mouse_y) = mouse_position();
let dx = mouse_x - center_x;
let dy = mouse_y - center_y;
// Расстояние до курсора (как в уроке «Расстояние»).
let distance = (dx * dx + dy * dy).sqrt();
// Угол вектора направления к курсору, в радианах (как в уроке «Угол вектора»).
let mouse_angle = dy.atan2(dx);
// Сколько радиан по часовой стрелке от sector_start до
// sector_end — весь сектор целиком (как в уроке «Сектор
// обнаружения»).
let sector_span = (sector_end - sector_start).rem_euclid(2.0 * PI);
// Сколько радиан по часовой стрелке от sector_start до курсора.
let relative = (mouse_angle - sector_start).rem_euclid(2.0 * PI);
// Турель замечает курсор, только если он и ближе radius, и
// внутри сектора — оба условия сразу, как в «Круглых кнопках».
let inside = distance < radius && relative <= sector_span;
// Дуга — единственное место, где нужны градусы: draw_arc из
// урока «Дуга» принимает только их, хотя весь остальной код
// здесь в радианах.
draw_arc(
center_x,
center_y,
60,
radius,
sector_start.to_degrees(),
3.0,
sector_span.to_degrees(),
GRAY,
);
// Два прямых края сектора — от центра до концов дуги, длиной
// ровно radius, замыкают контур с боков.
let start_x = center_x + sector_start.cos() * radius;
let start_y = center_y + sector_start.sin() * radius;
draw_line(center_x, center_y, start_x, start_y, 3.0, GRAY);
let end_x = center_x + sector_end.cos() * radius;
let end_y = center_y + sector_end.sin() * radius;
draw_line(center_x, center_y, end_x, end_y, 3.0, GRAY);
// Турель красная, если курсор внутри зоны, иначе синяя.
if inside {
draw_circle(center_x, center_y, 40.0, RED);
} else {
draw_circle(center_x, center_y, 40.0, BLUE);
}
// Игрок — кружок под курсором.
draw_circle(mouse_x, mouse_y, 25.0, WHITE);
next_frame().await;
}
}
  • inside = distance < radius && relative <= sector_span — турель срабатывает, только если курсор ближе радиуса и внутри сектора одновременно.
  • draw_arc(...) рисует изогнутый край зоны — единственное место, где понадобился .to_degrees(), потому что сама функция работает только с градусами.
  • Два draw_line — прямые края зоны, от центра до концов дуги, длиной radius: замыкают контур с боков.
  • Турель красная внутри зоны, синяя снаружи — та же идея, что и в уроках «Радиус обнаружения» и «Сектор обнаружения».