ビットマップを使ってRPGのマップを読み込む

アバター
T-ARCs
記事: 4
登録日時: 7年前

ビットマップを使ってRPGのマップを読み込む

投稿記事 by T-ARCs » 7年前

一昔前、RPGのマップエディタみたいなのを作らないといけないという固定観念に縛られていた気がするが
ビットマップ使えば、事足りるんじゃないかと思い、
DIBセクションを用意して、ビットマップを読み込むようにしてみた。

他にもビットマップは、工夫さえすれば、色々使えるのではないだろうか。
以下、マップチップ(タイル)とキャラとマップ上のオブジェクトを別々の同じサイズのビットマップで
作ったとして、それぞれ読み込むソースコード。

無料でブログを利用させてもらうので、誰かの何かの役に立つようなソースコード上げて
いけたらいいなと思う。

CODE:

int CMapManager::LoadMapFile(LPCWSTR pwstr_map , LPCWSTR pwstr_chara , LPCWSTR pwstr_obj)
{
// テスト用マップの用意
// DIBセクションの用意
	DWORD rgb;
	BYTE *p = NULL;
	int   i = 0;
	CDIBSection * pdib;
	pdib = new CDIBSection;
	pdib->CreateFromFileW(pwstr_map);
	
	m_map_width_chip          = pdib->GetWidth();
	m_map_height_chip         = pdib->GetHeight();
	m_map_width_absolute      = m_map_width_chip * m_mapchip_width;
	m_map_height_absolute     = m_map_height_chip * m_mapchip_height;
	
	MapMemAlloc(m_map_width_chip , m_map_height_chip);

	for(int y=0;y < m_map_height_chip;y++){
		for(int x=0;x < m_map_width_chip;x++)
		{
			p = pdib->GetPixel(x,y);
			rgb = p[0]|((0xffffff&p[1])<<8)|((0xffffff&p[2])<<16);
			m_pMapChip[x][y].m_init_flg = 1;
			m_pMapChip[x][y].__m_width   = m_mapchip_width;
			m_pMapChip[x][y].__m_height  = m_mapchip_height;		
			m_pMapChip[x][y].m_type = 1;
			m_pMapChip[x][y].m_code = rgb;
			m_pMapChip[x][y].setRotation(0.0f);
			m_pMapChip[x][y].setScalling(1.0f,1.0f);
		}
	}

	m_mapcopy_width   = m_mapscreen_width  / m_mapchip_width;
	m_mapcopy_height  = m_mapscreen_height / m_mapchip_height;
	
	delete pdib;
	pdib = new CDIBSection;
	if(!pdib->CreateFromFileW(pwstr_chara))goto SKIP_CHARA;
	
	for(int y=0;y < m_map_height_chip;y++){
		for(int x=0;x < m_map_width_chip;x++)
		{
			p =  pdib->GetPixel(x,y);
			rgb = p[0]|((0xffffff&p[1])<<8)|((0xffffff&p[2])<<16);
			switch(rgb)
			{
				case 0x00ff0000:
				m_pMapChara[i].m_type = 1;
				m_pMapChara[i].set_mappos(x , y);
				i++;
				break;
				case 0x0000ff00:
				m_pMapChara[i].m_type = 2;
				m_pMapChara[i].set_mappos(x , y);
				i++;
				break;
			}
		}
	}
	
SKIP_CHARA:
	
	delete pdib;
	pdib = new CDIBSection;
	if(!pdib->CreateFromFileW(pwstr_obj))goto SKIP_OBJ;
	
	for(int y=0;y < m_map_height_chip;y++){
		for(int x=0;x < m_map_width_chip;x++)
		{
			p =  pdib->GetPixel(x,y);
			rgb = p[0]|((0xffffff&p[1])<<8)|((0xffffff&p[2])<<16);
			switch(rgb)
			{
				case 0x00ff0000:
				m_pMapObj[i].m_type = 1;
				m_pMapObj[i].set_mappos(x , y);
				i++;
				break;
				case 0x0000ff00:
				m_pMapObj[i].m_type = 2;
				m_pMapObj[i].set_mappos(x , y);
				i++;
				break;
			}
		}
	}

SKIP_OBJ:

	delete pdib;
	
	return 0;
}
DIBセクションを扱うクラスを作ってみた。いや、汚すぎるから、もう少し綺麗にしたほうが良いのは分かりますm(_ _)m

