mieki256's diary



2013/12/22() [n年前の日記]

#2 [dxruby][game] DXRubyでtmxファイルを使ってBG表示

_Tiled Map Editor の .tmxファイルを、tmx ライブラリを使って読み込んで、DXRuby で表示、かつ、地形アタリ処理をしてみるソレが、そこそこなんとか動くようになってきた…ような気がするのでアップロード。

動作してる様子

以下のソースで動いてます。(メイン処理のみ)

mapdisp.rb
#= Tiledの .tmx を読んで、表示と地形アタリ判定をするサンプル
#
# * カーソルキーでカーソル移動
# * WASDキーでスクロール
# * Bキーで、BGアタリレイヤーを表示/非表示
# * Hキーで、ぶら下がり棒とアタリをとるか否かを切替
# * ESCキーで終了

require 'dxruby'
require 'tmx'
require_relative 'dxrbtmx'
require_relative 'bgatari'

font = Font.new(14)
Window.frameskip = false

# 画面サイズ(ドット数)を取得
scrw = Window.width
scrh = Window.height

# BG描画に使うタイル画像を読み込む
bgimg = Image.loadTiles("bg_attari.png", 256/16, 256/16)

# BGマップファイル(.tmx)を読み込む
tmx = DxrbTmx.new("./bg_atari_test.tmx", scrw, scrh)

# レイヤー名を指定して、DXRubyの描画用マップデータ(二次元配列)を取得
atari_layer = tmx.get_layer("bg_atari")
bg_layer0 = tmx.get_layer("layer0")
bg_layer1 = tmx.get_layer("layer1")
bg_layer2 = tmx.get_layer("layer2")
bg_layer3 = tmx.get_layer("layer3")

# 画面を占めるタイル個数を取得
bgtw = tmx.screenwidth
bgth = tmx.screenheight

# BGアタリ判定用テーブルを作成
#
# 以下の引数を渡す
#
# 1. アタリ判定用の画像配列(Image.loadTiles() で読み込んだ画像)
# 2. BGアタリマップデータ(二次元配列)
# 3. 画面横幅ドット数
# 4. 画面縦幅ドット数
# 5. 抜けアタリ番号 (省略時は 0番を抜けとして扱う)
# 6. 非床アタリ開始番号 (省略可能)
# 7. 非床アタリ終了番号 (省略可能)
#
bgatari = BgAtari.new(bgimg, atari_layer, scrw, scrh, 0, 22, 30)

bx, by = 0, 0 # スクロール位置初期化
px, py = scrw / 2, scrh / 2 # カーソル位置初期化
atari_disp = true # BGアタリを描画するかどうかのフラグ
floor = true # ぶら下がり棒のアタリは無視するか否かのフラグ

# カーソル描画用メソッド
def draw_cursor(x, y, col)
  w = 2
  Window.drawLine(x - w, y, x + w, y, col)
  Window.drawLine(x, y - w, x, y + (w + 1), col)
end

