Ruby/SDL への招待


!! CAUTION !!

以下の内容は、2001/3/27時点で、LinuxJapan編集部に渡した原稿の内容です。
それ以降の Ruby/SDL に関する変更は反映されてません。
例えば、Ver0.3 -> 0.6 とかで、require 'sdl' に変わったりしてます。 そのうち反映する予定ですが、ご注意ください。


さて、Ruby から SDL というライブラリを使うことについて書いてみる。

Ruby とは

Ruby とは、まつもとゆきひろ氏の開発したオープンソースのスクリプト言語である。Rubyは、以下の特徴を持つ。

きちんとしたオブジェクト指向言語であり、すべてがオブジェクトである。また動的にプログラム中でクラス定義をすることさえ可能である。

コンパイル・リンクが必要でないため開発サイクルが短くて済むので生産性が高い。 また、変数の宣言は最初の代入で行われるため、ソース自体が短くなる。

現在 CGI と言えば、Perl を使うケースが多いが(私も仕事では、Perlを指名されてしまいます)、それは Perl が文字列を扱うための機能(正規表現 / 柔軟な文字列出力など)が優れているせいである。 Ruby の初期の目標の一つに『Perlの機能を実装する』ということがあったらしく Ruby は、Perlの持つ拡張正規表現をほぼサポートする珍しい言語なのである。

Perl でも構造体を Hash で表現できたが、Ruby の配列は要素にオブジェクトを使えるので、例えばファイルを一気に読みこんで二次元配列にして扱うなどはお手のものである。

Socket クラス及び、HTTP/FTP/POP/SMTP/TELNET を標準でライブラリとして持っている。

例外と言えば Java だが、Rubyのそれはよりシンプルで使いやすい(と私は思っている)。Perl なんて、eval でコードをくくって、$@ の値を評価しなきゃいけないし、例外のない言語なんて、しないで済む苦労をわざわざするためにコードを書いているようなものである。

他にも、

といったところであろうか。

ここでは、Rubyの拡張ライブラリの Ruby/SDL をテーマに Ruby の魅力を説明したいと思う。

Ruby をインストールしよう

あなたが Linuxをお使いならば、(普通使ってますよね ?)以下のコマンドをコンソールから、入力して欲しい。

$ ruby -v

現在の日本語ディストリビューションならば、ほとんどデフォルトでインストールされているか、CD-Romから簡単な作業でインストールできるはずである。 しかしながら、インストールされていても最新のバージョンではないかもしれない。 Ruby は現在発展途上・成長期の言語なので日々新しくなっている。2001/03/24現在の安定版最新は、1.6.3 である。 ちなみに、Linux Kernel などと同様、リリースは、安定版・開発版があり、二つ目の数字が偶数が安定版を意味する。 安定版は、rpm/deb なども提供されるが、make も linux なら簡単である。 ほとんどのディストリビューションで追加で必要となるものはないだろう。

$ ./configure
$ make
$ su
# make install

これで、/usr/local/bin 以下にインタプリタが、/usr/local/lib/ruby 以下にライブラリがインストールされる。 Ruby開発版に関しては、最新ソースは CVS で提供されている。取得の手順などについては、Ruby Official Homepage を参照しよう。

さて、Rubyは動くようになった。何をしようか ?

Ruby の楽しさ

とりあえず、以下を入力してみよう。

$ ruby -e 'puts "Hello, Ruby world"'

Perl などでもそうだが、直接パラメータでスクリプトをインタプリタに渡すことをワンライナーと呼ぶ。これは、『Hello, Ruby world』を表示する。 Rubyでは、ワンライナーからアプリケーションプログラムまで幅広い分野と規模のプログラミングを可能にする。ここでは、Ruby プログラミング自体を紹介する目的ではないので、次のコードで雰囲気だけでも掴んで欲しい。

[get_ver.rb]
def get_http
  require 'net/http'

  host = 'www.ruby-lang.org'
  path = '/en/'

  http = Net::HTTP.new(host)
  http.get(path)[1].delete("\n")
end

html = get_http
print html.scan(%r|<div [^<]+>Ruby Versions.*?</div>|i)[0].gsub(%r|<.*?>|,'')