定数が定義されていない(コメントアウトしてあるtarcs_code.hで定義した)
から、それだけ修正すれば使えるはず。

.H

CODE:

#pragma once

	#include <windows.h>
	//#include "tarcs_code.h"

	class CDIBSection;

class CDIBSection
{

public:
	INT			    m__Code;
	BYTE *		    m_lpPixel;
	INT			    m_BitsPerPixel;
	HBITMAP		    m_hBitmap;
	HFONT		    m_hFont;
	HDC			    m_hMemDC;
	COLORREF		m_transparentcolor;
	DIBSECTION	    m_Dibsection;
	BITMAPINFO      m_Bmi;
	
	// 空のDIBセクションを作る
	CDIBSection(HWND hWnd , int w , int h)
	{
		HDC hdc;
	// DIBの情報を設定する
		BITMAPINFO bmi;
		bmi.bmiHeader.biSize=sizeof(BITMAPINFOHEADER);
		bmi.bmiHeader.biWidth=w;
		bmi.bmiHeader.biHeight=h;
		bmi.bmiHeader.biPlanes=1;
		bmi.bmiHeader.biBitCount=24;
		bmi.bmiHeader.biCompression=BI_RGB;
	// DIBSection作成
		hdc=GetDC(hWnd);
		m_hBitmap=CreateDIBSection(hdc,&bmi,DIB_RGB_COLORS,(void**)&m_lpPixel,NULL,0);
		if(m_hBitmap == NULL) {
			m__Code = -2;
		} else {
			GetObject(
			m_hBitmap,				// グラフィックオブジェクトのハンドル
			sizeof(DIBSECTION),		// オブジェクト情報を格納するバッファのサイズ
			&m_Dibsection				// オブジェクト情報を格納するバッファ
			);
			m_hMemDC=CreateCompatibleDC(hdc);
			SelectObject(m_hMemDC,m_hBitmap);
			ReleaseDC(hWnd,hdc);
		}
		m__Code = 0;
	}

	// ファイルを読み込んで初期化
	CDIBSection(HWND hWnd , LPCSTR lpszName)
	{
		HDC hdc;
		if(CreateFromFile(lpszName)==FALSE)exit(0);
		hdc = GetDC(hWnd);
		m_hMemDC=CreateCompatibleDC(hdc);
		SelectObject(m_hMemDC,m_hBitmap);
		ReleaseDC(hWnd,hdc);
		m__Code = 0;
	}

	CDIBSection()
	{
	}


	~CDIBSection()   {
		this->Release();
	}
	VOID Release() {
		if(m_hBitmap) {
			DeleteObject(m_hBitmap);
			m_hBitmap = NULL;
		}
		if(m_hMemDC) {
			DeleteDC(m_hMemDC);
		}
	}

	DIBSECTION GetDIBSection(){return m_Dibsection;}
	BOOL      CreateFromFile(LPCSTR lpszName);
	BOOL      CreateFromFileW(LPCWSTR lpszName);
	int       GetErrorCode() {return m__Code;}
	COLORREF  GetTransparentColor() {return m_transparentcolor;}
	VOID      SetTransparentColor(COLORREF color) {m_transparentcolor = color;}
	LONG &    GetWidth()  {return m_Dibsection.dsBm.bmWidth;}
	LONG &    GetHeight() {return m_Dibsection.dsBm.bmHeight;}
	WORD &    GetBitsPerPixel() {return m_Dibsection.dsBm.bmBitsPixel;}
	BYTE *    GetPixel(int x , int y);
	VOID      Copy(CDIBSection &srcdib , int srcx , int srcy , int cw , int ch , int destx , int desty );
	VOID      Blt(HDC hdcDest) {BitBlt(hdcDest,0,0 ,m_Dibsection.dsBmih.biWidth,m_Dibsection.dsBmih.biHeight,m_hMemDC,0,0,SRCCOPY);}
	int		  ScreenCapture(int width ,int height);
	BOOL	  SaveW(LPCWSTR lpFileName);

