Уроки · 66
Ловушка индекса
Индекс уже встречался в уроке «Разноцветные кружки»
— это просто номер элемента по порядку, начиная с 0: COLORS[0] —
первый элемент, COLORS[1] — второй, и так далее. Тот же приём
работает и здесь: circles[0] — первый кружок в списке.
В уроке «Кружки исчезают» клик убирал кружок из
списка, и список пересобирался заново, без него. А что, если нужно не
просто убирать кружки, а ещё и запомнить один конкретный — скажем,
турель держит его на прицеле, и это должен быть именно он, кадр за
кадром, что бы ни происходило с остальными? Цвет и радиус каждого
кружка тут ни при чём, так что для этого урока хватит одного
фиксированного радиуса, одного цвета и обычных пар (f32, f32), как
в уроке «Список кружков» — без отдельной структуры Circle.
Самый очевидный способ запомнить один конкретный кружок — запомнить его индекс:
// Кружок, который запомнили, — просто его номер в списке, четвёртый по счёту.const TARGET_INDEX: usize = 3;Подписываем кружки и обводим нужный
Заголовок раздела «Подписываем кружки и обводим нужный»Чтобы сразу видеть, у какого кружка какой индекс, подпишем каждый
его номером — тем же draw_text, что и в уроке «Текст на
экране», переведя число в строку через
.to_string(), как в уроке «Строки». Перед
i.to_string() стоит & — та же ссылка, что и в уроке «Список
кружков»: draw_text только читает строку, чтобы показать
её на экране, а не забирает её себе навсегда, поэтому передаём не саму
строку, а ссылку на неё. Заодно, прямо в этом же цикле, обведём
кольцом кружок, который запомнили, как только дойдём до его индекса:
// Рисуем каждый кружок, подписываем его индексом и, если это и есть// TARGET_INDEX, тут же обводим его кольцом.for i in 0..circles.len() { let (x, y) = circles[i]; draw_circle(x, y, RADIUS, YELLOW); draw_text(&i.to_string(), x, y, 128.0, BLACK);
if i == TARGET_INDEX { draw_circle_lines(x, y, RADIUS + 10.0, 4.0, WHITE); }}i пробегает только существующие индексы, 0..circles.len() — так
что если кружков станет меньше, чем TARGET_INDEX, условие i == TARGET_INDEX просто ни разу не сработает за весь цикл, и кольцо не
нарисуется.
Замени содержимое src/main.rs на это:
use macroquad::prelude::*;
// Радиус один и тот же у всех кружков — фиксированный, как в уроке «Константы».const RADIUS: f32 = 84.0;
// Сколько кружков рисуем и как их расставить в ряд — те же START_X и STEP,// что и в уроке «Цикл for».const COUNT: usize = 10;const START_X: f32 = 100.0;const STEP: f32 = 228.0;
// Своя функция: расстояние между двумя точками, как в уроке «Расстояние».fn distance(from_x: f32, from_y: f32, to_x: f32, to_y: f32) -> f32 { let dx = to_x - from_x; let dy = to_y - from_y; (dx * dx + dy * dy).sqrt()}
// Кружок, который запомнили, — просто его номер в списке, четвёртый по счёту.const TARGET_INDEX: usize = 3;
#[macroquad::main("Ловушка индекса")]async fn main() { // Начальный набор кружков — ровный ряд, одного радиуса и цвета. let mut circles: Vec<(f32, f32)> = Vec::new(); for i in 0..COUNT { circles.push((START_X + STEP * (i as f32), screen_height() / 2.0)); }
loop { clear_background(BLACK);
// Клик только что случился — убираем кружки под курсором. if is_mouse_button_pressed(MouseButton::Left) { let (mouse_x, mouse_y) = mouse_position();
// Новый пустой список — специально для кружков, которые стоит оставить. let mut kept_circles: Vec<(f32, f32)> = Vec::new();
// Забираем старый список circles в собственность. for (x, y) in circles { let clicked_inside = distance(mouse_x, mouse_y, x, y) < RADIUS;
if !clicked_inside { kept_circles.push((x, y)); } }
// На этот кадр circles становится тем, что мы только что собрали. circles = kept_circles; }
// Рисуем каждый кружок, подписываем его индексом и, если это и есть // TARGET_INDEX, тут же обводим его кольцом. for i in 0..circles.len() { let (x, y) = circles[i]; draw_circle(x, y, RADIUS, YELLOW); draw_text(&i.to_string(), x, y, 128.0, BLACK);
if i == TARGET_INDEX { draw_circle_lines(x, y, RADIUS + 10.0, 4.0, WHITE); } }
next_frame().await; }}Запусти как обычно:
cargo runОдин из кружков обведён белым кольцом — это и есть TARGET_INDEX, и
число на нём совпадает с номером кольца. Кликни по другому
кружку, который на экране левее него (то есть раньше попал в
список), чтобы убрать его, и посмотри, что станет с кольцом:
Сами кружки при этом никуда не двигаются — координаты оставшихся не меняются ни на пиксель, они рисуются там же, где и были. Меняется только то, под каким номером каждый из них лежит в списке: когда один кружок пропадает, список пересобирается без него, и все, кто шёл после, сдвигаются на одно место вперёд, чтобы не осталось дырки. А значит, у каждого из них индекс становится на единицу меньше.
Кольцо же каждый кадр рисуется не вокруг конкретного кружка, а вокруг
того, кто сейчас лежит в списке под номером TARGET_INDEX. Кружки
стоят на месте, а вот то, что считается «третьим», меняется — поэтому
кольцо и перескакивает на другой кружок, хотя сам он никуда не
двигался.
Что здесь происходит
Заголовок раздела «Что здесь происходит»for i in 0..circles.len()перебирает позиции списка, а не сами кружки — поэтому на каждом шаге под рукой и кружок (circles[i]), и его индекс (i).- Число на кружке — то же
i, переведённое в строку через.to_string(), как в уроке «Строки». - Проверка и кольцо встроены прямо в цикл отрисовки:
iпробегает только существующие индексы, и если среди них нетTARGET_INDEX, кольцо просто не нарисуется.
Индекс — это место в списке, а не личность кружка. В уроке «Идентификатор кружка» разберёмся, как запоминать именно кружок, а не его текущую позицию.