# メインループ
Window.loop do
  break if Input.keyPush?(K_ESCAPE) # ESCキー押しで終了

  # Bキーで、BGアタリを描画するかしないかを切り替える
  atari_disp = !atari_disp if Input.keyPush?(K_B)

  # Hキーで、ぶら下がり棒アタリを無視するかしないかを切り替える
  floor = !floor if Input.keyPush?(K_H)

  # WASDキーでスクロール
  spd = 4.0
  bx -= spd if Input.keyDown?(K_A)
  bx += spd if Input.keyDown?(K_D)
  by -= spd if Input.keyDown?(K_W)
  by += spd if Input.keyDown?(K_S)
  
  # カーソルキーでカーソル移動
  spd = 1.0
  px += Input.x * spd
  py += Input.y * spd

  # BG描画
  if atari_disp
    # BGアタリのみ描画
    Window.drawTile(0, 0, atari_layer, bgimg, bx, by, bgtw, bgth)
  else
    # 通常BGのみ描画
    Window.drawTile(0, 0, bg_layer3, bgimg, bx / 8, by / 8, bgtw, bgth)
    Window.drawTile(0, 0, bg_layer2, bgimg, bx / 4, by / 4, bgtw, bgth)
    Window.drawTile(0, 0, bg_layer1, bgimg, bx / 2, by / 2, bgtw, bgth)
    Window.drawTile(0, 0, bg_layer0, bgimg, bx, by, bgtw, bgth)
  end

  # カーソル座標のBGアタリ番号を取得
  x = bx + px
  y = by + py
  bg_code = bgatari.get_bg_code(x, y)

  # カーソル座標がBGと当たってるかチェック
  chk_hit = bgatari.check_bg_hit(x, y, BgAtari::ADJUST_NONE, floor)
  col = (chk_hit)? [192, 255, 0, 0] : [192, 0, 128, 255]

  # カーソル座標描画
  draw_cursor(px, py, col)

  # 床補正座標を得る
  nx, ny = bgatari.adjust_bg_hit(x, y, BgAtari::ADJUST_UP, floor)
  hosei_code = bgatari.bg_code
  draw_cursor(nx - bx, ny - by, C_CYAN) if nx

  # 天井補正の座標を得る
  nx, ny = bgatari.adjust_bg_hit(x, y, BgAtari::ADJUST_DOWN, floor)
  draw_cursor(nx - bx, ny - by, C_CYAN) if nx

  # 壁補正の座標を得る
  nx, ny = bgatari.adjust_bg_hit(x, y, BgAtari::ADJUST_LEFT, floor)
  draw_cursor(nx - bx, ny - by, C_YELLOW) if nx
  
  nx, ny = bgatari.adjust_bg_hit(x, y, BgAtari::ADJUST_RIGHT, floor)
  draw_cursor(nx - bx, ny - by, C_YELLOW) if nx

  # BGアタリ番号その他の情報を描画
  yh = 16
  Window.drawFont(16, yh * 0, "FPS: #{Window.real_fps}", font)
  Window.drawFont(16, yh * 1, "input CURSOR、WASD,B,H key", font)
  Window.drawFont(16, yh * 2, "#{bg_code} : BG code", font)
  Window.drawFont(16, yh * 3, "#{hosei_code} : BG code (Floor adjust)", font)

end
一見すると長いのですが、見栄えを良くするために行数が増えちゃってるだけで…。処理の実体は、
.tmx の読み込みと変換
tmx = DxrbTmx.new( tmxファイル名, 画面横幅, 画面縦幅 )
レイヤー名を指定してBGマップデータ(二次元配列)を取得
atari_layer = tmx.get_layer(レイヤー名)
BGアタリ処理用のテーブル作成
bgatari = BgAtari.new(BGアタリ用画像配列, BGアタリに使うマップデータ, 画面横幅, 画面縦幅, 抜けアタリ番号, 床として扱わないアタリ開始番号, 床として扱わないアタリ終了番号)
BGアタリ番号を取得
bg_code = bgatari.get_bg_code(x, y)
BGと当たってるかチェック
chk_hit = bgatari.check_bg_hit(x, y, BgAtari::ADJUST_NONE, 床のみチェックするかをtrue/falseで)
BGアタリを元に座標補正(床補正)
nx, ny = bgatari.adjust_bg_hit(x, y, BgAtari::ADJUST_UP, 床のみチェックするかをtrue/falseで)
このあたりです。

使い回しができるように、別クラスにしてみました。
.tmxファイル、画像ファイル、スクリプト、各クラスのドキュメントを、zip にして Dropbox に置いておきます。Public Domain ってことで。

_dxruby_map_disp_20131222.zip

tmxライブラリについて。 :

_tmxライブラリ は、
gem install tmx
でインストールできます。 _multi_json_nokogiri も一緒にインストールされる模様。

tmx、nokogiri、multi_json、どれも MIT License のようですね。

アタリ判定用画像について。 :