http を扱うライブラリと正規表現で Ruby の 最新Version を表示する。他の言語だとこう短くは、出来ないものの一つである。

Ruby拡張ライブラリって何 ?

Rubyの拡張ライブラリ機能とは、C/C++で記述したRubyに組み込む形のライブラリだ。 詳しいことは、ソースと一緒に同梱されている README.EXT.jp や Ruby本を参照して欲しい。 Rubyの拡張ライブラリは、perl/pythonなどのそれと比べて以下の特徴がある。

Rubyの持つ GC(ガーベージコレクタ) を利用することによって、開発者がメモリの解放を管理する必要がない。

これまたRubyの持つ例外を拡張ライブラリ内で利用することで処理が複雑にならなくて済む。

r = foo();
if ( r==NULL ){ /* ここで関数の戻値を判断 */
    rb_raise(eError,"Couldn't execute");  /* 例外を起す */
}
   : (A)
   :

などと書くだけで、例外の箇所でインタプリタに制御が戻り、スクリプトで rescue などの指定がなければ実行は終了してしまう。通常なら上の階層の関数でまた戻値を判断してなんてめんどくさい処理が必要だが、Ruby拡張ライブラリでは、C で記述しても楽にプログラムが書けるのである。

もちろん、拡張ライブラリは動的に組み込み可能(一部OSでは不可)だ。 拡張ライブラリの用途としては、

  1. ネィティブコードでの処理の高速化
  2. 有用なライブラリをRubyから利用

の二通りがある。ここでは、もちろん後者を扱い SDLというライブラリをRubyから利用することにしよう。その前に、SDL の紹介だ。

SDL

SDL とは、Simple DirectMedia Layer の略で、Loki Entertainment Software 社の Sam Lantinga が作成したマルチメディアを扱うための API群のライブラリだ。 最近の日本の Linux月刊誌はどれもオンラインソフトウェアの紹介のコーナーがあり毎月いくつかはゲームを取り上げている。SDLを利用しているものもたくさん紹介されている。

Linux Japan
  2001/1 CannonSmash
  2001/2 なし
  2001/3 Marbles / SDL4freepascal
  2001/4 blackhole

Linux magazine
  2001/1 Tux Typing
  2001/2 Chromium B.S.U / Marbles
  2001/3 なし
  2001/4 Rocks'n'Diamonds

SDL の特徴

公式なものだけでも、以下の通り。

[official support on README]
Linux X11 / framebuffer console
Win32 GDI / DirectX
BeOS BDirectWindow
MacOS toolkit / sprockets
FreeBSD 4.X X11
Solaris 2.X X11
IRIX 6.X X11

[unofficial but on README]
Linux SVGAlib / GGI / AAlib / DGA 2.0
OpenBSD X11
AIX X11

[ports on CREDIT]
QNX Neutrino
Amiga
MacOS
OSF/1 (Alpha port)

* linux は、i386/alpha/ppc/sparc のすべて

Mac だとか、Amiga(!!)ってのが、すごい。SDLはそれぞれのプラットフォームのライブラリを吸収する形で移植性の高さを保持しているのがすごいところで、例えば、Win32では、DirectX が利用できれば使い、Linuxなどの環境では、同じコードが、OpenGLを利用して実行される。

基本的には C言語からの利用を前提にされている。よって、各OSのgccだとか、ネイティブのコンパイラ(Win32のVC++/MacのCodeWarriorなど)で開発が可能である。上記のベースとなるライブラリを利用する上でもC言語を採用したことは有利なわけだ。

Loki社では、実際にSDLを用いて、Windowsゲームの移植などを手がけている。これは、機能だけが優れているだけではなく商用ソフト開発にも利用できる程『こなれている』ことを意味する。(ていうか元々そのためのモノなんだけど)

SDL をインストールしよう

Linux 使ってますよね ? 日本語ディストリビューションでデフォルトでインストールされてはいないかも知れないが(未確認です) Ruby 同様、rpm/deb が存在するので、RedHat/Debian ベースな人は、簡単に導入できるはずである。自前で make するのも簡単で、

$ ./configure && make 
$ su
# make install

