日曜プログラミング

休日趣味でやってるプログラミング関連記事をダラダラと

ArcadeRS サンプルゲームを通じて play-clj に慣れる

ArcadeRSという ゲーム制作を通じての Rust 言語を学ぼうと言うチュートリアル記事がある。

この記事はそのチュートリアルを通じて完成するゲームを play-clj で作ってみようと言う記事。 元記事と違って Clojure そのものは既にある程度学習済で、ここのタイトルにあるように play-clj、引いてはベースとなる libGDX の使い方に慣れるのが主な目的になる。 なので、Clojure 自体の入門や開発環境の構築は他書籍やサイトを参考にして下さい。

この記事を作成するにあたっての開発環境だけは書いておきます。

流れだけは元記事に沿うようにするが、翻訳記事ではないので元記事のリンクを載せたりもしない。 また、後で検索しやすいよう arcade-clj とタグ付けしておく。

完成イメージ

現在は元記事のスクリーンショットをそのまま持ってきているだけだが完成したら差し替える予定。

f:id:shinmuro:20160524214923p:plain

と言っても見た目は多分タイトルバーデザインが Windows のものになるだけだと思う。

目次

日本語になっていない所はまだ play-clj 版での記事になっていない部分。 また、機能ベースでタイトルを付けていく予定。

  1. play-clj のインストール、シンプルなウィンドウの表示 - 日曜プログラミング
  2. ESC を押したら終了するようにする(イベントハンドリングの触り) - 日曜プログラミング
  3. More event handling, where we discuss macros
  4. Views, where we learn about boxes, pattern matching, trait objects, and dynamic dispatch
    • 3,4 は Rust の言語機能がメインで、特にゲームとしての機能追加を行ってるわけではないので割愛。
  5. Screen の 切替 - 日曜プログラミング
  6. 四角形を動かす、描画する - 日曜プログラミング
  7. Sprites, where we create our player’s ship
  8. Backgrounds, where we handle resizing, scale and translate through time
  9. Main menu, where we play with textures and Rust’s vectors
  10. Asteroid attack!, where we render animated asteroids
  11. Shooting bullets, where we explore iterators
  12. Brawl, at last!, where we make objects interact and explode
  13. ≪ Boom! ≫, where we play sound.
  14. Variety, where we create more enemies
  15. Difficulty, where we manage the difficulty level and the score
  16. High score & wrap-up, where we play with the file-system and enhance our main menu

目次だけ最初に掲げちゃって大丈夫かと自分でも少し思うが ペースは遅くとも何とか最後までいけるよう頑張ります。

勿論チュートリアルのゲームを完成させるのが一番の目的だけど 記事の方も日本語に置き換えられて*1リンクが貼られれば完成とします。 また、この記事は主に目次なので記事を更新次第リンクとタイトルも更新します。

*1:翻訳じゃなくあくまで機能ベースの置き換え

cider で play-clj 触り始めました

libGDX を Clojure 上でより使い易くされた play-clj 触り始めました。

github.com

まだサンプル触ってる段階だけど REPL で起動したままいじれるのは 非ゲーム分野では経験済みとは言えゲームでも可能なのが改めて新鮮。

今日は 公式チュートリアル ではフォローされてない emacs+cider 使った時の補足事項みたいなのをいくつか。

cider で REPL

公式チュートリアルで流れは書かれてるがもう少し細かく言えば ソースをいじって REPL から起動したゲーム画面に反映させるには、

  1. namespace をプロジェクトのコアに移動(チュートリアルで言う hello-world.core)
  2. ソースをセーブ
  3. ソースの評価
  4. OpenGL コンテキストスレッドへの再描画命令

の作業が必要で、1. は1回でいいが、2~4 は繰り返す事になり、 これをコマンド+タイピングで毎度やるのはさすがに煩わしい。

なので emacs にコマンドとしてキー1発で呼び出せるものをてきとーに書いた。

