mieki256's diary



2018/02/17() [n年前の日記]

#1 [dxruby] DXOpalでタイルマップBGを描画

_DXOpal で、どうにかタイルマップBGを描画することができた。

_DXOpal fake draw_tile test

マップデータは、 _Tiled マップエディタ 1.0.3 で作成して、json形式でエクスポートして用意した。

Rubyソース内にマップデータをずらずらと書いた場合は、初期化でめちゃくちゃ時間がかかったけど、jsonを読む形にしたら圧倒的に速く初期化が終わるようになった。

しかし…。予想はしていたけど、めちゃくちゃ描画が遅い。

640x480のウインドウサイズで、16x16ドットのタイルチップを使ったBGを、4枚表示してる状態だけど…。DXRuby なら 60FPS で動くけれど、DXOpal では10FPSも出ない。ちなみに自分の環境は、CPU : Core i5-2500 (3.3GHz、4コア)、ビデオカード : NVIDIA GeForce GTX 750 Ti。

考えてみたら、空白部分は描画してないとはいえ、最多では ((640 / 16) + 1) * ((480 / 16) + 1) * 4 = 5084個もタイルチップを描画するのだから、そりゃ遅いよな…。しかし、試しにBGを2枚に減らしてみても、それでも遅くて。タイルチップのサイズを16x16にしてるあたりで、どうしても枚数・個数が増えてしまうわけで…。

以下、ハマった点をメモ。

外部ファイルの読み込みができない。 :

DXRuby上ではスンナリ動いたのに、DXOpalではエラーが出て首を捻ってたけど。どうやら、DXOpal は外部テキストファイルを簡単に読み込めないようで。

例えば、ローカルで動かしてる Ruby上なら、以下のような形でテキストファイルが読み込めるけど。
text = File.open("data.txt") {|f| f.read }

DXOpal は、できない。考えてみれば、ブラウザ + JavaScript で動かしているのだから、サーバ上のファイルは読み込めても、HDDに入ってるローカルファイルを読み込めちゃったらマズいよなと。

なので、以下のような書き方をして、外部テキストファイルを読み込んでみたり。

_DXOpal file read test

  url = 'data.txt'
  req = Native(`new XMLHttpRequest()`)
  req.overrideMimeType("text/plain")
  req.open("GET", url, false)
  req.send
  text_data = req.responseText

何をしているかというと…。Native() を使ってJavaScriptの関数を直接呼んでどうにかしてる、という…。 _dxopal/dxopal.rb の require_remote() を参考にして、たぶんこうかな、と。JavaScript、全然分かんないけど…。

加えて、以下の記事が大変参考になりました。ありがたや。

_OpalでJavaScriptのAPIラッパーを作る - Qiita
_OpalのNativeはどのように実装されているのか - Qiita

「%x{ 〜 } って何だろう?」と首を捻ってたけど、その中に JavaScript を直接書けるのですな…。

jsonのparseができない。 :

Opal は jsonライブラリも用意されてるらしいので、外部テキストファイルが読み込めたら後は楽勝だろう、と思ったら、「JSONが初期化できねえ」と怒られ続けて。

結局、以下のような書き方に。

_DXOpal json parse test

  url = 'data.json'
  req = Native(`new XMLHttpRequest()`)
  req.overrideMimeType("text/plain")
  req.open("GET", url, false)
  req.send
  text_data = req.responseText
  data = Native(`JSON.parse(text_data)`)

これまた、Native() を使って JavaScriptの JSON.parse() を直接呼び出しているという…。いいのかコレで。まあ、動いてるから、いいか…。

DXRuby版。 :

せっかくだから、一応、DXRuby版もメモ。まあ、DXRuby の場合、Window.draw_tile() を使えばタイルマップBG描画はできてしまうのだけど。

_disp_map_load_json.rb
# Tiledからエクスポートしたjsonを読み込んでDXRubyで描画する

# require 'dxopal'
# include DXOpal

require 'dxruby'
require 'json'

# Image.register(:bgchip, 'images/bg_attari.png')