で、OK である。マシンの環境によっては、configure コマンドにオプションを追加した方が良いかもしれない。利用できるオプションは、./configure --help で表示される。 ライブラリは、/usr/local/lib にインストールされる。/etc/ld.so.conf に /usr/local/lib が無ければ、追加して /sbin/ldconfig を実行しよう。 この後、リブートか、LOGIN しなおせば、OK だ。 test/ 以下には、サンプルのコードが収められているので、make して実行したりソースを読んでみよう。また、docs/以下は、html形式のリファレンスもある(英語だけど)。SDL の Homepage には、日本語のリファレンスやイントロダクションもあるので、是非参考にして欲しい。 とりあえずは Linux 雑誌のバックナンバーのCD-Romや、SDL Homepage の games page などからゲームを入手して試してみてはいかがだろう。

SDL で出来ること

簡単に SDL の持つ機能を説明する。SDL Introduction などからの抜粋だ。

画面のフレームバッファへの APIを提供する。VideoModeを設定し、裏で画面を用意しておいてから転送するサーフェス機能を持つ。 サーフェスは、カラーキーやアルファブレンディング(透過)属性を持ち、可能な限り高速な実装(例えば、ハードウェアアクセラレートなど)を使ってスクリーンに転送される。 表示できる画像フォーマットとしては、Bitmapファイルだけだが、SDL_Image ライブラリを用いると PPM, PCX, GIF, JPEG, PNG, TGA, TIFF といった画像ファイルが扱える。 また、カーソルやアイコンといったウィンドウマネージャとのやり取りの機能も提供されている。

キーボード・マウス・ジョイスティックといった入力やアプリケーションのフォーカスの有無などの状態遷移、プログラムからのキューといったイベントを扱う機能を提供している。例えばマウスボタンイベントの場合、ボタン番号・押されたor離された・カーソルのx,y座標を取得できる。

8/16bit,Stereo/Mono の raw / MS-ADPCM WAVE ファイルの再生をサポートしている。 実際には、関連するライブラリである SDL_Mixer を用いるケースが多いようである。SDL_Mixer には便利な API 及び、mp3 などのフォーマットがサポートされている。

CD-Rom よりサウンドを再生するのに必要な API 。ドライブの選択、トラックの選択やフレーム指定での再生などをサポートする。

単純な Thread 生成および、同期のためのセマフォを提供する。

その他にも、1/1000秒単位の Timer や、システムのエンディアン検出及び変換ルーチンなどがあり、ゲームなどを開発するための様様な API が提供されているのが判るだろう。

Ruby/SDL

さて、この二つを組み合わせると『マルチプラットフォームで、コンパイルなしにオブジェクト指向にゲームが作れる』ことになる。 以下は、簡単な C のサンプルコードだ。引数で与えた bmpファイルを 640x480 のウィンドウに表示する。

[viewbmp.c]
01 #include "SDL.h"
02 #include <stdio.h>
03 
04 SDL_Surface *screen;
05 SDL_Surface *image;
06 SDL_Event event;
07 char *filename;
08 int done;
09 
10 main(int argc,char *argv[]){
11   filename=argv[1];                   /* (A)引数の処理 - ここから */
12   if (filename==NULL){
13     fprintf(stderr,
14         "SDL sample\nview bitmap file\n\n  usage: %s [bmp-file]\n",
15             argv[0]);
16     exit(1);
17   }                                                 /* - ここまで */
18 
19   if(SDL_Init(SDL_INIT_VIDEO)<0){ /* (B)スクリーン準備 - ここから */
20     fprintf(stderr,"couldn't initialize SDL:%s\n",SDL_GetError());
21     return(-1);
22   }
23   atexit(SDL_Quit);
24   
25   screen=SDL_SetVideoMode(640,480,16,SDL_SWSURFACE);
26   if(screen==NULL){
27     fprintf(stderr,"couldn't set 640x480x16 video mode: %s\n",SDL_GetError());
28     return(-1);
29   }                                                /* - ここまで */
30   
31   SDL_WM_SetCaption("SDL viewbmp", filename);   /* (C)キャプション指定 */
32   
33   image=SDL_LoadBMP(filename);   /* (D)画像ファイルを表示 - ここから */
34   if(image==NULL) {
35     fprintf(stderr,"couldn't load bmp-file: %s\n",SDL_GetError());
36     return(-1);
37   }
38   SDL_BlitSurface(image,NULL,screen,NULL);
39   SDL_UpdateRect(screen,0,0,0,0);                        /* ここまで */
40 
41   done = 0;                      /* (E)イベント取得・処理 - ここから */
42   while (!done) {
43     if (SDL_PollEvent(&event) ) {
44       switch (event.type) {
45       case SDL_QUIT:
46         done = 1;
47         break;
48       case SDL_MOUSEBUTTONDOWN:
49         done = 1;
50         break;
51       case SDL_KEYDOWN:
52         if (event.key.keysym.sym == SDLK_ESCAPE) {
53           done = 1;
54           break;
55         }
56       }
57     }
58   }                                                      /* ここまで */
59 
60   SDL_FreeSurface(image);        /* (F)後始末 */
61   return(0);
62 }

