2016/12/11(日) [n年前の日記]
#2 [ruby][gosu] Windows + Ruby + Gosu を使ってもうちょっと色々試す
もうちょっと色々試してみる。
◎ タイルマップでBGを描画してみる。 :
タイルマップでBGを描画してみよう。
とりあえず、2枚描画してみる。メガドライブがBG2枚だったから、Gosuも2枚描けたら、メガドラ程度のゲーム画面は実現できる、はず。
_tilemap_bg_test.rb
_tmp_bg1.png
_tmp_bg2.png
ruby tilemap_bg_test.rb と打って実行してみる。
描画できてる。メガドラ程度にはなりそう。
少し解説。
タイルマップ用の画像は、えてして、たくさんのチップ画像が一枚の画像にまとめてあったりするので、画像を分割して読み込む必要が出てくる。
Gosu も、画像を分割して読み込む機能が用意されてる。以下のように書けばOK。結果は、Gosu::Image の配列で返ってくる。
タイルマップ用の画像なので、画像の端がクッキリスパッとしていてくれないと困る。敷き詰めた時に隙間が入ったら見苦しい。故に、:tileable => true を指定してる。
描画については…。例えば DXRuby なら、タイルマップBGを描画するための機能があらかじめ用意されてるけど、どうやら Gosu には無いらしい。仕方ないので、Rubyスクリプト内でループを回して描画してる。
とりあえず、2枚描画してみる。メガドライブがBG2枚だったから、Gosuも2枚描けたら、メガドラ程度のゲーム画面は実現できる、はず。
_tilemap_bg_test.rb
require 'gosu'
# 背景描画用クラス
class Map
# コンストラクタ
# @param wdw_w [Integer] ウインドウ横幅
# @param wdw_h [Integer] ウインドウ縦幅
def initialize(wdw_w, wdw_h)
@wdw_w = wdw_w
@wdw_h = wdw_h
# 画像の読み込み
@imgs = []
["tmp_bg1.png", "tmp_bg2.png"].each do |fn|
# Gosu::Image.load_tiles()を使えば画像を分割して読み込める。
# ここでは 512x512の画像を、1チップ32x32として読み込んでるので
# 16x16個 = 256個の画像に分割されているはず
imgs = Gosu::Image.load_tiles(fn, 32, 32, :tileable => true)
@imgs.push(imgs)
end
# タイルマップ用の配列を作成
@bgarr = []
c = 0
16.times do |by|
@bgarr[by] = []
16.times do |bx|
@bgarr[by][bx] = c
c = (c + 1) % 256
end
end
end
# 背景描画処理
# @param bx [Integer] BG座標x
# @param by [Integer] BG座標y
# @param n [Integer] BG番号(0 or 1)
def draw(bx, by, n)
cw, ch = 32, 32 # セル1つ分のサイズ
bx = bx.to_i
by = by.to_i
imgs = @imgs[n]
lenx = @bgarr[0].length
leny = @bgarr.length
cx = (bx / cw) % lenx
cy = (by / ch) % leny
sx = (bx % cw)
sy = (by % ch)
wcnt = @wdw_w / cw + 1
hcnt = @wdw_h / ch + 1
hcnt.times do |y|
wcnt.times do |x|
k = @bgarr[(cy + y) % leny][(cx + x) % lenx]
imgs[k].draw(x * cw - sx, y * ch - sy, 0)
end
end
end
end
class MyWindow < Gosu::Window
# コンストラクタ
def initialize
super 640, 480, false
self.caption = "Tilemap BG Test"
@maps = Map.new(self.width, self.height) # マップ表示用
# self.width、self.height はウインドウの横幅、縦幅になってる
@bgx = [0, 0]
@bgy = [0, 0]
@frame = 0
end
# 更新処理
def update
# BGスクロール位置を更新
ang = Math::cos(@frame * Math::PI / 180.0)
@bgx[0] = -64 + (64.0 * ang) + 640
@bgx[1] = -128 + (128.0 * ang) + 640
@bgy[0] = (@bgy[0] + 2)
@bgy[1] = (@bgy[1] + 6)
@frame += 1
end
# 描画処理
def draw
2.times do |i|
@maps.draw(@bgx[i], @bgy[i], i)
end
end
# キーボードチェック
def button_down(id)
if id == Gosu::KbEscape
# ESCキーが押されたらウインドウを閉じて終了する
close
end
end
end
window = MyWindow.new
window.show
使用画像は以下。_tmp_bg1.png
_tmp_bg2.png
ruby tilemap_bg_test.rb と打って実行してみる。
描画できてる。メガドラ程度にはなりそう。
少し解説。
タイルマップ用の画像は、えてして、たくさんのチップ画像が一枚の画像にまとめてあったりするので、画像を分割して読み込む必要が出てくる。
Gosu も、画像を分割して読み込む機能が用意されてる。以下のように書けばOK。結果は、Gosu::Image の配列で返ってくる。
# 画像を分割して読み込む
@imgs = Gosu::Image.load_tiles("画像ファイル名", 32, 32, :tileable => true)
「32, 32」のあたりは、1チップが何x何ドットなのか、の指定。例えばこれが DXRuby だったら、「画像を何分割するか」で指定するけど、Gosuの場合は「チップの大きさ」を指定するので少し注意。タイルマップ用の画像なので、画像の端がクッキリスパッとしていてくれないと困る。敷き詰めた時に隙間が入ったら見苦しい。故に、:tileable => true を指定してる。
描画については…。例えば DXRuby なら、タイルマップBGを描画するための機能があらかじめ用意されてるけど、どうやら Gosu には無いらしい。仕方ないので、Rubyスクリプト内でループを回して描画してる。
◎ ESCキーを押したら終了するようにしたい。 :
前述のスクリプトは、ESCキーを押すと終了することができる。処理してるのは以下の部分。
今気づいたけど、上の部分、Ruby なら以下のように書けますな…。1行で済む…。
キー種類は、 _Module: Gosu - Documentation for gosu/gosu (master) を眺めてもらえば予想できるかなと。KbXXXXX てのがキー種類、だと思われます。
# キーボードチェック
def button_down(id)
if id == Gosu::KbEscape
# ESCキーが押されたらウインドウを閉じて終了する
close
end
end
button_down(id) というメソッドを書くと、何かキーが押された時に、このメソッドを呼んでくれるらしい。id にはキーの種類が入ってるので、Gosu::KbEscape (ESCキー)だったら、close を呼んでウインドウを閉じている。今気づいたけど、上の部分、Ruby なら以下のように書けますな…。1行で済む…。
def button_down(id)
close if id == Gosu::KbEscape
end
キー種類は、 _Module: Gosu - Documentation for gosu/gosu (master) を眺めてもらえば予想できるかなと。KbXXXXX てのがキー種類、だと思われます。
◎ スプライト相当を描画してみる。 :
スプライト相当を描画してみる。64x64ドットの画像を、512枚描画してみよう。
_sprite_test.rb
_tmp_ufo.png
ruby sprite_test.rb で実行してみる。
動きました。
DXRuby には、Sprite というクラスが用意されてたりするけど、Gosu には無いみたい。なので、スプライト相当のクラスを自前で作って、描画処理等もその中で書いている。
_sprite_test.rb
require 'gosu'
# スプライト相当のクラス
class MySpr
# コンストラクタ
def initialize(x, y, vx, vy, img, scrw, scrh)
@fpx = x
@fpy = y
@fpvx = vx
@fpvy = vy
@scrw = scrw
@scrh = scrh
@w = img.width
@h = img.height
@whf = @w / 2
@hhf = @h / 2
@image = img
@x = (@fpx - @whf).to_i
@y = (@fpy - @hhf).to_i
end
# 更新処理
def update
@fpx += @fpvx
@fpy += @fpvy
@x = (@fpx - @whf).to_i
@y = (@fpy - @hhf).to_i
@fpvx *= -1 if @x + @w >= @scrw or @x <= 0
@fpvy *= -1 if @y + @h >= @scrh or @y <= 0
end
# 描画処理
def draw
@image.draw(@x, @y, 0)
end
end
class MyWindow < Gosu::Window
# コンストラクタ
def initialize
super 640, 480, false
self.caption = "Sprite Test"
scrw = self.width
scrh = self.height
# スプライト発生
pmax = 512
img = Gosu::Image.new("tmp_ufo.png", :tileable => true)
x, y = 320, 240
@sprgroup = []
pmax.times do |i|
rad = (i * 360 / pmax) * Math::PI / 180
dx = 3.0 * Math::cos(rad)
dy = 3.0 * Math::sin(rad)
@sprgroup.push(MySpr.new(x, y, dx, dy, img, scrw, scrh))
end
@frame = 0
end
# 更新処理
def update
@sprgroup.each do |spr|
spr.update
end
@frame += 1
end
# 描画処理
def draw
@sprgroup.each do |spr|
spr.draw
end
end
# キーボードチェック
def button_down(id)
if id == Gosu::KbEscape
# ESCキーが押されたらウインドウを閉じて終了する
# Gosu::Window.close() を呼ぶとウインドウが閉じる
close
end
end
end
window = MyWindow.new
window.show
使用画像は以下。_tmp_ufo.png
ruby sprite_test.rb で実行してみる。
動きました。
DXRuby には、Sprite というクラスが用意されてたりするけど、Gosu には無いみたい。なので、スプライト相当のクラスを自前で作って、描画処理等もその中で書いている。
◎ FPS(フレームレート)を測定する。 :
ところで、自分の手元の環境で Gosu を動かすと、なんだか動きがガクガクするんだよな…。Gosuって、本当に60FPS出てるのかな?
不安になってきたので、 _フレームレートの計測 を参考にして、FPSを測定する処理を書いてみたり。
_framerate_test.rb
ruby framerate_test.rb で実行。
計測できてるみたい。しかし、どうも微妙に60FPSに足りてない感じが。なんでだろ。
不安になってきたので、 _フレームレートの計測 を参考にして、FPSを測定する処理を書いてみたり。
_framerate_test.rb
require 'gosu'
# フレームレートを測定するクラス
class FrameRate
attr_reader :framerate
def initialize
@cnt = 0
@framerate = 0
@start_time = Time.now
end
# 毎フレーム呼ぶ
def update
@cnt += 1
n = Time.now
nd = n - @start_time
return if nd < 1.0
@framerate = @cnt / nd
@start_time = n
@cnt = 0
end
end
# メインウインドウ
class MyWindow < Gosu::Window
# コンストラクタ
def initialize
super 640, 480, false
self.caption = 'Sprite Test'
@font = Gosu::Font.new(20)
@frate = FrameRate.new
end
# 更新処理
# def update
# end
# 描画処理
def draw
@frate.update
f = @frate.framerate.to_s
@font.draw("FPS #{f}", 10, 10, 0)
end
# キーボードチェック
def button_down(id)
# ESCキーが押されたらウインドウを閉じて終了する
# Gosu::Window.close() を呼ぶとウインドウが閉じる
close if id == Gosu::KbEscape
end
end
window = MyWindow.new
window.show
ruby framerate_test.rb で実行。
計測できてるみたい。しかし、どうも微妙に60FPSに足りてない感じが。なんでだろ。
◎ 合体させる。 :
今まで書いてきたソレを全部合体させてみる。ソースが長くなってきたので貼らないけど。
やってることは、スプライト相当 512枚 + タイルマップBG x 2枚の描画。その程度の描画ができれば、一般的というか、若干レトロ風なリアルタイム2Dゲームも書けるだろう…。
_gosu_fps_test.rb
使用画像。
_tmp_bg1.png
_tmp_bg2.png
_tmp_ufo.png
ruby gosu_fps_test.rb で実行。
何故か微妙に60FPSに足りてなくて、そのせいかガクガクしてる感じもするけど、このくらいの速度で動けば、まあ、ゲームになるんじゃないかな、と。
やってることは、スプライト相当 512枚 + タイルマップBG x 2枚の描画。その程度の描画ができれば、一般的というか、若干レトロ風なリアルタイム2Dゲームも書けるだろう…。
_gosu_fps_test.rb
使用画像。
_tmp_bg1.png
_tmp_bg2.png
_tmp_ufo.png
ruby gosu_fps_test.rb で実行。
何故か微妙に60FPSに足りてなくて、そのせいかガクガクしてる感じもするけど、このくらいの速度で動けば、まあ、ゲームになるんじゃないかな、と。
◎ 別のWindows機で動かしてみる。 :
このスクリプトを他の環境に持っていっても動くのかどうか。気になる…。
ちなみに、今まで動かしてた環境(ハードウェアスペック)は以下。
前述の gosu_fps_test.rb を、他のWindows機でも動かせるように、ocra で exe化。ocra を使うと Rubyスクリプトを exe化してくれる。使い方は、DXRuby の解説記事が参考になるかと。
_ocraによるexe化
少し古い別PCに持っていって動かしてみた。
AMD/ATI製とは言え所詮はオンボードGPUなので、どうかなーと思ったけど。60FPS前後で動いてくれた。
一昔前の省電力性を重視したCPU + 一昔前のオンボードGPUでもこの程度動くなら、Gosuを使ってゲームを作っても動かせる環境は比較的多いのではあるまいか、と。
もっとも、Atom CPU あたりが載った環境で動かすとどうなるか、気になるところではあるけど…。もちろん初期のAtomでは話にならないはずだけど、今時のAtomならどうなのだろうと。60FPSが出ちゃうのか、ガクガクしちゃうのか、ちょっと気になるなと。
ちなみに、今まで動かしてた環境(ハードウェアスペック)は以下。
- CPU : Intel Core i5-2500 (4コア, 3.3GHz)
- GPU : NVIDIA GeForce GTX 750 Ti (RAM 2GB)
- RAM : 8GB (DDR3-1333/PC-10600 SDRAM, 4GB x 2)
- OS : Windows10 x64
前述の gosu_fps_test.rb を、他のWindows機でも動かせるように、ocra で exe化。ocra を使うと Rubyスクリプトを exe化してくれる。使い方は、DXRuby の解説記事が参考になるかと。
_ocraによるexe化
少し古い別PCに持っていって動かしてみた。
- CPU : AMD AthlonII X4 605e (4コア, 2.3GHz, TDP 45W) …当時としては処理能力より省電力性を重視したCPU。
- M/B : ASRock M3A785GMH/128M (MicroATX, Socket AM3, AMD 785G)
- GPU : オンボードビデオ Radeon HD 4200 (RS800, VRAM 624MB) AMD785Gチップセットと統合されてる。
- RAM : 8GB (DDR3-1333/PC3-10600, 4GB x 2)
- OS : Windows7 Home x64
AMD/ATI製とは言え所詮はオンボードGPUなので、どうかなーと思ったけど。60FPS前後で動いてくれた。
一昔前の省電力性を重視したCPU + 一昔前のオンボードGPUでもこの程度動くなら、Gosuを使ってゲームを作っても動かせる環境は比較的多いのではあるまいか、と。
もっとも、Atom CPU あたりが載った環境で動かすとどうなるか、気になるところではあるけど…。もちろん初期のAtomでは話にならないはずだけど、今時のAtomならどうなのだろうと。60FPSが出ちゃうのか、ガクガクしちゃうのか、ちょっと気になるなと。
[ ツッコむ ]
以上です。