
Давно горел идеей разработать прототип игры Super Mario. Поэтому в этой статье мы рассмотрим создание 2D-платформера с анимацией, взрывами, полосой здоровья и движущимися врагами — полностью на Rust с использованием библиотеки macroquad.
В качестве примера мы покажем процесс сборки игры, в которой главный герой — персонаж по имени Упячка (он же Upyachka) преодолевает препятствия, уклоняется от врагов-айтишников-hbr и сражается с бомбами на пути.

1. Планирование архитектуры игры

- TileMap — карта из тайлов (земля, ловушки, платформы) 
- Player — персонаж игрока с состояниями (передвижение, прыжки, урон, здоровье) 
- Enemy — враги с логикой движения и столкновений 
- Explosion — эффект анимированного взрыва 
- UI — отрисовка интерфейса: полосы здоровья, текст, уведомления 
- Audio — звуки прыжков, взрывов и музыки 
2. Графика и структура ресурсов
Игра использует:
- Спрайты PNG 64x64 (герой, враг, земля, бомба, взрыв) 
- Аудио в формате WAV 
- Карта уровня — двумерный массив - Vec<Vec<u8>>, где 0 — воздух, 1 — земля, 2 — бомба
Пример карты:
let tiles = vec![
    vec![0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    vec![0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    vec![0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    vec![0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    vec![0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    vec![0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0],
    vec![0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0],
    vec![0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    vec![0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0],
    vec![1; 20],
];
3. Игрок: движение, прыжок, урон, анимация
В Player реализовано:
- передвижение и прыжок по нажатию клавиш 
- влияние гравитации 
- проверка столкновений с землёй 
- анимация при движении (смена кадров) 
- обработка урона через - hurt_timer
Игрок может мигать при получении урона:
let alpha = if self.hurt_timer > 0.0 {
    ((get_time() * 15.0).sin() * 0.5 + 0.5) as f32
} else { 1.0 };Метод draw() отрисовывает спрайт нужного кадра с эффектом прозрачности.
4. Игровая логика: столкновения и урон
if enemy.collides_with(player.pos) && player.hurt_timer <= 0.0 {
    player.damage();
}
if tilemap.is_bomb_tile(tile_x, tile_y) && player.hurt_timer <= 0.0 {
    explosions.push(Explosion::new(player.pos));
    play_sound_once(&explosion_sound);
    player.damage();
    screen_shake = 10.0;
}
Игрок мигает при получении урона, имеет hurt_timer, чтобы предотвратить спам урона.
5. Враги: движение и взаимодействие
Враги двигаются влево-вправо по экрану. Если они сталкиваются с игроком, вызывается метод collides_with, и игрок получает урон. Если игрок приземляется сверху — враг уничтожается, появляется взрыв и звук:
if player.get_velocity().y > 0.0 && player_bottom <= enemy_top + 20.0 {
    explosions.push(Explosion::new(enemy.pos));
    play_sound_once(&explosion_sound);
}6. Анимация и эффекты
Взрыв — это 8 кадров по горизонтали. Каждый Explosion содержит frame и timer, и обновляется:
if self.timer > 0.05 {
    self.frame += 1;
    self.timer = 0.0;
    if self.frame >= 8 {
        self.finished = true;
    }
}Игрок также мигает:
if self.hurt_timer > 0.0 && (get_time() * 10.0).floor() as i32 % 2 == 0 {
    return; // пропускаем кадр
7. Звук и дрожание камеры
if screen_shake > 0.0 {
    offset = vec2(rand::gen_range(-2.0, 2.0), rand::gen_range(-2.0, 2.0));
    screen_shake -= get_frame_time() * 50.0;
}8. UI: полоса здоровья
draw_rectangle(10.0, 10.0, 200.0, 20.0, DARKGRAY);
draw_rectangle(10.0, 10.0, 200.0 * (player.health as f32 / 3.0), 20.0, RED);Полностью исходный код: https://github.com/digkill/RustyMario
Видео геймплея: https://t.me/notecto/3
Следующим шагом может стать редактор уровней, меню, или экспорт в Android, IOS через cargo mobile.
Комментарии (3)
 - flx023.05.2025 14:54- Вот кстати про macroquad/miniquad и что оно может было бы интересно почитать. Я для своей недавней статьи по глупости взял обертку для sdl2 на расте, и потом страдал. Хотя казалось бы - там задача всего лишь создать окно и нарисовать на нем два треугольника через opengl. Даже разделить пользовательский ввод и рендер в разные треды невозможно (что частично проблема самой libsdl, но на плюсах бы получилось рендерить в отдельном треде). 
 Поэтому мне теперь очень интересно, как это делать по-человечески, и не тащить при этом тяжелые зависимости. - AlexCatLeva Автор23.05.2025 14:54- Привет! Очень интересная статья у тебя, прочитал, захотел повторить. 
 По поводу коробок для раста,
 Как вариант есть еще wgpu
 Он лучше дружит с cargo-mobile, у меня была мысль портировать на Android, IOS
 Помучившись 2 дня и 2 ночи, понял что macroquad/miniquad не очень дружит cargo mobile
 
 
           
 
JRTJITEADER
Спасибо, интересно