(require 'cider)
(defun play-clj-reload ()
  "REPL へ play-clj への反映コマンドを投げる。
   hello-world-game と mainscreen は適時書き換える事。"
  (interactive)
  (cider-interactive-eval
   "(on-gl (set-screen! hello-world-game main-screen))"))

(defun save->eval->reload ()
  "play-clj での開発用。バッファセーブ→バッファ評価→play-cljのリロードのコンボをまとめて実行する。
   sit-for で wait かましているのはあんまり意味ないかも。"
  (interactive)
  (save-buffer)
  (sit-for 1)
  (cider-load-buffer)
  (sit-for 1)
  (play-clj-reload))
(define-key cider-mode-map (kbd "<f5>") 'save->eval->reload)

docstring にも書いたが sit-for は別になくてもいいかも。 まあけどこれでF5一発で画面反映までしてくれるようになるので結構楽になる。

cider-jack-in だとランタイムエラーが出ない?

play-clj(と言うか libGDX) は GL スレッドを 1 個作ってそこでイベントハンドリングするという まあ Swing や JavaFX などと同じような手法を取ってるのだが、この手のものは cider-jack-in した場合別スレッドを立ち上げる為か(詳しくは調べてない)、 例外のメッセージを吐き出さずに止まってしまう事がある。

play-clj はその場合にも対応できる方法を用意してて、set-screen-wrapper! を使って 例外時は printStackTrace して空の画面を出させるようにするというスニペットチュートリアルの 一番最後に載せてあり、それを core.clj の一番最後に入れておけば大抵 REPL を再起動せずとも 復帰できるようになる。

ただ、それでも cider-jack-in でコンソールを立ち上げずに REPL 起動した場合だと エラー時もやはりメッセージが出てくれない。

ウィンドウは増えてしまうがコマンドプロンプトから REPL 立ち上げるとエラーメッセージは 出るので個人的にはこちらの方をオススメする。

ちなみにここでも前述した emacs コマンド(F5) で復帰できて便利。

駄文:すいません、Rust 俺にはムリ

まあ誰に謝ると言う訳でもないんだけど Rustはプログラミング言語Rustを 一通り試してみて、さあそれじゃちょっとしたツールやライブラリみたいなの試しに作ってようかと 手を動かしてみたらコンパイラに怒られまくって正直心折れました。

いや、ネイティブコード吐き出す言語はちゃんと手を出した事がなかった分野なんでやってみたかったんだけどね、 トレイト+ジェネリック+静的型システム+ライフタイム辺りのコンボがどうにも辛い。

で、結構な間何も更新してなかったんだけど 今後はまた Clojure に出戻って気がついたような事を書いていきたいと思う。

Clojure も 1.8.0 になったってのを見た後くらいから全然触ってなかった。 またちょっと触りだして思ったのは cider も進化してんなあ、界隈の hot なライブラリ全然知らないなあ、 多少の起動の遅さというデメリット以上に REPL はやっぱり便利過ぎるなあ、などなど。

こんな事思ってる人いないかなあと ググッてこんな記事見つけてみたりしてまた Clojure へのモチベーションを上げてみたり。 postd.cc

ちなみにこの記事は Rust については全く触れてないので注意。

と言うわけで Rust は言語というよりは周りの開発環境的なものが もう少しこなれたらまた手を出すかも、出さないかも。 Winodows だけかもしれんけど racer もやたら固まるしなあ。

以上駄文でした。

環境変数設定画面を直接開く方法

前日本語でググって見つからず諦めてたが今日英語でググッてみたらあった。

serverfault.com

と言うわけで以下のような感じでそのままバッチファイルにしたものを PATH 通してる所に放り込んだ。

@echo off
rundll32 sysdm.cpl,EditEnvironmentVariables

コマンドプロンプト画面は出るが常時表示させておくものでもないので気にしない。

Rust Win GNU ABI で実際何か作る時は MinGW-w64 の gcc にパスを通しておくのが実質必須と言うお話

今回は Conrod と言う GUI ライブラリのサンプルを動かしてみたくて 試そうとしたら、コンパイルが途中で止まったのが発端。

確認環境

  • Win7 64bit
  • Rust Stable 1.8.0 GNU ABI

公式ガイドにあるサンプルを動かす Cargo タスクを実行すると外部 Crate のコンパイルで止まった。

cargo run --release --example text

エラーメッセージはもう長くなりすぎるんで省略するけど、 miniz-sys 0.1.7 という Crate のコンパイルで止まっていた。

現象やメッセージがどんなものかと言うのは gcc-rs の issue#7と同じなので 詳しくはそちらをどうぞ。

今回問題の原因は何かと言うと、Cargo は前の記事でもチラッと話題にしたが、

と言う事。

ここで一つ疑問なのは Win GNU ABI 版 Rust 同梱の gcc じゃダメなのかと言う事だが *1、 rustc から呼ぶ gcc は主にコンパイル済みライブラリのリンクを目的として使ってて C ソースからコンパイルするにはヘッダファイル等も一緒に入ってないと色々と都合が悪く こういう仕様になってるみたい。Rust 同梱の gcc でも試してみたが確かに動かなかった。 まあ C ソースのコンパイルから必要な Crate だとそうするしかないか。

似たような主旨の事は実は gcc-rs の README にも書かれているんだが、この事は外部 Crate を使わずに何か作るという可能性が低いから 公式でももっと大きく書いておいて欲しいと個人的には思った。 *2

また、リンク先には MSVC 環境の場合もフォローしてるけど自分は GNU ABI でやってるので未確認。

最後に MinGW-w64 の gcc に PATH を通せば最初のコマンドは無事実行できた。

*1:実際 The Programming Language Rust でサンプルコードを試している時は今回の事はなかった→Rust 同梱/MinGW-w64 いずれの gcc にも PATH は通していなかったので

*2:未読のForeign Function Interfaceなどに書いてあったらごめんなさい

Cargo で gcc のフラグを任意に渡す方法→cargo rustc からのみ可能(2016-05-08修正)

要は WindowsGUI サンプルコードをお試しで動かすと コンソールも一緒に立ち上がってたのが嫌だったので調べると見つかった。

stackoverflow.com

で、このフラグを Cargo から渡す方法はないか調べた。

確認環境

  • Win7 64bit
  • Rust Stable 1.8.0 GNU ABI

2016-05-08修正

申し訳ない、最初に書いた記事は自分の誤読で案について話しているだけで Cargoの設定ファイルで gcc へ任意のオプションを渡す方法は現時点では存在しておりませんでした。

ただ、cargo rustc からであれば渡せます。

cargo rustc --release -- -C link_args=-Wl,--subsystem,windows

これは簡単なツール作って Windows エクスプローラから exe 起動して コマンドプロンプトが出なくなったのを確認済。

修正前の記事で紹介した issue#544の件 のはまさしくこの cargo rustc での機能を実装したよと言う事のみでした。すみません。


↓で見つけた。 github.com

やり方は簡単で、対象のプロジェクトの Cargo.toml に以下の一文を追加する。

[rustc]
flags="-C link_args=\"-Wl,--subsystem,windows\""

MSVC ABI だとこの現象が発生しないのかは未確認。

ちなみにissue#544はcargo rustcタスクでコマンドラインからオプションとして渡す方法がメインの話なのだが、 こちらでやると 前の記事の方法で設定した 追加ライブラリサーチパスが有効にならない。あくまでcargo runorcargo buildした時だけって事なのかな。

Cargo でグローバルにライブラリサーチパスを追加する方法

Cargo で C ネイティブライブラリをリンクさせる為に 配置されているパスを Cargo に明示的に指示したい場合どうするか。


確認環境*1

  • Win7 64bit
  • Rust Stable 1.8.0 GNU ABI

プロジェクト単位での設定方法

探すと Qiita でこんな記事が見つかった。

qiita.com

グローバルに設定する方法

今回自分がやりたいのはこちら。

先に紹介した方法でもいいんだけど特に自分のような Windows ユーザだと MSVC/MinGW/Cygwin とかで配置する場所の慣習が変わってくるのと ほぼマルチユーザで使う事はないのでライブラリをインストールしたら プロジェクト単位すら面倒でグローバルに設定したくなる。

で Cargo のドキュメント探してたらあった。

Cargo は設定ファイルを記述する事が可能らしく *2cargo runcargo build 実行時はまずその設定を読み込んでくれるらしい。

自分は %HOME% を設定してるので、%HOME%.cargo\config ファイルを配置。 そしてライブラリサーチパスの設定を以下のように書いた。 *3

[target.native]
rustc-link-search = ["C:/path/to/lib"]

["C:/path/to/lib"] は自分がリンクさせたいライブラリが存在するパスを指定する事。

参考

一度このドキュメントはどこかでちゃんと読み込んでおきたいかな。 Tomlファイル自体の形式は分かりやすいんだけど何をどう設定できるのかがあんまり把握できてないし。


*1:しつこいようだけど Rust は情報追っかけてると 6week リリースするとか更新早そうなのと今はともかくこれまでは破壊的変更が何度もされてきたみたいなのでRust 関連記事は今後も極力確認した環境をまず書いていこうと思う

*2:英語だけどパスの一覧がリストにあるので場所は分かると思う

*3:例にあるようにパス区切りは / でも行ける。Windows区切りだと \\ と書く必要があり少し見づらくなるのでRust の場合は / で行った方が良さそう。