	void Write32(HANDLE fh,const DWORD *lpPixel,int w,int h)
	{
		int extra;
		if(w*3%4) extra=4-w*3%4;
		else extra=0;

		DWORD dwWriteSize;
		int zero=0;
		for(int y=0;y<h;y++){
			for(int x=0;x<w;x++)
				WriteFile(fh,lpPixel+x+y*w,3,&dwWriteSize,NULL);
			if(extra) WriteFile(fh,&zero,extra,&dwWriteSize,NULL);
		}	//一行分のバイト数を4の倍数に補正
	}

	void SetFont(LPCTSTR lpszFace , int Height , int Width , COLORREF crColor)
	{

		SetBkMode(
		m_hMemDC,		// デバイスコンテキストのハンドル
		TRANSPARENT		// 背景モード
	);

		SetTextColor(
		m_hMemDC,           // デバイスコンテキストのハンドル
		crColor// テキストの色
	);

		m_hFont = CreateFont(
		Height,							 	// フォントの高さ
		Width,								// 平均文字幅
		0,					 				// 文字送り方向の角度
		0,									// ベースラインの角度
		0,									// フォントの太さ
		FALSE,					 			// 斜体にするかどうか
		FALSE,								// 下線を付けるかどうか
		FALSE,								// 取り消し線を付けるかどうか
		DEFAULT_CHARSET,					// 文字セットの識別子
		OUT_DEFAULT_PRECIS,					// 出力精度
		CLIP_DEFAULT_PRECIS,				// クリッピング精度
		ANTIALIASED_QUALITY,				// 出力品質
		FIXED_PITCH|FF_MODERN,				// ピッチとファミリ
		lpszFace							// フォント名
	);
		SelectObject(m_hMemDC , (HGDIOBJ)m_hFont);
	}

	int DrawText(int x, int y , LPCTSTR lpString , int len){

		RECT rct;
		rct.left   = x;
		rct.top    = y;
		rct.right  = 0;
		rct.bottom = 0;
		
		::DrawText(
		m_hMemDC,          // デバイスコンテキストのハンドル
		lpString, // 描画するテキスト
		len,       // テキストの長さ
		&rct,    // テキストを描画する長方形領域
		DT_NOCLIP      // テキスト描画オプション
		);
		
		return 0;
		
	}
};
.CPP

CODE:

#include "dibsection.h"

BOOL CDIBSection :: CreateFromFile(LPCSTR lpszName) 
{
	this->Release();
	m_hBitmap = (HBITMAP)LoadImageA(
	NULL ,								// インスタンスのハンドル
	lpszName,								// イメージの名前または識別子
	IMAGE_BITMAP,							// イメージのタイプ
	0,     								// 希望する幅
	0,     								// 希望する高さ
	LR_DEFAULTCOLOR | LR_CREATEDIBSECTION		// ロードのオプション
	| LR_DEFAULTSIZE | LR_LOADFROMFILE
	);
	if(m_hBitmap == NULL) {
		m__Code = (INT)APP_ERR_FILE_NOT_FOUND;;					// ファイルが見つからない
		return FALSE;
	}
	if(GetObject(
	m_hBitmap,							// グラフィックオブジェクトのハンドル
	sizeof(DIBSECTION),						// オブジェクト情報を格納するバッファのサイズ
	&m_Dibsection							// オブジェクト情報を格納するバッファ
	) == 0 ) {
		m__Code = (INT)APP_ERR_INITIALIZE_ERR;					// その他のエラー
		return FALSE;
	}
	m_lpPixel = (BYTE *)m_Dibsection.dsBm.bmBits;
	return TRUE;
}