# DXOpal
def json_to_hash_dxopal(url)
  req = Native(`new XMLHttpRequest()`)
  req.overrideMimeType("text/plain")
  req.open("GET", url, false)
  req.send
  text_data = req.responseText
  return Native(`JSON.parse(text_data)`)
end

# DXRuby
def json_to_hash_dxruby(url)
  json_text = File.open(url) {|f| f.read }
  return JSON.load(json_text)
end

def get_map_data_from_json(json_file)
  # res = json_to_hash_dxopal(json_file)
  res = json_to_hash_dxruby(json_file)

  d = {}
  d[:tilewidth] = res["tilewidth"]
  d[:tileheight] = res["tileheight"]
  d[:width] = res["width"]
  d[:height] = res["height"]

  layers = {}
  res["layers"].each do |layer|
    name = layer["name"]
    w = layer["width"]
    h = layer["height"]
    org_data = layer["data"]
    layers[name] = Array.new(h){ Array.new(w) }
    h.times do |y|
      w.times do |x|
        code = org_data[y * w + x] - 1
        layers[name][y][x] = (code < 0)? nil : code
      end
    end
  end
  d[:layers] = layers

  return d
end

def draw_tile(x, y, map_array, imgs, start_x, start_y, x_cnt, y_cnt)
  ofs_x = x
  ofs_y = y
  tile_w = imgs[0].width
  tile_h = imgs[0].height
  bg_h = map_array.length
  bg_w = map_array[0].length
  start_x = start_x % (tile_w * bg_w)
  start_y = start_y % (tile_h * bg_h)
  x_mod = (start_x % tile_w).to_i
  y_mod = (start_y % tile_h).to_i
  x_index = (start_x / tile_w).to_i
  y_index = (start_y / tile_h).to_i
  y_cnt.times do |y|
    x_cnt.times do |x|
      ix = (x_index + x).to_i % bg_w
      iy = (y_index + y).to_i % bg_h
      code = map_array[iy][ix]
      next if code.nil?
      px = ofs_x + (x * tile_w) - x_mod
      py = ofs_y + (y * tile_h) - y_mod
      Window.draw(px, py, imgs[code])
    end
  end
end

def game_main(img)
  imgs = img.slice_tiles(img.width / 16, img.height / 16)

  # mapdata = get_map_data_from_json("mapdata/bg_atari_test.json")
  mapdata = get_map_data_from_json("bg_atari_test.json")
  x_cnt = Window.width / mapdata[:tilewidth] + 1
  y_cnt = Window.height / mapdata[:tileheight] + 1
  bg_x, bg_y = 0, 0
  ofs_x, ofs_y = 0, 0

  Window.bgcolor = [32, 64, 128]

  Window.loop do
    break if Input.keyPush?(K_ESCAPE)

    if false
      a = 8
      bg_x -= a if Input.keyDown?(K_LEFT)
      bg_x += a if Input.keyDown?(K_RIGHT)
      bg_y -= a if Input.keyDown?(K_UP)
      bg_y += a if Input.keyDown?(K_DOWN)
    else
      bg_x += 3
      bg_y += 1
    end

    layer_names = ["layer3", "layer2", "layer1", "layer0"]
    spd = 0.25
    layer_names.each do |name|
      x = bg_x * spd
      y = bg_y * spd
      map_data = mapdata[:layers][name]

      # Window.draw_tile(ofs_x, ofs_y, map_data, imgs, x, y, x_cnt, y_cnt)
      draw_tile(ofs_x, ofs_y, map_data, imgs, x, y, x_cnt, y_cnt)

      spd += 0.25
    end

    Window.draw_font(2, 2, "#{Window.real_fps} FPS", Font.default)
    Window.draw_font(2, 32, "bg x,y = #{bg_x}, #{bg_y}", Font.default)
  end
end

# Window.load_resources do
img = Image.load("bg_attari.png")
# img = Image[:bgchip]
game_main(img)
# end

Tiled データ(.png / .tmx)と、エクスポートした json は、zipにして以下に置いときます。License : CC0 / Public Domain ってことで。

_bg_atari_test.zip

以上、1 日分です。

過去ログ表示

Prev - 2018/02 - 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

カテゴリで表示

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


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

Powered by hns-2.19.6, HyperNikkiSystem Project