mieki256's diary



2016/12/11() [n年前の日記]

#2 [ruby][gosu] Windows + Ruby + Gosu を使ってもうちょっと色々試す

もうちょっと色々試してみる。

タイルマップでBGを描画してみる。 :

タイルマップでBGを描画してみよう。

とりあえず、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キーを押すと終了することができる。処理してるのは以下の部分。
  # キーボードチェック
  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
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
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に足りてなくて、そのせいかガクガクしてる感じもするけど、このくらいの速度で動けば、まあ、ゲームになるんじゃないかな、と。

別のWindows機で動かしてみる。 :

このスクリプトを他の環境に持っていっても動くのかどうか。気になる…。

ちなみに、今まで動かしてた環境(ハードウェアスペック)は以下。
  • 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が出ちゃうのか、ガクガクしちゃうのか、ちょっと気になるなと。

以上です。

過去ログ表示

Prev - 2016/12 - Next
1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31

カテゴリで表示

検索機能は Namazu for hns で提供されています。(詳細指定/ヘルプ


注意: 現在使用の日記自動生成システムは Version 2.19.6 です。
公開されている日記自動生成システムは Version 2.19.5 です。

Powered by hns-2.19.6, HyperNikkiSystem Project