BOOL CDIBSection :: CreateFromFileW(LPCWSTR lpszName) 
{
	this->Release();
	m_hBitmap = (HBITMAP)LoadImageW(
	NULL ,										// インスタンスのハンドル
	lpszName,									// イメージの名前または識別子
	IMAGE_BITMAP,								// イメージのタイプ
	0,     										// 希望する幅
	0,     										// 希望する高さ
	LR_DEFAULTCOLOR | LR_CREATEDIBSECTION		// ロードのオプション
	| LR_DEFAULTSIZE | LR_LOADFROMFILE
	);
	
	if(m_hBitmap == NULL)
	{
		m__Code = (INT)APP_ERR_FILE_NOT_FOUND;					// ファイルが見つからない
		return FALSE;
	}
	
	if(GetObject(
	m_hBitmap,													// グラフィックオブジェクトのハンドル
	sizeof(DIBSECTION),											// オブジェクト情報を格納するバッファのサイズ
	&m_Dibsection												// オブジェクト情報を格納するバッファ
	) == 0 )
	{
		m__Code = (INT)APP_ERR_INITIALIZE_ERR;					// その他のエラー
		return FALSE;
	}
	m_lpPixel = (BYTE *)m_Dibsection.dsBm.bmBits;
	
	HDC hdc  = GetDC(NULL);
	m_hMemDC = CreateCompatibleDC(hdc);
	SelectObject(m_hMemDC,m_hBitmap);
	ReleaseDC(NULL,hdc);
	
	return TRUE;
}

BYTE * CDIBSection ::  GetPixel(int x , int y) 
{
	BYTE * 	 p = m_lpPixel;
	switch(m_Dibsection.dsBm.bmBitsPixel)
	{
		case 24:
			p += (m_Dibsection.dsBm.bmWidth * 3) * ((m_Dibsection.dsBm.bmHeight - 1  - y)) + (x * 3);
		break;
		case 32:
			p += (m_Dibsection.dsBm.bmWidth * 4) * ((m_Dibsection.dsBm.bmHeight - 1  - y)) + (x * 4);
		break;
		default:
		break;
	}
	return p;
}

VOID CDIBSection ::  Copy(CDIBSection &srcdib , int srcx , int srcy , int cw , int ch , int destx , int desty) {
	BYTE * psrc , * pdest;
	int x,y;
	COLORREF transparentcolor = srcdib.GetTransparentColor();
	switch(this->m_Dibsection.dsBm.bmBitsPixel)
	{
	case 24:
		for(y=0;y < ch;++y) {
			if(((desty + y) >= this->GetHeight()) || ((srcy + y) >= srcdib.GetHeight()))break;
			psrc  = srcdib.GetPixel(srcx , srcy + y);
			pdest = this->GetPixel(destx , desty + y);
			for(x=0;x < cw;++x) {
				if(((destx + x) >= this->GetWidth()) || ((srcx + x) >= srcdib.GetWidth()))break;

				if(
				((0xff&transparentcolor   )    == psrc[2]) &&
				((0xff&(transparentcolor>>8))  == psrc[1]) &&
				((0xff&(transparentcolor>>16)) == psrc[0]) 
				) {} else {
					pdest[2] = psrc[2];
					pdest[1] = psrc[1];
					pdest[0] = psrc[0];
				}
				psrc  += 3;
				pdest += 3;
			}
		}
	break;
	case 32:
		for(y=0;y < ch;++y) {
			if(((desty + y) >= this->GetHeight()) || ((srcy + y) >= srcdib.GetHeight()))break;
			psrc  = srcdib.GetPixel(srcx , srcy + y);
			pdest = this->GetPixel(destx , desty + y);
			for(x=0;x < cw;++x) {
				if(((destx + x) >= this->GetWidth()) || ((srcx + x) >= srcdib.GetWidth()))break;

				if(
				((0xff&transparentcolor   )    == psrc[2]) &&
				((0xff&(transparentcolor>>8))  == psrc[1]) &&
				((0xff&(transparentcolor>>16)) == psrc[0]) 
				) {} else {
					pdest[2] = psrc[2];
					pdest[1] = psrc[1];
					pdest[0] = psrc[0];
				}
				psrc  += 4;
				pdest += 4;
			}
		}
	break;
	default:
		this->m__Code = -5;
	break;
	}
}

