Jewel-mmo開発日記

RubyでMMORPGを作る過程を記録する日記。 Yokohama.rb をよろしくお願いします。

Rubyゲーム界に必要なのはエディタとHPでは?

俺はもともとBASIC→アセンブラ→C→Rubyとメイン言語を移してきている。

http://d.hatena.ne.jp/mirichi/20090701/p1

オレと一緒だ。いやオレの場合、正確にはBASIC→アセンブラ→C→Perl(挫折)→C++(挫折)→Ruby。 これまで人生でJavaを一行も書いたことがないのだ、えっへん。

という話はどうでもよくって。。。

「Ruby×ゲームを盛り上げるために初心者に魅力的な開発環境を用意する」 ←このために必要なのは、便利なライブラリではなく、専用エディタとホームページじゃないかと思う。

専用エディタ

シンプルなテキストエディタに「スクリプトの実行」と「exeの生成」機能がついてるやつ。 スクリプトって言うのはもちろんRubyスクリプトで、そのRubyにはあらかじめゲーム開発用のライブラリが組み込んであるの。 この専用エディタをインストールすると、自動的にゲーム開発用のRubyも一緒にインストールされるようにしておく。

(前にRubyゲーム界を盛り上げるのにどうしたらいいかを話したときにエディタが必要って話があったと思う。)

ホームページ

HSPみたいなホームページがほしい。 たぶんRubyとは言わなくていい。 もう専用のゲーム開発ツールとして新しい名前をつけて、上の専用エディタ(=開発環境)をダウンロードできるようにしておく。 あとそこにゲームの作り方のチュートリアルを載せておく。このチュートリアルは初心者向けのもので文法とライブラリの説明をすごく単純化したもの。 もちろんエディタの使い方も説明。 このホームページだけ見れば、ゲーム開発に必要な情報が一通り(インストールの仕方、ソースの書き方、実行の仕方、exeの作り方など)手に入るようにしておく。

やっぱり、いくら使いやすいRubyのゲーム用ライブラリを作っても、本当の初心者にはしんどいと思う。 Rubyをインストールする時点でいくつか選択肢があるし、そして、それからライブラリのインストール……。

あとRubyでゲームを作ろうと思った初心者がいたとして、Rubyについて調べるとゲーム開発以外の情報がわんさかと出てきてしまう。 だから「RPGツクール」みたいにRubyとは関係のないピンポイントで情報を検索しやすい名前があったほうが、 ゲームを作りたい人たちのコミュニティが出来やすいんじゃないかな。

permalink
category: Ruby(91)
no tweet

Rubyはそんなに遅くない

ゲーム開発環境としてRubyが遅いと言われるとどうにも納得がいかないので、 下のようなスクリプトで実験。

1000個のスプライトを作成し、スプライトそれぞれを移動、 壁との跳ね返り、自機とのあたり判定を行っている。 秒間60フレームを想定して60回実行する。

$hit_counter = 0

class Sprite
  attr_accessor :x, :y, :vx, :vy
  def initialize(x, y)
    @x, @y = x, y
    @vx, @vy = 1, 1
  end
end

def run(sprites)
  ship = Sprite.new(10, 10)
  60.times do
    sprites.each do |e|
      e.x += e.vx
      e.y += e.vy
      e.vx *= -1 if e.x < 0 or e.x > 100
      e.vy *= -1 if e.y < 0 or e.y > 100
      if ship.x == e.x and ship.y == e.x
        $hit_counter += 1
      end
    end
  end
end

require 'benchmark'
sprites = Array.new(1000) { Sprite.new(0, 0) }
puts Benchmark.realtime { run(sprites) } 

手元のPC(Core2 CPU 6400 @ 2.13Ghz 上の ruby 1.8.7 (2008-08-11 patchlevel 72) [i386-cygwin])で動かした結果、かかった時間は、

0.180000066757202

である。1.0だとギリギリ秒間60回まわせると言うことになるので、まだだいぶ余裕がある。

ちなみに別のPCで計測すると、

0.189450025558472    # ruby 1.8.7 (2009-04-08 patchlevel 160) [i686-linux]
0.0885939598083496   # ruby 1.9.1p0 (2009-01-30 revision 21907) [i686-linux]

という結果に。さすが1.9は速い。

上記処理で負荷の1〜2割程度と言うと、 1000個のキャラクターを凝った動かし方にするのは厳しいと思うけど、 数十から数百個レベルなら問題ないんじゃないかな。 描画ライブラリが十分に速ければ。 ライブラリはCで実装できるから十分な高速化が可能だろうし。 良く知らないのだけど速度的な意味では今時の携帯ゲーム機と同じくらいなんじゃないかな? (いやPSPは圧倒的に速そうだな……)

