雑念日記

主に技術的なことをつらとらと(書ければいいな)。

DXRubyゲームアプリをProcessingで動かそう

本投稿はProcessing Advent Calendar 2013の18日目の記事です。
残すところあと7日、佳境に入って来ましたね。

(2013-12-24 13:00 追記) 日本時間で多分2013年12月23日に、Processing 2.1対応のruby-processing-2.4.1がリリースされました。現在最新のdxruby_rp5-0.0.2はruby-processing 2.4.1に対応していません。近日中にruby-processing-2.4.x対応のdxruby_rp5-0.0.3をリリースする予定です。

(2013-12-25 00:10 追記) ruby-processing-2.4.1対応のdxruby_rp5-0.0.3をリリースしました


DXRubyという、RubyDirectXを制御してWindows用ゲームを作成できる拡張ライブラリがあります。(作者:@mirichiさん)

私の住む島根県では、毎年県がRuby合宿というイベントを催しており、このDXRubyを利用したアプリ制作を通じて参加者にRubyを体験してもらっていたりします。

DXRubyの詳細はこちらへどうぞ。→ Project DXRuby


さて、DXRubyはDirectXを利用しているため、当然LinuxやMacでは使用できません。

じゃあDXRubyっぽく動くライブラリをruby-processingを使って用意してあげれば、DXRubyを利用して作成されたゲームがProcessingを描画エンジンとしてLinuxやMacで動かせるんじゃね?と思い、やってみました。

先に結論

それっぽいのができました。
以下はDXRubyのソースに添付されているサンプルゲームを動かしてみたときのキャプチャです。
画像だけ見せられてもナンノコッチャでしょうが、間違いなくProcessingで動いています。

f:id:hoshi_sano:20131208220450p:plain

以下、プレイ動画。

敵弾に当たっても自機が死なないのはサンプルの仕様です。
ホンモノと比べてちょっと表示がおかしい箇所が何点かありますが、発展途上のご愛嬌ということで堪忍を...。

DXRubyRP5

上記を実現するために、DXRubyRP5というライブラリを作りました。

DXRubyRP5ではDXRubyと同じクラス、メソッド、定数などを定義しているため、アプリがDXRubyを利用しようとして require 'dxruby' を実行したり、DXRubyのAPIを呼び出したりしても、実態としてはDXRubyRP5のAPIが呼び出されることになります。

そしてDXRubyRP5の内部ではruby-processingを使用しているため、結果的に画面の描画はDirectXに代わってProcessingが行うことになります。

注意:現状では完全に互換性があるわけではないので、世の中のすべてのDXRuby向けのアプリが動くわけではありません。(なんと音の再生すらまだできない。)

DXRubyRP5は、Ruby/SDLを利用してDXRubyアプリをLinuxやMacで動かすことを目的とした、@takaokoujiさんのdxruby_sdlにインスパイアされたもので、コードの方もかなりパクって参考にさせていただいております。

インスパイアとかカッコイイこと言っちゃってますが、上にも書いたような「できるんじゃね?」という技術的興味が主な動機だったりします。

インストール

DXRubyRP5はruby-processingを利用するので、まずはruby-processingをインストールします。ruby-processingのインストール方法は前の記事を参考にしていただければと思います。

次に以下のコマンドでDXRubyRP5をインストールします。

  $ gem install dxruby_rp5

使い方

DXRubyRP5は内部でruby-processingを利用しているので、使い方もruby-processingみたいな感じになります。

  $ dxrp5 --nojruby run sketch.rb

指定するファイル(sketch.rb)はruby-processingの形式で書かれていてもDXRubyの形式で書かれていても動作するはずです。

DXRubyでは次のような形式でコードを書きます。

require 'dxruby'

image = Image.load('data.png')  # data.pngの読み込み
x = 0
y = 0

# メインループ処理
Window.loop do
  x = x + Input.x  # 入力に応じてx座標を更新
  y = y + Input.y  # 入力に応じてy座標を更新

  Window.draw(x, y, image)  # (x,y)座標にdata.pngを描画する
end

