Это простая и удобная Rust библиотека для разработки небольших 2D игр. Библиотека является кроссплатформенной и работает на Windows, Linux, MacOs, HTML5, Android, IOS. Основные особенности: Высокопроизводительный 2D рендеринг, Минимум зависимостей и быстрая компиляция, UI библиотека в комплекте, Простое развёртывание на всех платформах, Не требует написания platform-specific кода. Данная библиотека избавляет разработчика от следующих задач: Создание окна для отрисовки, Организация игрового цикла, Обработка событий ОС, Организация рендеринга, Выбор или написание утилитарных библиотек (алгебра, цвет, звук, случайные числа, ...). Предполагаю, что для разработки на Rust у нас всё готово. Если нет, то тут есть инструкция по установке необходимых утилит.  Для начала создадим новое Rust-приложение с именем "asteroids" используя команду: Теперь у нас есть заготовка для Rust приложения. Подключим к нему библиотеку macroquad, добавив в таблицу  файла Cargo.toml строку: Теперь мы можем использовать эту библиотеку для инициализации окна и запуска игрового цикла. Добавим в main.rs файл следующий код: Данный фрагмент кода использует макрос #[macroquad::main("Asteroids")] для автоматической генерации кода инициализации окна и запуска асинхронного рантайма, в котором будет выполняться наше приложение.  Асинхронность нужна, в основном, для того, чтобы функция ожидания следующего кадра не блокировала основной поток выполнения приложения. Блокировка основного потока не допустима в Android и WASM. Также асинхронное выполнение позволяет более эффективно выполнять задачи ввода/вывода. Например, чтение файла. При запуске приложения у нас должно появиться пустое окно: Можно приступать к реализации игровой логики. Игра будет представлять собой вертикальный скроллер. Игрок, управляя небольшим кораблём, должен уворачиваться от астероидов как можно дольше. Для простоты, позволим кораблю двигаться только по горизонтали. При этом, корабль будет постоянно ускоряться, из-за чего уклоняться от астероидов будет всё сложнее. Опишем структуру, содержащую общее состояние приложения: В данной структуре будем хранить рекордное время и состояние игрового процесса. Так как, помимо игрового процесса, у нас в игре будет меню, состояние игры обернём в перечисление Option. Данное перечисление либо содержит в себе объект, либо не содержит ничего. Новое состояние игры будем создавать и помещать в Option при каждом новом запуске игрового процесса. Далее, опишем логику инициализации состояния приложения по умолчанию. Это делается с помощью реализации трейта Default на типе: Опишем методы для обновления состояния приложения и его отрисовки: Стоит отметить, что метод update() подразумевает изменение состояния нашего приложения, поэтому принимает данные приложения по мутабельной ссылке (&mut self). Метод draw() подразумевает только чтение данных для их отображения. Поэтому он принимает иммутабельную ссылку (&self).  Далее, инициализируем состояние приложения перед запуском игрового цикла. Обновляем и отображаем состояние на каждом кадре. Сейчас методы update() и draw() не делают ничего. Реализуем их. В данном коде мы: Запускаем игру, если была нажата клавиша Enter и игра ещё не запущена. Строки 3-8. Так как поле State::game имеет тип Option<Game>, мы не можем получить непосредственный доступ к самой игре для обновления её состояния. Поэтому, для обновления состояния игры, передадим в метод Option::and_then() замыкание, которое будет выполнено только в том случае, если игра запущена. Результат выполнения game.update() возвращает Option<f64>, и может содержать время, в течении которого продержался игрок. Переменная finished будет содержать Some только если игра была завершена. Строки 10-15. Проверяем, завершилась ли игра. Если это так, то завершаем игру и обновляем значение рекордного времени. Строки 17-24. В методе draw(), в зависимости от состояния приложения, отображаем на экране либо меню, либо игру. Строки 29-35. Для компиляции приложения нам не хватает некоторых типов и методов. Добавим их. Тип Game пока оставим пустым: Добавим его реализацию по умолчанию: Опишем методы update() и draw(). На данный момент добавим только функционал завершения игры нажатием Escape. Последнее, чего не хватает - метод рисования меню. В нём отобразим только текст о том, как запустить игру: Теперь, запустив приложение, видим приглашение начать игру, можем её запустить клавишей Enter и прервать клавишей Escape. К сожалению в самой игре у нас ничего нет. Пора наполнить её содержимым. Для расчёта физики движений игровых объектов нам потребуется знать, сколько времени прошло между предыдущим и текущем кадром. Привязка ко времени обеспечит ровную скорость игрового процесса, независимо от частоты кадров. Добавим в структуру Game время последнего обновления: А в методе Game::update() будем обновлять это значение, вычисляя, предварительно, временной интервал между кадрами: Теперь опишем корабль игрока: Способ его инициализации по умолчанию: Константные параметры корабля: Обновление его состояния: И способ его отображения на экране в виде треугольника: Добавим корабль в игру: Теперь, при запуске игры у нас должен появится белый треугольный кораблик, который можно перемещать клавишами A и D: Помимо корабля, отобразим рекордное и текущее время. Для вычисления текущего времени, поместим в структуру Game поле start_time, содержащее время начала игры. Добавим вызов self.draw_time() в Game::draw() и у нас должны появится надписи в левом нижнем углу экрана: Для полноценной игры осталось добавить астероиды, от которых должен уворачиваться игрок. Опишем состояние астероида следующей структурой: Создавать астероиды будем выше верхнего края экрана со случайным положением по горизонтали и случайной скоростью: Астероид должен уметь обновлять своё состояние, отображаться на экране и проверять, не вышел ли он далеко за край экрана. Последнее пригодиться для вычисления астероидов, которые можно удалить из игры. Осталось только включить астероиды в игру. Добавим в структуру Game вектор астероидов и таймер их появления. Теперь игра и её инициализация выглядят так: В метод обновления игры добавим порождение новых астероидов, обновление существующих и удаление лишних. Лишними астероидами считаем те, которые улетели далеко за пределы экрана: В метод отображения игры добавим отображение астероидов: Теперь в игре должен появиться поток астероидов, несущихся на корабль. Но при столкновении с ними ничего не происходит. Исправим это. Добавим кораблю возможность детектировать столкновения с астероидами: И добавим проверку на столкновение в цикл обновления астероидов в методе Game::update(): Наконец, игра готова. Теперь можно попытаться установить рекорд! В обычном режиме мне удалось продержаться не более 35 секунд. Но игра позволяет немного жульничать не меняя код. Как? Жду ваши варианты в комментариях. А также приглашаю всех на бесплатный вебинар, в рамках которого я расскажу о том, какие проблемы решает Rust. Регистрируйтесь по ссылке. Репозиторий с кодом  Старший разработчик Rust