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