これと同等の機能の Ruby/SDL 実装のスクリプトは以下の通り。

[viewbmp.rb]
01 require 'rubysdl'
02 
03 filename = ARGV.shift           # (A)引数の処理 - ここから
04 if filename.nil?
05   STDERR.print <<EOM
06 Ruby/SDL sample
07 view bitmap file
08 
09   usage: #{$0} [bmp-file]
10 EOM
11   exit 1
12 end                                           # - ここまで
13 
14 SDL.init SDL::INIT_VIDEO    # (B)スクリーン準備 - ここから
15 
16 screen = SDL::setVideoMode(640,480,16,SDL::SWSURFACE)
17                                               # - ここまで
18 SDL::WM::setCaption "Ruby/SDL viewbmp", filename  # (C)キャプション指定
19 
20 image = SDL::Surface.loadBMP(filename) # (D)画像ファイルを表示 - ここから
21 SDL.blitSurface(image.displayFormat,0,0,0,0,screen,0,0)
22 screen.updateRect 0,0,0,0                                    # - ここまで
23 
24 event=SDL::Event.new                   # (E)イベント取得・処理 - ここから
25 while true 
26   if event.poll != 0 then
27     case event.type
28       when SDL::Event::QUIT, SDL::Event::MOUSEBUTTONDOWN
29         break
30       when SDL::Event::KEYDOWN
31         break if event.keySym == SDL::Key::ESCAPE
32     end
33   end
34   SDL::Key.scan
35 end                                                         # - ここまで
36 exit

まず最初に気付くのは、Rubyスクリプトの行数の少なさである(62 vs 36)。一番の原因は、変数宣言の無い事と、関数の戻値の判断処理が無いせいである。例えば存在しないファイル名を渡した場合、(D)の処理途中でエラーが発生する。C ではコードで記述しなければいけないことが、Rubyスクリプトだと一行も必要ないのである。この場合インタプリタは、エラーメッセージを表示して終了するが、例えば rescue 節で捕捉して(Java の場合の Catch)ファイル名の再入力を施すといった処理も簡単に出来る。 (E)のイベント処理部はほぼ同じであるが、C の switch 〜 case 文と Ruby の case 〜 when 文および、while と break の扱いの違いが興味深い。 また、Ruby拡張ライブラリでは、クラスを構造体にラップするマクロ Data_Wrap_Struct にその構造体が消滅するときに実行する関数の指定が出来る。これを利用して画像ファイルの後始末の記述が必要でなくなるのである。 他には、SDL の関数はプレフィックスに『SDL_』が付くが、Rubyから扱うときは SDLクラスのメソッドという扱いになって、階層も判りやすくなるという利点もある。 どうだろう ? Ruby/SDL は、SDL をベースにもっと『楽しくプログラミング』出来ると言えるのではないだろうか ?

Ruby/SDL の現在

まず、これを読んでいるあなたに言わなければいけないことがある。 『Ruby/SDL はまだまだ試験実装である』ということだ。 現在筆者である私たむらの実装と京都大学KMCの大林一平氏<URL:mailto:ohai@kmc.kyoto-u.ac.jp>の実装の二つが存在する。 今回の記事は、大林氏の実装を利用している。なぜならば、

