D言語でPlatinum

そろそろステージデータを使ったゲーム作りをしたいと思って作ってみた。
 
マップエディタはいつものように、「Platinum」
http://www.hyperdevice.net/
 

import std.file;

/**
 * 2次元マップ<br>
 * ハッシュテーブルによる実装
 */
class Layer2D
{
public:
	/**
	 * データテーブル
	 */
	int[int] data;
	/**
	 * 幅
	 */
	int width;
	/**
	 * 高さ
	 */
	int height;
public:
	/**
	 * コンストラクタ
	 * @param width  幅
	 * @param height 高さ
	 * @param data   データ
	 */
	this(int width, int height, char[] data)
	{
		this.width  = width;
		this.height = height;
		foreach(i, d; data)
		{
			if(d != 0)
			{
				this.data[i] = cast(int)d;
			}
		}
	}
	/**
	 * 座標を指定して値を取得
	 * @param x, y 座標
	 */
	int get(int x, int y)
	{
		if(x < 0 || width <= x || y < 0 || height <= y) return -1; // 場合によっては例外吐いてもいいかも
		if(y*width+x in data)
		{
			return data[y*width+x];
		}
		return 0;
	}
	/**
	 * デバッグ出力
	 */
	void dump()
	{
		for(int j = 0; j < height; j++)
		{
			for(int i = 0; i < width; i++)
			{
				printf("%d", get(i, j));
			}
			printf("\n");
		}
	}
}

/**
 * FMF読み込みクラス
 */
class FMFLoader
{
public:
	/**
	 * 識別文字「FMF_」
	 */
	char[] identifier;
	/**
	 * ヘッダを除いたデータサイズ
	 */
	int datasize;
	/**
	 * マップの幅
	 */
	int width;
	/**
	 * マップの高さ
	 */
	int height;
	/**
	 * チップの幅
	 */
	int chipwidth;
	/**
	 * チップの高さ
	 */
	int chipheight;
	/**
	 * レイヤー数
	 */
	int layercount;
	/**
	 * データのbit数(8/16)
	 */
	int bitcount;
	/**
	 * レイヤー配列
	 */
	Layer2D[] layerArray;
private:
	char[] _file;
	int    _ptr;
public:
	/**
	 * コンストラクタ<br>
	 * TODO: マップデータは8bitのみ
	 * @param filepath FMFファイル
	 * @throw ファイルフォーマット例外
	 */
	this(char[] filepath)
	{
		_file = cast(char[])read(filepath);
		_ptr  = 0;
		identifier = _file.dup[0..4]; // ファイル識別子  [4byte]
		if(identifier != "FMF_")
		{
			throw new Error("Invalid FMF format (" ~ filepath ~ ")");
		}
		_ptr += 4;
		datasize   = readLong(); // ヘッダを除いたサイズ [4byte]
		width      = readLong(); // マップの幅           [4byte]
		height     = readLong(); // マップの高さ         [4byte]
		chipwidth  = readByte(); // チップの幅           [1byte]
		chipheight = readByte(); // チップの高さ         [1byte]
		layercount = readByte(); // レイヤー数           [1byte]
		bitcount   = readByte(); // データのbit数(8/16)  [1byte]
		layerArray = new Layer2D[layercount];
		for(int i = 0; i < layercount; i++)
		{
			Layer2D layer = new Layer2D(width, height, _file[_ptr.._ptr+width*height]);
			layerArray[i] = layer;
			_ptr += width*height;
		}
	}
	/**
	 * 指定レイヤーを取得する
	 * @param id レイヤーID
	 * @return 2次元マップ
	 */
	Layer2D getLayer(int id)
	{
		if(id < 0 || id >= layercount) return null;
		return layerArray[id];
	}
	/**
	 * デバッグ出力
	 */
	void dump()
	{
		printf("identifier='" ~ identifier ~ "' datasize=%d (w,h)=(%d,%d) chip(x,y)=(%d,%d) layer=%d bit=%d\n",
			datasize, width, height, chipwidth, chipheight, layercount, bitcount);
		foreach(i, layer; layerArray)
		{
			printf("Layer-%d\n", i);
			layer.dump();
		}
	}

private:
	/**
	 * 1byte読み込み
	 */
	int readByte()
	{
		if(_ptr - 1 >= cast(int)_file.length) return -1;
		int result = _file[_ptr];
		_ptr += 1;
		return result;
	}
	/**
	 * 4byte読み込み
	 */
	int readLong()
	{
		if(_ptr - 4 >= cast(int)_file.length) return -1;
		int result = _file[_ptr] + (_file[_ptr+1] << 8) + (_file[_ptr+2] << 16) + (_file[_ptr+3] << 24);;
		_ptr += 4;
		return result;
	}
}



void main()
{
	FMFLoader fmf = new FMFLoader("map.fmf");
	fmf.dump();
}

まー、やっていることは、
http://d.hatena.ne.jp/kenmo/20060509
とだいたい同じ。
 
あと、Layer2Dがハッシュテーブルになっているのは、
例えば、シューティングで敵出現情報をマップに埋め込む場合、
多くの部分が「0(存在しない)」になるため、メモリの節約となるからです。
……まー、そんなに気にする必要はないかもしれませんが。
 
注意点は、8bitデータのみ対応、です。
 
 

おまけ情報

あと、Platinumのちょっとしたテクニックはこちら。
http://d.hatena.ne.jp/kenmo/20060522