int	CDIBSection::ScreenCapture(int width ,int height) 
{
						HDC hdc=GetDC(NULL);
						BitBlt(m_hMemDC,0,0,width,height,hdc,0,0,SRCCOPY);
						ReleaseDC(NULL,hdc);
						return 1;
}


BOOL CDIBSection::SaveW(LPCWSTR lpFileName) 
{

	BITMAPINFOHEADER bmpInfoH;
	CopyMemory(&bmpInfoH,&m_Dibsection.dsBmih,sizeof(BITMAPINFOHEADER));

	int bitCount=bmpInfoH.biBitCount;
	if(bitCount!=32 && bitCount!=24 && bitCount!=8)
	{
	    return FALSE;
	}

	int w=bmpInfoH.biWidth , h=bmpInfoH.biHeight;
	DWORD nColorTable=bmpInfoH.biClrUsed;
	if(bitCount==8 && nColorTable==0) nColorTable=256;

	int len;
	if(w*(bitCount/8)%4) len=w*(bitCount/8)+(4-w*(bitCount/8)%4);
	else len=w*(bitCount/8);

	BITMAPFILEHEADER bmpfh;
	bmpfh.bfSize     = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER)+len*h;
	bmpfh.bfType     = ('M'<<8)+'B';
	bmpfh.bfOffBits  = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
	if(bitCount==8)
	{
	    bmpfh.bfSize+=nColorTable*4;
	    bmpfh.bfOffBits+=nColorTable*4;
	}

	HANDLE fh=CreateFileW(lpFileName,GENERIC_WRITE,0,NULL,
	    CREATE_NEW,FILE_ATTRIBUTE_NORMAL,NULL);
	if(fh==INVALID_HANDLE_VALUE)
	{
	    MessageBoxW(NULL,L"同名のファイルが既に存在しています",lpFileName,MB_OK);
	    return FALSE;
	}
	
	DWORD dwWriteSize;
	WriteFile(fh,&bmpfh,sizeof(BITMAPFILEHEADER),&dwWriteSize,NULL);
	WriteFile(fh,&bmpInfoH,sizeof(BITMAPINFOHEADER),&dwWriteSize,NULL);

	if(bitCount == 8)
	WriteFile(fh,m_Bmi.bmiColors,nColorTable*4,&dwWriteSize,NULL);
		
	if(bitCount == 32) Write32(fh,(LPDWORD)m_lpPixel , w , h );
	else WriteFile(fh,m_lpPixel , len * h , &dwWriteSize , NULL);
		
	CloseHandle(fh);
	return TRUE;
}


最後に編集したユーザー T-ARCs on 2018年8月19日(日) 14:12 [ 編集 2 回目 ]

dic
記事: 658
登録日時: 14年前

Re: ビットマップを使ってRPGのマップを読み込む

投稿記事 by dic » 6年前

はじめまして、ですね。
dicといいます。
私は薬で脳の故障をなくそうとしているのです。
なんだが、境遇が似ていててビックリしています。

ビットマップ・・・昔のゲームデータをいじっていたことは
何度お世話になったことか、
プレフィックスもつけていることから、なんだか他人事に思えないです。

今後ともよろしくお願いいたします。

アバター
T-ARCs
記事: 4
登録日時: 7年前

Re: ビットマップを使ってRPGのマップを読み込む

投稿記事 by T-ARCs » 6年前

dicさん、はじめまして。
コメント本当にありがとうございます^_^

ゲームのデータですか?
コーディングスタイルについても、
もっとお話を聞かせていただきたいです。

かなり、前から、プログラミングをやっているのですが、
いろいろなことをやっていて、こちらにも、随分前に登録させて頂いていたのですが
(実は黒かった頃も知っています)

自分、イラスト(ピクシブ)もやっていて、
それ以外に文章も書きたかったり、作曲もしたかったり
いろいろするのですが、どれも進まないことにイライラして、
プログラミングからも今の今まで離れておりました。
今回、何でもできるようになってしまったことが驚愕です。

蛇足

驚愕だと思いますが(見てくれた人の為にも)
ここに書いている事は実話です。

