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

Уроки · 22

Направление

В уроке «Расстояние» dx и dy были не только строительным материалом для формулы расстояния — вместе они ещё и показывают направление от центра к курсору. Проблема в том, что размер этих чисел зависит от того, как далеко мышь: dx и dy растут вместе с расстоянием, хотя направление может быть тем же самым.

Если разделить dx и dy на distance — то есть на их же собственную длину — получится пара чисел, которая показывает только направление, а её длина всегда равна ровно 1, независимо от того, где была мышь. Такое деление называют нормированием, а получившуюся пару — нормированной: направление есть, а лишней длины уже нет.

// Делим каждую составляющую на длину — получаем направление длиной 1.
let direction_x = dx / distance;
let direction_y = dy / distance;

Дальше это направление можно растянуть до любой нужной длины — умножение на число увеличивает длину ровно во столько же раз: умножил на 10 — длина стала в 10 раз больше. В этом и ценность единичного вектора: раз его собственная длина ровно 1, а единица, умноженная на любое число, даёт то же самое число, — умножив единичный вектор на 10, получаем вектор длиной ровно 10, а не просто “в 10 раз длиннее, чем было”.

В уроке «Расстояние» было неважно, что из чего вычитать — mouse_x - center_x или center_x - mouse_x дают одно и то же расстояние, потому что оба числа потом возводятся в квадрат и минус пропадает. А вот для направления порядок важен: dx = mouse_x - center_x — это направление от центра к мыши, а если вычесть в обратную сторону, получится направление от мыши к центру, ровно противоположное. Общее правило простое: из координат того, на что смотрим, вычитаем координаты того, кто смотрит.

Белая точка — неподвижный центр. Жёлтый луч из неё всегда смотрит на курсор, а вот его длина не зависит от того, далеко мышь или близко — она задаётся полем ниже:

src/main.rs
use macroquad::prelude::*;
// Длина одна и та же на каждом кадре, поэтому это константа
// (как в уроке «Константы»), а не переменная внутри loop.
const LENGTH: f32 = 200.0;
#[macroquad::main("Направление")]
async fn main() {
loop {
clear_background(BLACK);
// Неподвижный центр.
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();
// Нормированное направление — та же пара чисел, но длиной ровно 1.
let direction_x = dx / distance;
let direction_y = dy / distance;
// Растягиваем направление до нужной длины.
let end_x = center_x + direction_x * LENGTH;
let end_y = center_y + direction_y * LENGTH;
// Белая точка — неподвижный центр.
draw_circle(center_x, center_y, 10.0, WHITE);
// Жёлтый луч — направление на курсор, заданной длины.
draw_line(center_x, center_y, end_x, end_y, 4.0, YELLOW);
next_frame().await;
}
}
  • direction_x и direction_y — направление на курсор, но уже без информации о расстоянии: длина этой пары всегда 1.
  • direction_x * LENGTH и direction_y * LENGTH — растягивают это направление до длины LENGTH, какой бы она ни была. LENGTH — это const вне main, а не переменная внутри loop — как в уроке «Константы», её значение одно и то же на каждом кадре.
  • center_x + ... и center_y + ... — сдвигают растянутое направление от центра, чтобы получить точку, где заканчивается луч (тот же приём сложения, что и в уроке «Смещение»).