アタリ判定用画像は、以下の画像(1タイル = 16x16ドット)を使ってます。

アタリ判定用画像

一番左上から、右に向かって、
  1. 抜けアタリ
  2. 進めない床
  3. フツーの床
  4. 斜め床…
と続いて…。青いところが、ぶら下がり棒用のアタリ。0〜31番までをアタリ判別用として使ってる状態です。

bgatari.rb の中で、この0〜31番を、1個ずつ、16x16ドットを全スキャンして、アルファ値が128以上ならその座標はアタリ有り、として扱ってます。

結局、やってることは、画像を読んで、ドットを見て、
[ [ [y最小値, y最大値] x 横ドット数 ], [ [x最小値, x最大値] x 縦ドット数 ] ]
という配列を作ってるだけなので、事前にそういう配列を用意して渡すように修正すれば、DXRuby 以外のライブラリでも bgatari.rb が使えるようになるのではと。…そういう作りにしといたほうがいいのかな。

先日アップした画像が参考になるかもしれないので再掲。

y最小値とy最大値があればアタリ判定できる

テーブルで持ってしまう

アタリ判定用データの扱いについて。 :

今回は、画像をスキャンして、アタリ判定用データを作ってるわけですが。

このあたり、以下のような作りでもいいんですけど、というか昔の自分は、そういうやり方してたんですけど。
  • あらかじめ、BGマップを作る前に、アタリ種類を決めておく。
  • アタリ種類毎に、数式だの条件分岐だので、必要な値を求める処理を書いておく。(一般的には switch 〜 case、Ruby なら case 〜 when になると思う)
  • BGアタリ番号と、アタリ種類の対応表を作っておく。

しかし…。
  • マップエディタやドットエディタで作業してるうちに、「もうちょっとアタリ種類を増やしたいな…」と考えてしまいがち。
  • アタリ種類を増やすの、メンドクサイ。数式や条件分岐を一々書くのは、メンドクサイ。
  • BGアタリ番号と、アタリ種類の対応表を作るのって、地味にメンドクサイ。
  • BGマップ側で使うアタリ種類の並びが変わっただけでも、対応表を作り直すので、メンドクサイ。
  • 曲線的なアタリの形に対応するのが、メンドクサイ。
とにかくメンドクサイ。

その点、今回のように、画像をスキャンして判別用データを作る方法なら…。
  • アタリ種類を増やしたり、並びを変える作業が、CGツール上で完結する。
  • 人間様が対応表を作らなくて済む。
  • 曲線的なアタリにも対応できる。
メンドクサくない。

その代り、
  • 画像の読み込み、かつ、ドットをチェックできるライブラリが必要。どこでも使い回せるソースにならない。
  • 毎回ゲームを動かすたびに、判別用データと対応表を作り直してるので、その処理時間が、ガチで無駄。
  • アタリ種類分のテクスチャ領域を、無駄遣いしてる。
というデメリットはありますが。

とりあえず、プロトタイプをサクッと、てな段階では、今回のやり方も有効なのかもしれないなと。このあたり、判別用データを別途出力するスクリプトを書くだけでも作業は楽になると思うのですが、そのスクリプトを動かす操作すら、プロトタイプ作成段階ではメンドクサイよなと…。

アプリとして完成間近で、実行バイナリに、無駄な処理も無駄なデータも入れたくない、という状態なら…。ツールを作って、判別用データを出力して、それをソース内に列挙、あるいは、別ファイルとして同梱して読み込む、という流れがベターではないかと。

でも、LL使ってゲーム作ってる時点で…。どうせ富豪的プログラミングをしてるわけですし…。このあたりのデータを毎回作り直しちゃってもいいよな、という気もするのでした。ンMbitのROMに収めて、ンMHzのCPUで動かすわけでもないし。

2017/03/19追記。 :

Dropboxのpublicフォルダが死んだのでファイルの置き場所を変更。

以上です。

過去ログ表示

Prev - 2013/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