Уроки · 24
Погоня за мышью
Теперь у нас есть всё нужное: «Расстояние» считает, далеко ли курсор, «Направление» превращает это в чистое “куда”, а «Точка плюс вектор» — в способ сдвинуть точку в эту сторону. Осталось повторять это на каждом кадре — и кружок сам побежит за мышью.
Вместо того чтобы телепортироваться прямо на курсор, кружок каждый
кадр делает один шаг фиксированной длины в его сторону. direction_x
и direction_y сами по себе — направление длиной ровно 1, тот самый
единичный вектор из урока «Направление», а длина 1 —
это один пиксель за кадр, еле заметное движение. Чтобы шаг получился
побольше, направление нужно растянуть — умножить на нужную длину шага,
SPEED, тем же приёмом, что и в конце того же урока:
// Направление длиной 1, растянутое до длины SPEED, — и есть шаг.x += direction_x * SPEED;y += direction_y * SPEED;Как и в уроке «Движение», позиция (x, y) объявлена
переменной вне loop — она должна помнить, где кружок был на прошлом
кадре, а не начинать каждый кадр заново (см.
«Где живёт переменная»). А SPEED — наоборот: она одна
и та же на каждом кадре, поэтому это const вне main, как в уроке
«Константы».
Попробуй сам
Заголовок раздела «Попробуй сам»Кружок бежит за курсором с фиксированной скоростью. Двигай мышь по демке — если водить её резко, кружок может проскочить мимо и затрястись рядом, пока не подстроится: это ожидаемо, скорость-то не замедляется у цели. Поменяй скорость полем ниже:
use macroquad::prelude::*;
// Скорость одна и та же на каждом кадре, поэтому это константа// (как в уроке «Константы»), а не переменная внутри loop.const SPEED: f32 = 8.0;
#[macroquad::main("Погоня за мышью")]async fn main() { // Кружок стартует в центре экрана. mut и вне loop — позиция // должна помнить прошлый кадр, а не сбрасываться каждый раз. let mut x = screen_width() / 2.0; let mut y = screen_height() / 2.0;
loop { clear_background(BLACK);
// Координаты курсора. let (mouse_x, mouse_y) = mouse_position();
// Вектор от кружка к курсору. let dx = mouse_x - x; let dy = mouse_y - y; let distance = (dx * dx + dy * dy).sqrt();
// Курсор не точно на кружке — есть куда шагать. if distance > 0.0 { // Направление на курсор, длиной ровно 1. let direction_x = dx / distance; let direction_y = dy / distance;
// Точка + вектор: шаг фиксированной длины в эту сторону. x += direction_x * SPEED; y += direction_y * SPEED; }
draw_circle(x, y, 30.0, YELLOW);
next_frame().await; }}Что здесь происходит
Заголовок раздела «Что здесь происходит»- Каждый кадр кружок узнаёт направление на курсор — так же, как в
уроке «Направление», — а значит, оно уже длиной 1.
Умножение на
SPEEDрастягивает этот единичный шаг до нужного размера: без умножения кружок полз бы по одному пикселю за кадр. SPEEDзадаётся полем ниже. Она не меняется от кадра к кадру — поэтому этоconst, а не обычная переменная внутриloop.- Скорость фиксированная и не замедляется у цели: если курсор рядом, а шаг больше, чем оставшееся расстояние, кружок проскочит мимо. На следующем кадре направление развернётся в обратную сторону — кружок как бы трясётся вокруг курсора. Это ожидаемо: код нарочно простой, без плавного торможения.
- Резко дёрни мышь — кружок не успеет среагировать мгновенно, ему нужно несколько кадров, чтобы догнать курсор, каждый раз пересчитывая направление заново.