はじめに
こんにちは! 中2のAwashAmityOakです! 今回は、PythonのレトロゲームエンジンであるPyxelを、Rustで動かす方法をまとめます。
Pyxelとは?
![]() |
| https://github.com/kitao/pyxel/blob/main/docs/images/pyxel_message.png より |
Pyxelは、Python向けのレトロゲームエンジンです。日本人のkitaoさんという方が作られたので、日本語ドキュメントが完備されています。2023年12月に、バージョン2.0.0を迎え、その後もアップデートが続いています。
Pyxelのライブラリのコア部分は、Rustで書かれていて、crates.ioにも公開されているので、Cargo.tomlに依存関係を書き込むことで、Rustで使用することができます。
動作環境
- OS: macOS 10.15.7
- CPU: Intel Core i7
- メモリ: 8GB
Homebrew 4.2.11SDL2 2.30.1rustc 1.76.0cargo 1.76.0
手順
今回は、Rustで、PyxelのexamplesのHello, Pyxel!を動かすことを目標にします。(Pythonのソースコードはこちらです。)
RustはPCにインストール済みであることを前提とします。インストールしていない場合は、公式ページから、インストールしてください。(ググればインストールについての情報はたくさん出てきます。)
(2024年3月18日追記)
macOS 11より古いバージョンをお使いの方は、記事後半の「動かない場合は」を一度お読みください。
1. プロジェクト作成
普通のRustプロジェクトと同じように、cargo newを使用して、プロジェクトを作成します。
$ cargo new hello_pyxel
$ cd hello_pyxel
ディレクトリ構成は以下のようになります。
hello_pyxel
├── .gitignore
├── Cargo.toml
└── src
└── main.rs
2. 依存関係の解決
Pyxelのコア部分は、v1.8.2からv1.9.18はpyxel-coreというcrateで管理されてます。また、v1.5.1からv1.8.2、そしてv2.0.0以降はpyxel-engineというcrateで管理されています。
私の環境では、最新のpyxel-engineをコンパイルするときに、
ld: symbol(s) not found for architecture x86_64
というエラーが発生してしまったので、この記事では旧バージョンのコアであるpyxel-coreを使っていきます。
1. SDL2のインストール
macOSで、pyxel-coreを使う場合には、SDL2が必要となります。今回は、Homebrewを用いて、インストールしていきます。(macOS以外の環境については、Rust-SDL2公式ドキュメントを参照してください。)
$ brew install sdl2
を実行し終わった後、
$ brew info sdl2
と実行して、出力の最初の行に
==> sdl2: stable 2.30.1, HEAD
などと表示されていればOKです。
2. Cargo.tomlの編集
Cargo.tomlに、
pyxel-core = "1.9.18"
と追記します。
もしくは、cargo addを使用して、
$ cargo add pyxel-core
とCLIで実行しても良いです。
3. 画像ファイルの用意
PyxelのGitHubにあるロゴ画像をダウンロードします。ダウンロードが完了したら、
hello_pyxel
├── .gitignore
├── Cargo.toml
├── assets
│ └── pyxel_logo_38x16.png
└── src
└── main.rs
となるように、./assets/pyxel_logo_38x16.pngに配置してください。
4. プログラムの記述
1. 必要なAPIのuse宣言
pyxel::PyxelCallbackというトレイトをuseします。
use pyxel::PyxelCallback;
2. App構造体の作成
PythonコードでのAppクラスにあたるものです。
struct App {}
今回は特に作成すべきフィールドがないので、空の構造体を作ります。
3. init()メソッドの実装
implを使って、App構造体にinit()メソッドを実装していきます。
impl App {
fn init() {
pyxel::init(
160,
120,
Some("Hello Pyxel"),
None,
None,
None,
None,
None,
);
pyxel::image(0).lock().load(0, 0, "assets/pyxel_logo_38x16.png");
let app = App {};
pyxel::run(app);
}
}
Pythonでのpyxel.init()関数には、デフォルト引数があり、特に指定しない場合は省略できました。しかし、Rustのpyxel::init()関数では、ちゃんとすべての引数にNoneを渡してあげる必要があります。
イメージのロードについても、Pythonと少し異なる点があります。Rustでのpyxel::image()の戻り値は、ミュータブルな値を指す、参照カウント形式のスマートポインタです。そのため、Rustの特徴の一つであるスレッド安全性を確保するために、lock()メソッドを実行する必要があります。これは、他のタイルマップやサウンド、ミュージックについても同様です。(難しい話なので、わからない人は、「イメージやサウンドを扱うときは、lock()をつけなければいけないんだな」くらいに思っておいてください。)
4. update()メソッドの実装
update()メソッドは、先ほどuse宣言したPyxelCallbackトレイトを用いて実装します。
impl PyxelCallback for App {
fn update(&mut self) {
if pyxel::btnp(pyxel::KEY_Q, None, None) {
pyxel::quit();
}
}
// ここには`draw()`メソッドの実装が入ります。
)
pyxel::btnp()関数にも、Noneを渡してあげなければいけない引数があります。
5. draw()メソッドの実装
draw()メソッドも、update()メソッドと同様に、PyxelCallbackトレイトを用いて実装します。
impl PyxelCallback for App {
// ここには`update()`メソッドの実装が入ります。
fn draw(&mut self) {
pyxel::cls(0);
pyxel::text(55.0, 41.0, "Hello, Pyxel!", (pyxel::frame_count() % 16).try_into().unwrap());
pyxel::blt(0.0, 0.0, 0, 0.0, 0.0, 8.0, 8.0, None);
}
)
pyxel::text()に渡す引数の型変換に気をつけてください。
6. main()関数の実装
Pythonコードでの最後のApp()というところに対応します。
fn main() {
App::init();
}
ソースコードすべて
use pyxel::PyxelCallback;
pub struct App {}
impl App {
fn init() {
pyxel::init(
160,
120,
Some("Hello Pyxel"),
None,
None,
None,
None,
None,
);
pyxel::image(0).lock().load(0, 0, "assets/pyxel_logo_38x16.png");
let app = App {};
pyxel::run(app);
}
}
impl PyxelCallback for App {
fn update(&mut self) {
if pyxel::btnp(pyxel::KEY_Q, None, None) {
pyxel::quit();
}
}
fn draw(&mut self) {
pyxel::cls(0);
pyxel::text(55.0, 41.0, "Hello, Pyxel!", (pyxel::frame_count() % 16).try_into().unwrap());
pyxel::blt(61.0, 66.0, 0, 0.0, 0.0, 38.0, 16.0, None);
}
}
fn main() {
App::init();
}
5. コンパイルと実行
$ cargo run
を走らせて、プログラムをコンパイル&実行します!
こんなHello, Pyxel!が表示されましたか?
![]() |
Hello, Pyxel!の実行結果 |
動かない場合は(2024年3月18日追記)
古いバージョンのmacOSだと、リンクが上手く働かずに、コンパイルできない場合があるみたいです。ここでは、若干ゴリ押し気味ではありますが、対処法をお伝えしようと思います。
長いので折り畳みました
1. Pyxel v1.9.18をクローン
適当なディレクトリ内で、
$ git clone https://github.com/kitao/pyxel.git -b v1.9.18
「ブランチ作ってないよ〜」みたいな警告が出るので、気になる人はgit switch -c [ブランチ名]で作ってあげてください。(ここからコミットするつもりはないので、私は特に気にしてません。)
2. ./crates/pyxel-core/Cargo.tomlを書き換える
pyxel-corecrateのCargo.tomlを編集します。具体的には、
[target.'cfg(any(target_os = "windows", target_os = "macos"))'.dependencies]
sdl2 = { version = "0.35", default-features = false, features = [
"bundled",
- "static-link",
+ # "static-link",
"unsafe_textures",
] }
のように、"static-link",というfeatureを無効にします。
コマンドでやる場合は、pyxelディレクトリに移動した後、こちらを実行してください。
$ sed 's/"static-link",/# "static-link",/' crates/pyxel-core/Cargo.toml > crates/pyxel-core/Cargo.toml.tmp && mv crates/pyxel-core/Cargo.toml.tmp crates/pyxel-core/Cargo.toml
3. 元のhello_worldプロジェクトのCargo.tomlも書き換える
元のプロジェクトに戻って、Cargo.tomlのpyxel-coreを、ローカルのパスで指定します。
[package]
name = "hello_pyxel"
version = "0.1.0"
edition = "2021"
[dependencies]
- pyxel-core = "1.9.18"
+ pyxel-core = { path = "../pyxel/crates/pyxel-core" }
../pyxel/crates/pyxel-coreの部分は、クローンしたpyxel-coreがあるパスに、適宜書き換えてください。
これで完成です! すでに手順5まで進めている場合は、cargo runを実行してみてください!
おわりに
Pyxelは、公式でRustに対応しているわけではありませんが、コア部分が使える状態で公開されているので、この記事のようにRustでプログラミングすることができてしまいます。
Pyxelを触ったことがある+Rust勉強中の人は、この機会に、自分が過去にPyxelで作ったゲームのソースコードを、Rustで書き直してみるのはいかがですか?
この記事が好評であれば、RustでPyxelを使うときのチートシートのような記事も書いてみようと思います。
誤字・脱字などあれば、コメントか編集リクエストで教えていただけるとありがたいです。
この記事が良いと思ったら、ぜひいいねとフォローをよろしくお願いします! Xアカウントのフォローなどもしてくれると泣いて喜びます!
最後まで読んでくださり、ありがとうございました!

