Мое задокументированное путешествие по разработке кроссплатформенных игр с использованием исключительно Rust.
Серьезно. 100% ржавчина или перебор!
Следующая статья: Нативные сенсорные события iOS с Rust
Почему я выбрал ржавчину
Rust — популярная тема, когда речь заходит о производительности и модульном дизайне, но на момент написания этой статьи в сообществе Rust были только слабые намеки на разработку iOS. Поэтому я выбираю Rust, потому что мне нужен кроссплатформенный язык системного программирования.
Взгляд в прошлое: что мне нравится в Rust
Покопавшись в Rust в течение нескольких недель, я обнаружил, что некоторые из самых ужасных функций на удивление приятны. Хотя есть кривая обучения, но, оглядываясь назад, я действительно ценю следующие особенности Rust.
Нет ООП
Модульный код
Экосистема документации
Система сборки
Управление пакетами
Мои цели изучения Rust
Моя цель — найти язык, который позволит мне писать кроссплатформенный код без необходимости прыгать через кучу обручей. Как у любителя, у меня не так много времени, чтобы изучать новые языки или копаться в фреймворках, как раньше.
В конечном итоге я хочу опубликовать готовый продукт в App Store, но пока моя цель — что-то компилировать. Я был бы счастлив, если бы Rust позволил мне отказаться от большей части, если не всего, программирования на Swift и Objective-C.
0. Настройка среды проекта
Приступая к этому проекту, я знаю, что буду часто изменять переменные среды и создавать сценарии. Поэтому, прежде чем начать работать с Rust, я улучшил свой .bashrc
, что позволяет легко вносить большие изменения в мою среду разработки.
Следующий сценарий позволяет мне расширить мой .bashrc
из любого каталога проекта в моей папке ~/code
, перемещаясь вверх, пока не найдет папку с именем .devenv
. Оттуда я могу получить сценарий bash для каждого проекта.
# get the first match from `find` while traversing upwards function find_above { old_pwd="$PWD" while [[ "$PWD" == $HOME/code/* ]] ; do new_pwd=`find "$PWD"/ -maxdepth 1 "$@"` if [[ "$new_pwd" ]]; then break fi cd .. done echo "$new_pwd" cd "$old_pwd" old_pwd="" new_pwd="" }
Вот часть, где я использую этот метод для получения локального файла .bashrc
. Мне приходится перезагружать свою среду каждый раз, когда я переключаю проекты, но это не проблема.
# see code above ENVDIR=$(find_above -type d -name ".devenv") if [[ "$ENVDIR" ]]; then source "$ENVDIR/.bashrc" fi
Создание псевдонимов
Теперь, когда моя машина может обрабатывать среды bash для каждого проекта, я настроил несколько команд для быстрого перехода по моему коду.
# ~/code/tictactoe/.devenv/.bashrc echo "detected local env: $PWD" # src directory export SRCDIR=$(find_above -name "src") # root directory export ROOTDIR=$(dirname $SRCDIR) # build directory export BUILDDIR="$ROOTDIR/target" mkdir -p "$BUILDDIR" # lib directory export LIBDIR="$ROOTDIR/src" mkdir -p "$LIBDIR" # bin directory export BINDIR="$ROOTDIR/.devenv/bin" mkdir -p "$BINDIR" export PATH="$BINDIR:$PATH" export RUST_LOG="warn,handmade=debug" export RUST_BACKTRACE=1 # aliases alias c="cargo build" alias clean="clean" alias s="source $HOME/.zshrc" alias root="cd $ROOTDIR" alias r="cargo run" alias t="cargo test" alias i="run-ios-sim.sh" alias cr="c && r"
1. Привет, мир ржавчины/Беви
Несколько недель назад я хотел начать с самого низкого уровня. Я хотел написать свой собственный графический рендерер и игровой движок. Однако примерно через 3 дня нулевого прогресса я отказался от этой мечты и стал искать игровой движок на Rust. Вероятно, это классический сценарий, когда сначала пытаются бегать, а потом учатся ходить.
Установка Беви
Естественно, я скачал первый попавшийся игровой движок. Который оказался довольно хорошим фреймворком. Bevy — это игровой движок, разработанный для приложений, которые должны быть модульными. Я использовал библиотеку в течение нескольких недель, и это здорово!
cargo add bevy
Написание кода с использованием Bevy’s Paradigm
Вот Hello world, использующий Bevy в Mac OS:
use bevy::prelude::*; fn main() { App::new() .add_system(hello_world_system) .run(); } fn hello_world_system() { println!("Hello Rust"); }
Обратите внимание, что я не вызываю println
напрямую. Это связано с тем, что Bevy предоставляет структуру, очень похожую на систему маршрутизации в ExpressJS
. В более крупных проектах вы можете извлекать возможности из модулей/плагинов, и каждый плагин имеет доступ к контексту для регистрации большего количества систем.
Бегущий Беви Hello World
cargo run
Где графический интерфейс?
Bevy изолировала все свои компоненты в плагины, поэтому, если вы хотите отказаться от всего «игрового движка», вы можете это сделать! Вы также можете запустить Bevy в безголовом режиме! Хотя ради этого эксперимента я включаю плагины по умолчанию.
use bevy::prelude::*; fn main() { App::new() .add_plugins(DefaultPlugins) // ... .run(); } // ...
2. Сборка для iOS (кросс-платформенная версия)
Хочу сразу попробовать собрать hello world
для iOS. Это экономит мне много времени, потому что, если я не смогу заставить iOS работать с Rust, проект провалится. Кроме того, нет ничего сложнее, чем написать проект, а затем попытаться портировать его на другую систему постфактум.
Оглядываясь назад: предостережения ведут к открытиям
На успешную компиляцию у меня ушло гораздо больше времени, чем я готов признать. Первоначально я думал, что мне нужно создать мост заголовка, используя
extern "C"
, но это не так. Все работает с чистым кодом Rust!
Комплектация
Существует инструмент сборки для груза, который позволяет мне скомпилировать и связать для iOS, не прыгая через какие-либо обручи. Сначала установите инструмент сборки:
cargo install cargo-bundle
Добавление целей компиляции
Затем вы можете перечислить все возможные цели сборки, которые поддерживает Rust. Поскольку я ориентируюсь на iOS, я выполнил следующую команду:
rustup target list | grep ios
Если вы не указали iOS в качестве цели, вы можете добавить новую цель на основе архитектуры вашей машины.
# for production rustup target add aarch64-apple-ios # for development rustup target add aarch64-apple-ios-sim
Упаковка для симулятора iOS
Если у вас есть цели компиляции, доступные в вашей Системе и вы работаете на Mac, вы можете использовать приведенный ниже сценарий или скопировать и вставить следующие команды, чтобы внедрить свое приложение в симулятор. Большинство команд поставляются с XCode.
Сценарий использует dasel для запроса имени и идентификатора пакета из файла проектов Cargo.toml
.
brew install dasel
Используя ряд команд, предоставляемых XCode, я могу работать с симулятором iPhone, не запуская графический интерфейс. Подробнее о том, как работают команды этого скрипта, можно узнать с помощью этой шпаргалки.
#/usr/bin/env bash APP_NAME="$(cat Cargo.toml | dasel -r toml '.package.name')" BUNDLE_ID="$(cat Cargo.toml | dasel -r toml '.package.metadata.bundle.identifier')" cargo bundle --target aarch64-apple-ios-sim xcrun simctl boot "iPhone 12 mini" open /Applications/Xcode.app/Contents/Developer/Applications/Simulator.app xcrun simctl install booted "target/aarch64-apple-ios-sim/debug/bundle/ios/$APP_NAME.app" xcrun simctl launch --console booted "$BUNDLE_ID"
3. Успех проекта!
Если все работает правильно, у меня должно остаться приложение для iOS, которое спамит «Hello Rust» на мой терминал и соответствует следующим критериям:
- Написан полностью на Rust
- Нет промежуточного кода
- Нет X-кода
- Модульная конструкция
- Гибкая среда разработки
Продолжение следует
Если вы хотите задокументировать остальную часть проекта в виде серии или поделиться исходным кодом, подписывайтесь, аплодируйте и комментируйте! Это помогает мне оставаться мотивированным
Следующая статья: Нативные сенсорные события iOS с Rust
Заключение
Можно полностью написать игру на Rust и скомпилировать для iOS.