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 endbutton_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が出ちゃうのか、ガクガクしちゃうのか、ちょっと気になるなと。
[ ツッコむ ]
以上です。