筆者の実装より、完成度が高い

という当たり前の事実であるからだ。ただし、大林氏もREADMEで書かれている通り

>これはまだ開発途中のバージョンでSDLのruby拡張ライブラリ開発の参考に
>するというのが主目的です。

である。また現在判っている問題の一つに SDL の pthread の問題がある。SDL はどうやら、明示的に SDL_CreateThread などを使わない場合も内部で Thread を生成しているらしく、Rubyから exit や raise などで終了した場合に SDL 自体がきちんと終了出来ない問題があるらしい([ruby-ext:01516])。Ruby から SDL を利用する場合、SDL の Thread は使わず Ruby の Thread オブジェクトを使うべきだろうから、拡張ライブラリにインターフェースは用意しなかったとのことだが、意外なところで判明した問題である。Linuxにおいては、SDL の configure 時に、--disable-pthread を指定することで、生じなくなるとのことである。つまり、普通の SDL では問題が起きる可能性があるので、Ruby/SDL を試したい方は、ソースからの make をする必要がある。 大林氏の実装は、SDL_Mixer と SGE (SDL Graphics Extension) がオプションで利用できる。SGE を使うとプラットフォームは、Linux/Win32に限定されるが拡大・縮小や線や図形を書くといったメソッドが利用できる。 また、Version 0.3 からは、SDL_image / SDL_ttf も利用できるようになった。

Ruby/SDL のインストール手順

Ruby/SDL で利用するパッケージの依存関係

Ruby/SDL
  Ruby
  SDL
    SDL_mixer  *
      SMPEG
    SDL_image  *
    SDL_ttf    *
      FreeType
    SGE        *

上記 * 付きのものは、オプションで利用できるライブラリなのでインストールしなくても、Ruby/SDL は使える。それぞれ、

Multi-channel audio mixer .8チャンネルのオーディオと、1チャンネルの音楽を扱える。フォーマットは、

であり、それぞれのライブラリ(SMPEGなど)を必要とする。

SDLで扱える画像形式は、BMPだけだが、SDL_image を利用すると、 PPM/PCX/GIF/JPEG/PNG/TGA/TIFF のフォーマットが利用できる。 それぞれ画像のためのライブラリに依存するが、最近のディストリビューションならば特にインストールする必要はないだろう。

FreeType Library を使って、TrueType Font をサーフェスに描画する。コード指定をユニコードで行う。

SDL にはない線や図形の描画や回転・拡大・縮小を行うライブラリ。SDL_image や FreeTypeを利用した関数もあるが、Ruby/SDLからは利用しない。 サポートしているプラットフォームが、Linux と Win32 だけなのが残念。

関連ライブラリのインストール作業

環境

OS: Omoikane GNU/Linux 1.1 WorkStation(ftp版/kernel2.2.17)

SDL_mixer

まず先に、SMPEG をインストールする。用意したのは、smpeg-0.4.1.tar.gz

$ tar xfvz smpeg-0.4.1.tar.gz
$ cd smpeg-0.4.1
$ ./configure && make
$ su
# make install

続いて、SDL_mixer

$ tar xfvz SDL_mixer-1.0.6.tar.gz
$ cd SDL_mixer-1.0.6
$ ./configure && make

playwave という実行ファイルが作られるので、音が鳴るか試してみよう。

$ su
# make install

SDL_image

$ tar xfvz SDL_image-1.0.10.tar.gz
$ cd SDL_image-1.0.10
$ ./configure && make

showimage という実行ファイルで、make の確認が出来る。

$ su
# make install

SDL_ttf

まず先に、FreeType をインストールする。SDL_ttf の README を見ると FreeType 1.2 の wrapper だと書かれてるが、1.3.1 でも大丈夫だった。

$ tar xfvz freetype-1.3.1.tar.gz
$ cd freetype-1.3.1
$ ./configure && make
$ su
# make install

次に、SDL_ttf をインストールする。

$ tar xfvz SDL_ttf-1.2.2.tar.gz
$ cd SDL_ttf-1.2.2
$ ./configure && make