やっぱり古い環境を知っているからRubyは遅くないって思ってしまうんだろうな。 でも過去の名作ゲームにはもっとずっと厳しい環境で作られてきたものがたくさんあるはず。

(GCで止まるとかはまた別の話。)

permalink
category: Ruby(91)
no tweet

Rubyのグラフィックライブラリの漠然とした案(2)

いや、今回は特にRubyに限った話じゃないんだけど。

引き続き、render(とかdraw)をいちいち呼ばないというのを考えてみる。

画面に描画するオブジェクトをクラスとして設計して、 オブジェクト自身に移動処理や描画処理(renderメソッド)を持たせてしまう、 というのはPSのゲームを作っていたころたどり着いた形で、 当時(3年前)この方向性の理想系として設計したのがMyGame(Rubyで手軽にゲームを作るためのライブラリ)だ。

ライブラリを実装するとき、 描画処理を描画オブジェクトのrenderメソッドとして シンプルに実装できる場合はいいのだけど、 現実問題そう出来ないこともある。 renderの前後である手続きが必要で、 その手続きが複数の描画物をまたがざる終えないような場合だ。

開発環境によっては、 MyGame型のAPIにうまく収まらない状況を何度か経験していて、 結局状況に応じてアドホックな実装を行う破目に。 具体的にはrenderメソッドの呼び出し前後で思いっきりネイティブなライブラリ関数を呼んだり。 いちいち対応すれば、無理やりオブジェクトの中に処理を隠蔽できそうな気はするが、 面倒なのででそこまではやらない。

結局やりたいことは、キャラクターをコントローラや移動アルゴリズムで操作するようなレイヤーから 次のことを隠蔽したいわけだ。

  • 描画物生成・破棄のメモリ管理
  • 描画物生成・破棄の煩雑な手続き
  • 描画の煩雑な手続き

MyGame型の設計では、表示物ひとつひとつを描画プリミティブとしてオブジェクトとして管理していた。 オブジェクトの中に煩雑な処理を隠蔽していたわけだ。

が、最近の考えているのは、描画処理全般を一枚岩で実装して、 外部にはシンプルはAPI(コマンド)を公開する形。 新しい環境で開発をするときは、結局描画クラスを毎回実装することになるのだから、 それなら一枚岩のほうが、汎用的なAPIが楽に実装できるんじゃないかと。

……と思ったけど、改めて考えるとやっぱり描画プリミティブをオブジェクトにするメリットはでかいんだよなあ。 何もかもをオブジェクトにしようと意識しすぎていただけかもしれない。 要はバランスの問題か。

permalink
category: Ruby(91)
no tweet

Rubyのグラフィックライブラリの漠然とした案

こんなのどうだろう。

コンセプトは描画オブジェクトのインスタンスをクライアントで保持し続けないことと、あとループ処理を作らずにクロージャー。

WorldServer.start で世界を作る。 あとは描画物を create するとそこに自動的に描画される。 render メソッドとかいらない。

WorldServer.start

Image.create('sample.bmp', :x => 100, :y => 100)

loop {}

画像を移動させる。

WorldServer.start

img = Image.create('sample.bmp', :x => 100, :y => 100)

loop {
  img.x += 1
  vsync
}

クライアントでインスタンスを保持していなくても、 id があれば描画要素にアクセスできる。 ついでに vsync_callback 。

WorldServer.start

id = Image.create('sample.bmp', :x => 100, :y => 100).id
vsync_callback {
  Image.find(id).x += 1
}

loop {}

id を記録したらインスタンス保持するのと同じか。じゃあ名前でアクセスとか。

WorldServer.start

Image.create('sample.bmp', :name => :sample)
vsync_callback {
  Image.find_by_name(:sample).x += 1
}

loop {}

上のコードは下のようにも書ける。

WorldServer.start

Image.create('sample.bmp').vsync_callback {|e|
  e.x += 1
}

loop {}

下のように書いてもいい。

WorldServer.start

Image.create 'sample.bmp', :vsync_callback => lambda {|e| e.x += 1 }

loop {}

x キーで画像をひとつ追加。 z キーで描画要素をすべて削除。

WorldServer.start

x = 0
Input.x {
  Image.create('sample.bmp', :x => x)
  x += 10
}

Input.z {
  WorldServer.clear
}

loop {}
permalink
category: Ruby(91)
no tweet

Rubyのアップデート

wget ftp://ftp.ruby-lang.org/pub/ruby/1.8/ruby-1.8.6-p114.tar.bz2
tar -xvjpf ruby-1.8.6-p114.tar.bz2

RubyGemsを入れてから、

gem install rails
gem install sqlite3-ruby

これでプラポケは動く。

permalink
category: Ruby(91)
no tweet