Уроки · 55
Поворот и движение
В уроке «Движение» стрелки двигали кружок прямо по
осям экрана: влево-вправо трогали x, вверх-вниз — y. Сделаем
по-другому: пусть влево и вправо поворачивают игрока, а вверх и
вниз — двигают его вперёд или назад вдоль того направления, куда он
сейчас повёрнут, как машина или танк, а не свободно летающая точка.
Для этого мало помнить x и y — нужен ещё и угол поворота, он и
говорит, куда сейчас “вперёд”. Все три числа удобно держать вместе, в
своей структуре, как в уроке «Свои структуры», а
направление по этому углу получить методом, как в уроке
«Методы» — тем же самым cos/sin, что и в уроке
«Вращение»:
struct Player { x: f32, y: f32, angle: f32,}
impl Player { fn direction(&self) -> (f32, f32) { (self.angle.cos(), self.angle.sin()) }}Обрати внимание на &self, а не просто self: в main мы вызываем
player.direction(), а сразу следом ещё и меняем player.x,
player.y. Если бы direction забирала player в собственность,
player после вызова стал бы недоступен (как в уроке «Владение и
ссылки») — поменять его координаты уже не вышло бы.
Дальше всё просто: влево и вправо крутят angle, а вверх и вниз
двигают x и y вдоль того направления, которое этот угол сейчас
задаёт — вызвали direction() один раз за кадр и используем результат
дважды. И раз экран перевёрнут по y (как в уроке
«Координаты»), рост angle поворачивает игрока по
часовой стрелке, а не против, — та же деталь, что уже была в уроке
«Вращение».
Попробуй сам
Заголовок раздела «Попробуй сам»Стрелки влево/вправо поворачивают игрока, стрелки вверх/вниз двигают его вперёд и назад вдоль того направления, куда он сейчас повёрнут. Глазик показывает, куда он смотрит (клик по демо переносит на него фокус клавиатуры). Поменяй скорости полями ниже:
use macroquad::prelude::*;
// Игрок — положение и угол поворота вместе, как в уроке «Свои структуры».struct Player { x: f32, y: f32, angle: f32,}
impl Player { // Направление, куда сейчас смотрит игрок, — единичный вектор по // текущему углу (как в уроке «Синус и косинус»). fn direction(&self) -> (f32, f32) { (self.angle.cos(), self.angle.sin()) }}
// Скорости одни и те же на каждом кадре, поэтому это константы// (как в уроке «Константы»), а не переменные внутри loop.const SPEED: f32 = 4.0;const ROTATE_SPEED: f32 = 0.050;
#[macroquad::main("Поворот и движение")]async fn main() { // Игрок стартует в центре экрана и смотрит вправо — угол 0. let mut player = Player { x: screen_width() / 2.0, y: screen_height() / 2.0, angle: 0.0, };
loop { clear_background(BLACK);
// Стрелка влево — поворот против часовой стрелки. if is_key_down(KeyCode::Left) { player.angle -= ROTATE_SPEED; } // Стрелка вправо — поворот по часовой стрелке. if is_key_down(KeyCode::Right) { player.angle += ROTATE_SPEED; }
// Направление, куда сейчас смотрит игрок, — пересчитывается // заново на каждом кадре, сразу после поворота. let (direction_x, direction_y) = player.direction();
// Стрелка вверх — шаг вперёд вдоль направления взгляда. if is_key_down(KeyCode::Up) { player.x += direction_x * SPEED; player.y += direction_y * SPEED; } // Стрелка вниз — шаг назад вдоль того же направления. if is_key_down(KeyCode::Down) { player.x -= direction_x * SPEED; player.y -= direction_y * SPEED; }
// Тело игрока. draw_circle(player.x, player.y, 75.0, YELLOW);
// Глазик — как у турели: точка плюс вектор, растянутый на 62.5 // пикселей в направлении взгляда. let eye_x = player.x + direction_x * 62.5; let eye_y = player.y + direction_y * 62.5; draw_circle(eye_x, eye_y, 20.0, DARKBLUE);
next_frame().await; }}Что здесь происходит
Заголовок раздела «Что здесь происходит»struct Player { x, y, angle }— положение и поворот вместе, одной структурой, как в уроке «Свои структуры».player.direction()— метод, возвращающий единичный вектор взгляда по текущемуangle; внутриself.angle.cos()иself.angle.sin()— то же самое, что и в уроке «Вращение».&selfвместоself— не отбираетplayerв собственность:direction()вызывается, аplayerостаётся на месте и его можно менять дальше в этом же кадре.SPEEDиROTATE_SPEEDзаданы полямиspeedиrotate_speedниже. Они не меняются от кадра к кадру — поэтому это константы, а не обычные переменные внутриloop.- Глазик — та же точка плюс вектор, что и в уроке «Турель стреляет»: положение игрока плюс направление взгляда, растянутое на фиксированные 62.5 пикселя.