Hatena::ブログ(Diary)

mirichiの日記 このページをアンテナに追加 RSSフィード

2009-12-12

Tilemapクラスの実装

TilemapのマップデータはTableオブジェクトで格納されるが、その値はタイルIDと8方向の境界パターンの組み合わせで決まる。

自分で調べると面倒なこういうのも、ネットで検索すると調査した先人がちゃんと存在する。

http://supponweblog.blog88.fc2.com/blog-entry-9.html

また、この記事からリンクされている先のExcelデータと組み合わせれば、オートタイル基本パターンは掴める。

集合パターンのほうはまた値が違って、0〜15までの値を使うようで、そっちは自分で調べた。

このあたりはFreeRGSSを使う場合は必要な情報になるから(標準マップエディタないし)、そのうちドキュメントにまとめるつもりだ。

しかしRGSSでRPGを作りたければそれこそツクールを使えばいいわけで、ツクールを買うお金がもったいない人を対象にしているわけではないから、ツクールレベルのRPGエディタを作る気はまったく無い。でもマップエディタはほしい。誰か作ってくれないかな。


さて、FirstSeedMaterialさまからサンプルマップ集を頂いて。

http://www.tekepon.net/fsm/modules/refmap/

ツクールで適当なスクリプトを書いて描画したのがこれ。

f:id:mirichi:20091212145430p:image

このマップデータを抜き出して、FreeRGSSで描画したのがこれ。

f:id:mirichi:20091212145613p:image

2レイヤ描画、影、水のアニメーションは未対応だが、とりあえずTilemapのデータ形式には対応できたようだ。


ちなみにCPU負荷はこんな感じ。

f:id:mirichi:20091212151544p:image

まだ処理が足りないからもう少し増えるだろうが、高速化はまったくしてない無駄だらけのコードだからいじれば速くなるかもしれない。

いずれにせよ、本家RGSSよりは負荷は低く抑えられているようだ。別に俺がすごいわけではなく、Direct3DとGPUのおかげ。

5VX5VX 2010/04/02 18:20 RGSS2組み込みクラスの分析・再現の記事、興味深く拝見しました。

組み込みクラスは、ヘルプに載っている以外のメソッドや、
インスタンス変数を持っていないようなのですが、
再現するにあたり、便宜上これらを定義なさっているのでしょうか?

mirichimirichi 2010/04/02 21:13 いらはりませ。
前にRubyで作ってみたときにはインスタンス変数やメソッドは色々と追加定義してました。
FreeRGSSはCで書いた拡張ライブラリなので、内部では色々やってますが、本家RGSS2に無いものはRubyから見えないように全て隠しています。

5VX5VX 2010/04/03 23:51 ありがとうございます。
Ruby では追加定義しないと厳しいですよね。
本家も Ruby 以外で処理していると聞くので、やはり隠す形をとっているのですかね。

現在、Tilemap の再現を(まずは Ruby で)試みているのですが、
本家 RGSS2 ではどのタイミングでマップを描画していると推測できますでしょうか。
Spriteset_Map#update_tilemap 内の @tilemap.update をコメントアウトし、
Spriteset_Map#create_tilemap 内を書き換える等、
いくつかの検証をしてみましたが、つきとめることができませんでした。

mirichimirichi 2010/04/04 12:55 Rubyではサブクラスにインスタンス変数やメソッドを隠すことができないので、クラスを継承すると色々追加定義したものが見えてしまうのがどうにも苦しいところです。
っていうかTilemap作ってるんですか・・・ツワモノですね!

RGSS2の全ての描画はGraphics.update内でまとめてやっています。
Tilemap.newしたときに内部情報に登録されて、SpriteやPlaneなどと同じように処理されると思っています。
マニュアルに「キャラクターの下に表示されるタイルのZ座標は0、上に表示されるタイルのZ座標は200」と書いてあるので、描画は上下2段を別々に処理していると推測していて、FreeRGSSでは
・内部Sprite配列を作って、Viewportに関連付けられていないSprite、Plane、Viewport、Tilemapを生成され次第順次格納する。Tilemapは上下2つに分けて2つ格納。
・Viewportに関連付けられたオブジェクトはViewportが保持する配列に格納。
・描画時は内部Sprite配列をZソートして描画。
・Viewportの描画時はViewportが保持する配列をZソートして描画。
という流れで処理しています。

5VX5VX 2010/04/04 17:09 >っていうかTilemap作ってるんですか・・・ツワモノですね!
作ろうとしたゲームが 32×32 のタイルサイズでは都合が悪かったので、
必要に駆られてまずは再現から始めたところです。
mirichi さんの詳しい考察を伺って、この先に少し壁を感じました…。

なるほど、描画自体は Graphics.update で行っているのですね。
ということは、その前のどこかで、Tilemap オブジェクト (内部スプライト) へ
各タイルセットビットマップから転送を行っていると思うのですが、
必要な bitmaps[0〜8]、map_data、passages への参照が
オブジェクト作成“後”なので、initialize 時ではないでしょうし、
@tilemap.update をコメントアウトしても初回の描画はされるので、
update 時でもないでしょうし、困っております。