showfont で、`The quick brown fox jumped over the lazy dog' が表示されれば、OK これは、アルファベット32文字を使った文章の有名な例のひとつだ。

$ su
# make install

SGE

基本的には、Ruby/SDL の README に書かれた通り

$ tar xfvz sge010224.tar.gz
$ cd sge010224

Makefile.conf をエディタで開いて、『C_ONLY = y』『NOTTF = y』の2行を有効に

$ make

example/ 以下にテストプログラムがあるが、NOTTF なため、エラーになる。 めんどくさくて対応していない。(^^;

$ su
# make install

Ruby/SDL のインストール

やっと、本番だ。

$ tar xfvz rubysdl-0.3.tar.gz
$ cd rubysdl-0.3
$ ruby extconf.rb

Ruby拡張ライブラリのお約束。configure のように環境の調査を行いライブラリ設定などを行う。Ruby/SDL では、ここで依存ライブラリの有無を確認し存在するオプションのみを組み込んだ拡張ライブラリ用の Makefile を生成する。存在するはずのライブラリが無いと表示された場合は、テストプログラムなどを確認して欲しい。

問題なければ、

$ make

で、拡張ライブラリ rubysdl.so が作られる。sample/ 以下のスクリプトを試すために

$ cp lib/rubysdl.rb .
$ cd sample/
$ ruby -I.. alpha.rb & 

で、サンプルのスクリプトが実行される。画像ファイルを差し替えたり、スクリプトを修正したり、もし動かなかったらデバッグしたりと楽しんで欲しい。:-) 余談だが、lib/ は、スクリプトのライブラリファイルを置く場所である。このおかげで install 前にサンプルを動かす場合には、上記の作業が必要となるのだ。


alpha.rb execalpha.rb
画像が薄くなったり濃くなったり。
movesp.rb execmove-sp.rb
中央の画像がキーで移動する。
rotate.rb execrotate.rb
画像が回転しながら、拡大縮小する。
sgetest.rb execsgetest.rb
線と丸・四角の描画の組合せ。
testsprite.rb exectestsprite.rb
画像がわらわらと飛び回る。


インストールは、いつもどおりに、

$ su
# make install

これで、どこからでも require 'rubysdl' で Ruby/SDL が利用できる。

最後に

Ruby/SDL は、筆者の公開したのが 今年の 2/9、大林版が 2/26 である。安定度や完成度もまだまだの出来たてホヤホヤである。興味のある方は是非開発に参加して欲しい。まずは、参考 URL などで情報を収集し、ruby-ext にて参加を表明していただければ幸いである。 Ruby のきちんとした言語仕様と、SDL の簡単にマルチメディアが扱えるコンビはプログラミング入門者にとっても有用であると考える。Ruby/SDL を一緒に育ててみませんか ?

参考資料・URL

Rubyソースアーカイブと、付属のマニュアル及びリファレンス

『オブジェクト指向スクリプト言語Ruby』
まつもとゆきひろ/石塚圭樹共著 アスキー

『Ruby デスクトップリファレンス』
まつもとゆきひろ著 オライリージャパン



大林氏の Ruby/SDLページ
<URL:http://www.kmc.kyoto-u.ac.jp/~ohai/>

Ruby 公式ホームページ
<URL:http://www.ruby-lang.org/ja/>

Ruby メーリングリストアーカイブ
<URL:http://blade.nagaokaut.ac.jp/ruby>

メーリングリストトピックス
<URL:http://www.jin.gr.jp/~nahi/RWiki/?cmd=view;name=ML+Topics>



SDL Official Homepage
<URL:http://www.libsdl.org/>

SGE
<URL:http://www.etek.chalmers.se/~e8cal1/sge/>

Adas' Linux ゲームプログラム - 日本での SDL の第一人者(と私は思っている)
<URL:http://www.geocities.co.jp/Berkeley/2093/sdl.html>

SDL Watch - SDL の CVSの動きや ML の話題、関連アプリ・ライブラリの Watching
<URL:http://ee5lance.ee5.yz.yamagata-u.ac.jp/%7Ezinnia/sdl/watch.html>