ふと、気づくと、キラリと光るオーブが送られてきたりもするんですよ。
神様=霊界=オブジェクト指向=ウィンドウズプログラムの仕組み(プロセスとスレッド)
それを考察し、観測していると……こういうことが起きるようですね……。
心の向きで言うと、「デバッガだ」「DLLインジェクションだ」とも告げられましたよ。

アバター
T-ARCs
記事: 4
登録日時: 7年前

Re: ビットマップを使ってRPGのマップを読み込む

投稿記事 by T-ARCs » 6年前

"プレフィックス"はハンガリアン記法のことと思いましたが間違っていたらすみません;

今後とも宜しくお願いいたしますm(_ _)m

dic
記事: 658
登録日時: 14年前

Re: ビットマップを使ってRPGのマップを読み込む

投稿記事 by dic » 6年前

ハンガリアン記法でいいと思います。
私もそれほど詳しくないのです。

こちらこそよろしくお願いします。

dic
記事: 658
登録日時: 14年前

Re: ビットマップを使ってRPGのマップを読み込む

投稿記事 by dic » 6年前

昔のゲームデータは、バイナリをちょっといじれば
グラフィックが全部見れたので遊んでいました。
最近は暗号かけてるのでとても無理です。

Direct9を直接いじるより ラップされてる
DxLibを使った方が生産性はあがると思います。
同時入力が出来ないノートパソコンでも
DxLibを使えば同時入力できます。
サーフェイス作成の画像フォーマットを
何パターンも作るより、DxLibで
フォーマットの違いすら吸収してコーディングに
集中できたり便利です。
DirectXは資料が英語で壁があるし、何パターンも
エラーチェックしないといけないし
私はお薦めしません。

以上、私の思ってることでした。

アバター
T-ARCs
記事: 4
登録日時: 7年前

Re: ビットマップを使ってRPGのマップを読み込む

投稿記事 by T-ARCs » 6年前

dicさんありがとうございます。

ただ、もう、DirectXを直接叩く方が慣れてしまった……かも知れません。
一応ですが、HSPから始めて、DirectX9SDKが出回り始めた頃(?)から
やっております。(物凄く時間かけて)

確かにそうなんです。
少し前まで、DXライブラリ使ってました(少しだけですが)
DirectX9を習得し始めた頃には、知らなかったことと
既に、英語のMSDNも読み漁っていたり
DXライブラリのルールを覚える方が億劫に感じたりで
止めてしまったのです。

いまだにデバイスの消失処理も不完全なので、今、完全な
0からのスタートだったらキツイなとは思います。
非効率的だと思いますし
一から作ることにこだわっているわけでも無いのですが
やっていたので後に引けなくなった感じですorz

いろいろなことをやり過ぎな自分が悪いところもあるとは思いますが……。

これから、3Dのモデリングなどにも少しずつ手を出したりしようと思って、
どうにかしないと、まずいなと思っているところです。

アバター
T-ARCs
記事: 4
登録日時: 7年前

Re: ビットマップを使ってRPGのマップを読み込む

投稿記事 by T-ARCs » 6年前

確かに今は無理そうですね。
独自フォーマットだと解析までする気になりませんし、
PNGならDirectXで読み込めますが……。

同時入力って何のことですか?

dic
記事: 658
登録日時: 14年前

Re: ビットマップを使ってRPGのマップを読み込む

投稿記事 by dic » 6年前

同時入力ってのは、一部のノートパソコンにおいて
十字キーが左と上や右と上などができない機種が
あります。わたしのも左と上の同時入力が
できない、対応してないゲームもありますが
DXライブラリだと、その心配がないということです。

アバター
T-ARCs
記事: 4
登録日時: 7年前

Re: ビットマップを使ってRPGのマップを読み込む

投稿記事 by T-ARCs » 6年前

dicさん
お忙しい中、コメントいただきありがとうございます。

現在もゲーム制作はなされているのでしょうか。
DXライブラリだと、ゲーム開発に十分な機能が実装されていますし、
やりたいことが、比較的早く実現できるので良いですね。

確かに、難しいロジックを1から作り上げるより、安心です。