変な事を言ってご迷惑をおかけしていたらすみません。
何かヒントを頂けたらありがたいです。

ちなみに、描画のための Graphics モジュールそのものも RGSS 組み込みなのですが、
FreeRGSS ではご自分で実装されているのですか?

mirichimirichi 2010/04/05 01:20 RGSS2の作りと動作から察するに、Tilemap自体は画像データを持っておらず、描画時にbitmapsを参照して必要な画像データを引っ張ってきて使っているのだと思います。
SpriteもTilemapも、Graphics.update直前で画像データを変更してもきちんと反映されたはずです。

RGSS2を使ってRubyでTilemapを実現するなら、
(a) Tilemap再現オブジェクトに画面サイズの画像を持たせて、bitmapsの画像から16*16単位でBitmap#bltでコピーして、それをPlaneかSpriteで一発描画する。
(b) 16*16サイズで画面を埋めるだけの数のSpriteオブジェクトをTilemap再現オブジェクトの中に持たせて、それぞれSprite#src_rectで16*16サイズの画像位置を指定して描画する。
のどちらかになりそうです。
たぶん5VXさんが考えてるのは(a)のやり方で、RGSS2やFreeRGSSが内部でやってるのはSpriteは使ってませんが(b)に近いやり方だと思います。
しかしRGSS2でRubyでとなると、(b)のやり方は非現実的なので、組込のTilemapに近い動作をさせるのは難しいかもしれません。

>ちなみに、描画のための Graphics モジュールそのものも RGSS 組み込みなのですが、
>FreeRGSS ではご自分で実装されているのですか?
はい、DXRubyのコードを流用して、DirectXで描画する仕組をCで実装してます。
なのでその辺は苦労してませんがTilemapはややこしいのでかなり苦労した上にいまだ未完成です・・・

5VX5VX 2010/04/05 21:31 ありがとうございます。

中身が見えない以上、完全な再現が不可能であることは承知していますので、
似たような挙動になれば、ひとまず目標は達成かなと考えています。
示していただいた指針を参考に進めていきたいと思います。

あまりお邪魔するといけないので、あと2点だけ質問させていただくと、
Tilemap 再現オブジェクトの持つ Sprite をマップサイズではなく画面サイズとしたのは、
処理の負担を減らすためでしょうか?
blt を用いて Tilemap 再現オブジェクトの持つ Sprite に転送する場合、
そのタイミングは Tilemap#update がよいでしょうか?
(毎フレーム描画は非現実的なので、描画フラグが必要になるかもしれませんが…)

mirichimirichi 2010/04/05 23:16 >Tilemap 再現オブジェクトの持つ Sprite をマップサイズではなく画面サイズとしたのは、
>処理の負担を減らすためでしょうか?
すみません、単純に間違いです。
現実的な時間でマップサイズの画像全体を生成できるなら、まとめてやってしまうのがいいと思います。
時間がかかりすぎる場合は、分割して必要なときに必要な分だけ生成しなければならないかもしれません。

>blt を用いて Tilemap 再現オブジェクトの持つ Sprite に転送する場合、
>そのタイミングは Tilemap#update がよいでしょうか?
RGSS2のTilemapに近い使い勝手を目指すなら、「bitmapsとpassagesを設定した後でmap_dataを設定すること」という条件だけ追加して、map_data=メソッドの中でマップ画像全体を生成するがいいのではないかと思います。
bitmapsとpassagesの中でもチェックして、3つとも全部設定されたらマップ画像生成、というふうにするとその制限も無くなります。
で、Spriteでox、oyをズラしてやれば簡単にスクロールもできますし、描画自体はRGSS2では最高速になりそうです。SpriteじゃなくてPlaneにすればマップのループもできますね。

まとめて全体を生成するのに時間がかかりすぎて現実的じゃない場合、ある程度スクロールして次の画像が必要になった時にTilemap#update内で生成することになるでしょう。
やり方は色々ありますが、だいたい生成にかかる時間と実装の大変さ(面倒くささ)とのトレードオフになると思います。

面白い話題だと思うので、また何か悩むこととか疑問とかあったら気軽にどぞー。

5VX5VX 2010/04/07 23:32 ありがとうございます。

VX のマップエディタに合わせて 500×500 タイル分のサイズを
Tilemap 内部スプライトのビットマップに持たせようと思ったら、
Bitmap は 2048(=64タイル) までしか生成できませんでした。
少なくとも、blt を使った方法では、重さ関係なく一括描画できないですね。
まぁ、私の作品では 64 タイルでも十分すぎるのですが。

それでは、試行錯誤しながらの製作に取りかかろうと思います。
まずは、お礼まで。
末筆ながら、今後のいっそうのご活躍をお祈りいたします。

スパム対策のためのダミーです。もし見えても何も入力しないでください
ゲスト


画像認証

トラックバック - http://d.hatena.ne.jp/mirichi/20091212/p1
Connection: close