StarRuby と DxRuby をためしてみた

ゲーム機の VRAM に関する属性情報なども全て表示したくなったので作ってみました。言語は最初は C# を選んだものの、コーディングが面倒とか、キャラクタの動的生成が異様に遅い上に、ラスタライズが気に入らないのでボツ。

C で自分で作ろうかと思ったら、Ruby にいくらかゲーム向けライブラリがあったので使ってみました(ブームは4年ぐらい前に終わったみたい)。候補は StarRuby と DxRuby の2つ。サンプルやリファレンスをみたところ、 DxRuby はウインドウの起動位置が設定できるので選択してみました。

VRAM のキャラは 8x8 dot で、等倍表示だと今のモニタだと小さくて見づらいので2倍に拡大する必要がありますが、DxRuby だとアンチエイリアスとかがきいてしまってドット絵ビューアとして致命的な原因を持つことが判明したのでかなり作ってからボツにしました。(写真ならいいんですけど...)

ボツにしても Ruby は型宣言がないのでライブラリ依存のクラスの初期化や呼び出しの名前や引数の順番を変えるだけでなんとかなるので、ある程度ロスはあるものの StarRuby で完成することが出来ました。

今日は両方向けに同じ動作をするソースを2つ作ってここがよい悪いと書こうとしたのですが、 DxRuby はドキュメントを眺めてみたところ flip もできないみたいなので、途中でやめてしまいました。
DxRuby はそれ以外にも Windows 専用とか、 Window という変数がいつの間にか宣言されているとか、レンダリングに統一感がないとかで、ツールを作る上ではなにかしら微妙でした。本来の目的はゲームを作るためのライブラリで、その為にいろいろ便利にしているようですし、精力的に更新されているので、駄目だというわけではありません。

StarRuby にもキー入力は押しただけしかできないとか、ウインドウの起動位置が設定できない(原因はライブラリの SDL1 がサポートしてない)とか、当たり判定クラスなどはなくてすごく単純とかあるし、3年以上更新がありません。

画面表示を両方あげておきます。

サンプルソース

char.rb

CHAR_A = [
	0x0000_0110,
	0x0000_1210,
	0x0001_2012,
	0x0012_0012,
	0x0120_0012,
	0x1111_1112,
	0x1222_2212,
	0x0200_0002
]
CHAR_0 = [
	0x0111_1100,
	0x1222_2110,
	0x1200_1212,
	0x1201_2012,
	0x1212_0012,
	0x1120_0012,
	0x0111_1120,
	0x0022_2200
]

starruby_sample.rb

# -*- coding: utf-8 -*-
require "./char.rb"
require "starruby"

class Chip
	def chargen(char, pal)
		ret = StarRuby::Texture.new(8, 8)
		y = 0
		char.each{|t|
			8.times{|x|
				ret[x ^ 0b111, y] = pal[t & 0xf]
				t >>= 4
			}
			y += 1
		}
		return ret
	end

	def initialize(char, pal)
		@@chip = chargen(char, pal)
	end
	
	def Draw(screen, dest_x, dest_y, flip_x, flip_y, scale)
#flip は倍率を負の値にすることで実現できる
#この場合、 dest_x, dest_y を原点として右や上に描画されるので並べたい場合は原点を補正する
		x = dest_x
		x += flip_x * @@chip.width * scale
		sx = flip_x == 0 ? 1 : -1
		sx *= scale
		
		y = dest_y
		y += flip_y * @@chip.height * scale
		sy = flip_y == 0 ? 1 : -1
		sy *= scale
		
		screen.render_texture(@@chip, x, y, :scale_x => sx, :scale_y => sy)
	end
end

def background_generate(width, height, scale)
	line = StarRuby::Texture.new(width / scale + 1, 1)
	line.fill(StarRuby::Color.new(0, 0x7f, 0x7f, 0xff))
	c = StarRuby::Color.new(0, 0x3f, 0x3f, 0xff)
	0.step(width / scale, 2){|x|
		line[x, 0] = c
	}
	dest = StarRuby::Texture.new(width / scale, height / scale)
	(height / scale).times{|y|
		dest.render_texture(line, 0, y, :src_x=>y&1, :src_y=>0)
	}
	line.dispose
	ret = StarRuby::Texture.new(width, height)
	ret.render_texture(dest, 0, 0, :scale_x=>scale, :scale_y=>scale)
	dest.dispose
	return ret
end

def pallete_generate
	ar = []
	ar << StarRuby::Color.new(0, 0, 0, 0)
	ar << StarRuby::Color.new(0xff, 0xff, 0xff, 0xff) #R,G,B,A
	ar << StarRuby::Color.new(0x9f, 0x9f, 0x9f, 0xff)
	return ar
end

def char_render(s, y, char, pal)
	char = Chip.new(char, pal)
	3.times{|shift|
		x = 16
		scale = 1 << shift
		2.times{|fy|
			2.times{|fx|
				char.Draw(s, x, y, fx, fy, scale)
				x += 8 * scale
			}
		}
		y += 8 * scale
	}
end

begin
	screen = background_generate(320, 240, 16)
	pal = pallete_generate
	char_render(screen, 8, CHAR_0, pal)
	char_render(screen, 8 + 0x80, CHAR_A, pal)
	
	screen.render_text(
		"Hello, こんにちわ", 32, 100, 
		StarRuby::Font.new("MS Gothic", 12), 
		StarRuby::Color.new(0xff, 0xff, 0, 0xbf)
	)
	
	StarRuby::Game.run(screen.width, screen.height, :title=> "StarRuby Test", :fps =>30){|g|
		g.screen.render_texture(screen, 0, 0)

		if StarRuby::Input.keys(:keyboard).include?(:escape)
			break
		end
	}
end

解説

  • char.rb は 4bpp で上位ビットから下位ビットへ左から右に流れる割とメジャなーチップフォーマットで構成しています。
  • Chip.chargen はそのデータを下位ビットから順番に処理しているので x 座標を xor 演算を使って反転しています。
  • background generate は 16x16 の背景画像を作るメソッドです。
    • line は1本の線で2色交互に点を打った、横線です。
    • dest は1本の線を縦に順番にコピーするのですが、y が奇数の時はコピーもとの x 座標をずらすので、1dot交互の縞模様ができます。
    • これを必要な倍率に拡大してできあがります。