
Давно горел идеей разработать прототип игры 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)
flx0
23.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
Спасибо, интересно