仮にsketch.rbファイルが上のようになっていたとすると、DXRubyRP5はこれを次のようなコードに整形します。

class Sketch < Processing::App
  def setup
    require 'dxruby'

    image = Image.load('data.png')  # data.pngの読み込み
    x = 0
    y = 0

    # メインループ処理
    Window.loop do
      x = x + Input.x  # 入力に応じてx座標を更新
      y = y + Input.y  # 入力に応じてy座標を更新

      Window.draw(x, y, image)  # (x,y)座標にdata.pngを描画する
    end
  end

  def draw
  end
end

「おいおいdrawメソッドが空じゃないの。これじゃ何も動きませんわ。」と思われたかもしれません。
実はDXRubyRP5の「Window.loop do 〜 end」では、doとendで囲まれた部分を動的にdrawメソッドの処理として定義する処理が行われます。
なので誤解を恐れず書くと、上記のコードは以下とほぼ同じようなものに内部で変換されているのでした。
※以下のコードをそのまま動かそうとしても動きません。そういった意味でも「誤解を恐れず」と書きました。

class Sketch < Processing::App
  def setup
    require 'dxruby'

    image = Image.load('data.png')  # data.pngの読み込み
    x = 0
    y = 0
  end

  def draw
    x = x + Input.x  # 入力に応じてx座標を更新
    y = y + Input.y  # 入力に応じてy座標を更新

    Window.draw(x, y, image)  # (x,y)座標にdata.pngを描画する
  end
end

Image.loadやInput.x、Input.y、Window.drawがどう動作するものなのかは、以下(本家DXRubyリファレンス)を参照していただけると詳細がわかるかと思います。

制限事項

上述のように、dxrp5コマンド実行時に指定されたファイルの内容はほぼそのままsetupメソッドの処理となります。そのため、dxrp5コマンド実行時に指定するファイルでは以下のことを行えません。

  • クラスやモジュールの定義
  • 定数の定義

これはRubyのシンタックスとして、メソッド内にクラスや定数などの定義を記述することを禁止しているためです。これを回避するためには、クラス定義や定数の定義は別ファイルに分けて、実行対象のファイルからrequireする必要があります。上のキャプチャで動かしたサンプルも、オリジナルは一つのファイルの中でマップや敵や自機のクラス定義をしていたので、それぞれファイルに分割してrequireした上で動かしています(コードそのものには変更は加えていません)。

何かいいやり方があればいいのですが...もしいい案がある方がいらっしゃったらご指摘願います。

また、既に述べたように、まだまだDXRubyとの完全な互換があるわけではないので、使用できないAPIがたくさんあります。生温い目で見守っていただけたら幸いです。

終わりに

ProcessingじゃなくてほとんどRubyの話じゃねーか、というお叱りの声が聞こえてきそうです。
本当にその通りですごめんなさい。
Advent Calendar参加にあたって、@p5infoさんに概要をお伝えした際には「Processingの可能性としてアリです」と快諾していただけたのですが、もしかしたら実際に記事を読んで憤慨しておられるかもしれません。もしそうであればこっそりとATNDのタイムテーブルから18日の行を削除するか、他のコンテンツに差し替えるなどの対応が考えられますのでご一考ください。

ただ、上でもちょろっと書いたように、ruby-processingのシンタックスでもDXRubyRP5は利用可能です。なので、例えばDXRubyにはSpriteという簡単に衝突判定をしてくれるクラスがあるのですが、こういったDXRubyの便利な部品をruby-processingによるアプリ開発時に部分的に利用することも用途として考えられます。何が言いたいかというと、ruby-processing(または本家Processing)を使うに当たって、こんな感じで便利なライブラリなりフレームワークなりを作れば自分や誰かが幸せになれるかも知れませんよ、てなところです。
本投稿は「広義のProcessingの可能性」としてご容赦いただければ、などという都合の良い言い回しにて締めさせていただきます。

ちなみに、DXRuby Advent Calendar 2013@aoitakuさん主催で執り行われており、作者の@mirichiさん含むDXRubyユーザさん達が熱く盛り上がっています。
もしDXRubyに興味が湧いた方がいらっしゃったら、是非覗いてみてください。