ページ 11

マップエディターのファイルの読み込み方。

Posted: 2017年4月29日(土) 16:52
by keito94
Platinumというマップエディターでマップを読み込んでそれを呼び出すプログラムを書いているのですが、ソースコードのようなエラーが起こってしまいます。
Source.cpp

コード:

#include "DxLib.h"
#include "fmfmap.h"
const int MAP_CELL = 16;
class CGame {
	int BitCount;
	int MapGraph;
	CFmfMap MapObj;
public:

	//コンストラクター。
	CGame() {
		MapLoad("テスト用.fmf", "マップチップ.bmp");
	}

	//マップチップの読み込みライブラリを呼び出す。
	CFmfMap MapData() {
		return MapObj;
	}
	//マップを呼び出す。
	int MapLoad(const char* mapname,const char* mapchip) {
		if (!MapData().Open(mapname)) {
			return -1;
		}
		BYTE* layer = (BYTE*)MapData().GetLayerAddr(0);
		if (layer == NULL) {
			MapData().Close();
			return -1;
		}
		if (MapGraph = LoadGraph(mapchip) == -1) {
			return -1;
		}
		BitCount = MapData().GetLayerBitCount() == 8 ? 16 : 256;
		return 0;
	}

	//ビットカウントを獲得する。
	int GetBitCount() {
		return BitCount;
	}

	//デストラクター。
	~CGame() {

	}

	//マップの描画。
	void Draw() {
		DWORD cwidth = MapData().GetChipWidth();
		DWORD cheight = MapData().GetChipHeight();
		for (int y = 0; y < 15; y++) {
			for (int x = 0; x < 20; y++) {
				int index = MapData().GetValue(0, x, y);
				int src_x = (index % GetBitCount())*cwidth;
				int src_y = (index / GetBitCount())*cheight;
				DrawRectGraph((x)*MAP_CELL, (y)*MAP_CELL, src_x, src_y, MAP_CELL, MAP_CELL, MapGraph, TRUE, FALSE);
			}
		}
	}
};


int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevHinstance, LPSTR lpCmdLine, int nCmdShow) {
	ChangeWindowMode(TRUE), SetGraphMode(320, 240, 32), DxLib_Init();
	CGame* Game;
	Game = new CGame;
	while (ScreenFlip() == 0 && ProcessMessage() == 0 && ClearDrawScreen() == 0) {
		Game->Draw();
	}
	delete Game;
	DxLib_End();
	return 0;
}
fmfmap.cpp

コード:


#include "fmfmap.h"

//-----------------------------------------------------------------------------
//	コンストラクタ
//-----------------------------------------------------------------------------
CFmfMap::CFmfMap(void) : m_pLayerAddr(NULL)
{}
//-----------------------------------------------------------------------------
//	デストラクタ
//-----------------------------------------------------------------------------
CFmfMap::~CFmfMap()
{
	Close();
}	
//-----------------------------------------------------------------------------
//	マップを開いてデータを読み込む
// 引数:	szFilePath	= マップファイルのパス
// 戻り値:	正常終了	= TRUE
//			エラー		= FALSE
//-----------------------------------------------------------------------------
BOOL CFmfMap::Open(const char *szFilePath)
{
	Close();

	// ファイルを開く
	HANDLE hFile = CreateFile(	szFilePath, GENERIC_READ, FILE_SHARE_READ, NULL,
								OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
	if (hFile == INVALID_HANDLE_VALUE)
		return FALSE;

	// ヘッダ情報を読む
	DWORD dwReadBytes;
	if (!ReadFile(hFile, &m_fmfHeader, sizeof(FMFHEADER), &dwReadBytes, NULL) ||
		dwReadBytes != sizeof(FMFHEADER))
		goto error_return;

	// 識別子のチェック
	if (memcmp(&m_fmfHeader.dwIdentifier, "FMF_", 4) != 0)
		goto error_return;

	// メモリ確保
	m_pLayerAddr = new BYTE[m_fmfHeader.dwSize];
	if (m_pLayerAddr == NULL)
		goto error_return;

	// レイヤーデータを読む
	if (!ReadFile(hFile, m_pLayerAddr, m_fmfHeader.dwSize, &dwReadBytes, NULL) ||
		dwReadBytes != m_fmfHeader.dwSize)
		goto error_return;
	
	// 正常終了
	CloseHandle(hFile);
	return TRUE;

error_return:
	// エラー終了
	CloseHandle(hFile);
	Close();
	return FALSE;
}
//-----------------------------------------------------------------------------
// マップが開かれているか
//-----------------------------------------------------------------------------
BOOL CFmfMap::IsOpen() const
{
	return m_pLayerAddr != NULL;
}
//-----------------------------------------------------------------------------
//	マップメモリを開放
//-----------------------------------------------------------------------------
void CFmfMap::Close(void)
{
	if (m_pLayerAddr != NULL)
	{
		delete [] m_pLayerAddr;
		m_pLayerAddr = NULL;
	}
}
//-----------------------------------------------------------------------------
//	指定レイヤの先頭アドレスを得る
//	引数:	レイヤ番号
//	戻り値:	正常終了	= レイヤデータのアドレス
//			エラー		= NULL
//	各レイヤデータは連続したメモリ領域に配置されてるので
//	指定レイヤデータのアドレスを計算で求める。
//-----------------------------------------------------------------------------
void* CFmfMap::GetLayerAddr(BYTE byLayerIndex) const
{
	// メモリチェック、範囲チェック
	if ((m_pLayerAddr == NULL) || (byLayerIndex >= m_fmfHeader.byLayerCount))
		return NULL;

	BYTE bySize = m_fmfHeader.byBitCount / 8;
	return m_pLayerAddr + m_fmfHeader.dwWidth * m_fmfHeader.dwHeight * bySize * byLayerIndex;
}
//-----------------------------------------------------------------------------
// レイヤ番号と座標を指定して直接データを貰う
// 引数:
// 	byLayerIndex	= レイヤ番号
// 	dwX				= X座標(0~m_fmfHeader.dwWidth - 1)
// 	dwY				= Y座標(0~m_fmfHeader.dwHeight - 1)
// 戻り値:
// 	正常終了	= 座標の値
//	エラー		= -1
//-----------------------------------------------------------------------------
int CFmfMap::GetValue(BYTE byLayerIndex, DWORD dwX, DWORD dwY) const
{
	int nIndex = -1;

	// 範囲チェック
	if (byLayerIndex >= m_fmfHeader.byLayerCount ||
		dwX >= m_fmfHeader.dwWidth ||
		dwY >= m_fmfHeader.dwHeight)
		return nIndex;

	if (m_fmfHeader.byBitCount == 8)
	{
		// 8bit layer
		BYTE* pLayer = (BYTE*)GetLayerAddr(byLayerIndex);
		nIndex = *(pLayer + dwY * m_fmfHeader.dwWidth + dwX);
	}
	else
	{
		// 16bit layer	
		WORD* pLayer = (WORD*)GetLayerAddr(byLayerIndex);
		nIndex = *(pLayer + dwY * m_fmfHeader.dwWidth + dwX);	 //ここでpLayerが0x1110112でしたという例外がスローされる。
	}

	return nIndex;
}

//-----------------------------------------------------------------------------
// レイヤ番号と座標を指定してデータをセット
//-----------------------------------------------------------------------------
void CFmfMap::SetValue(BYTE byLayerIndex, DWORD dwX, DWORD dwY, int nValue)
{
	// 範囲チェック
	if (byLayerIndex >= m_fmfHeader.byLayerCount ||
		dwX >= m_fmfHeader.dwWidth ||
		dwY >= m_fmfHeader.dwHeight)
		return;

	if (m_fmfHeader.byBitCount == 8)
	{
		// 8bit layer
		BYTE* pLayer = (BYTE*)GetLayerAddr(byLayerIndex);
		*(pLayer + dwY * m_fmfHeader.dwWidth + dwX) = (BYTE)nValue;
	}
	else
	{
		// 16bit layer	
		WORD* pLayer = (WORD*)GetLayerAddr(byLayerIndex);
		*(pLayer + dwY * m_fmfHeader.dwWidth + dwX) = (WORD)nValue;
	}
}

//-----------------------------------------------------------------------------
// マップの横幅を得る
//-----------------------------------------------------------------------------
DWORD CFmfMap::GetMapWidth(void) const
{
	return m_fmfHeader.dwWidth;
}
//-----------------------------------------------------------------------------
// マップの高さを得る
//-----------------------------------------------------------------------------
DWORD CFmfMap::GetMapHeight(void) const
{
	return m_fmfHeader.dwHeight;
}
//-----------------------------------------------------------------------------
// チップの横幅を得る
//-----------------------------------------------------------------------------
BYTE CFmfMap::GetChipWidth(void) const
{
	return m_fmfHeader.byChipWidth;
}
//-----------------------------------------------------------------------------
// チップの高さを得る
//-----------------------------------------------------------------------------
BYTE CFmfMap::GetChipHeight(void) const
{
	return m_fmfHeader.byChipHeight;
}
//-----------------------------------------------------------------------------
// レイヤー数を得る
//-----------------------------------------------------------------------------
BYTE CFmfMap::GetLayerCount(void) const
{
	return m_fmfHeader.byLayerCount;
}
//-----------------------------------------------------------------------------
// レイヤーデータのビットカウントを得る
//-----------------------------------------------------------------------------
BYTE CFmfMap::GetLayerBitCount(void) const
{
	return m_fmfHeader.byBitCount;
}
fmfmap.h

コード:

#ifndef __CLASS_FMFMAP_H__
#define __CLASS_FMFMAP_H__
#pragma once

#include <windows.h>

// FMFファイルヘッダ (20 bytes)
typedef struct tag_FMFHeader
{
	DWORD	dwIdentifier;	// ファイル識別子 'FMF_'
	DWORD	dwSize;			// ヘッダを除いたデータサイズ
	DWORD	dwWidth;		// マップの横幅
	DWORD	dwHeight;		// マップの高さ
	BYTE	byChipWidth;	// マップチップ1つの幅(pixel)
	BYTE	byChipHeight;	// マップチップ1つの高さ(pixel)
	BYTE	byLayerCount;	// レイヤーの数
	BYTE	byBitCount;		// レイヤデータのビットカウント
}FMFHEADER;

class CFmfMap
{
public:
	// 構築/消滅
	CFmfMap(void);
	~CFmfMap();

	// マップを開いてデータを読み込む
	BOOL Open(const char *szFilePath);

	// マップが開かれているか
	BOOL IsOpen() const;

	// マップメモリを開放
	void Close(void);
		
	// 指定レイヤーの先頭アドレスを得る
	void* GetLayerAddr(BYTE byLayerIndex) const;
	
	// レイヤ番号と座標を指定して直接データを貰う
	int GetValue(BYTE byLayerIndex, DWORD dwX, DWORD dwY) const;

	// レイヤ番号と座標を指定してデータをセット
	void SetValue(BYTE byLayerIndex, DWORD dwX, DWORD dwY, int nValue);

	// ヘッダの情報を得る
	DWORD GetMapWidth(void) const;
	DWORD GetMapHeight(void) const;
	BYTE GetChipWidth(void) const;
	BYTE GetChipHeight(void) const;
	BYTE GetLayerCount(void) const;
	BYTE GetLayerBitCount(void) const;
protected:
	// FMFファイルヘッダ構造体
	FMFHEADER	m_fmfHeader;
	// レイヤーデータへのポインタ
	BYTE* 		m_pLayerAddr;
};
エラーログを取っておいたのでそれも参考にしてください。

コード:

例外がスローされました:読み取りアクセス違反。
**pLayer** が 0x1110112 でした。 が発生しました

Re: マップエディターのファイルの読み込み方。

Posted: 2017年4月29日(土) 17:22
by みけCAT
keito94 さんが書きました:ソースコードのようなエラーが起こってしまいます。
わざわざご報告ありがとうございます。
デバッグトレーニング中なんですよね。
何か質問はありますか?

Re: マップエディターのファイルの読み込み方。

Posted: 2017年4月29日(土) 17:36
by keito94
みけCAT さんが書きました: 何か質問はありますか?
すいません、質問を忘れていました。
どうすれば、例外をスローせずにマップを表示させることができますか?

Re: マップエディターのファイルの読み込み方。

Posted: 2017年4月29日(土) 18:00
by みけCAT
オフトピック
このエラーの原因ではないと思いますが、
keito94 さんが書きました:Source.cpp

コード:

		if (MapGraph = LoadGraph(mapchip) == -1) {
という行では読み込んだ結果のハンドルではなく読み込めなかったかどうかがMapGraphに代入され、不自然だと思います。

Re: マップエディターのファイルの読み込み方。

Posted: 2017年4月29日(土) 18:33
by 梅衣堂ひよ
GetLayerAddrの返り値、

コード:

    return m_pLayerAddr + m_fmfHeader.dwWidth * m_fmfHeader.dwHeight * bySize * byLayerIndex;
となっていますが、レイヤー全体の先頭アドレスに対してマップのサイズを足しちゃダメじゃないですかね?

Re: マップエディターのファイルの読み込み方。

Posted: 2017年4月29日(土) 18:39
by keito94
>>梅衣堂ひよ さん
マップエディターについているマップ読み込みのためのコードで、可読性を重視しているから仕方ないのです。

Re: マップエディターのファイルの読み込み方。

Posted: 2017年4月29日(土) 18:55
by 梅衣堂ひよ
あ、すいません。マップの座標と勘違いしてましたw
レイヤーだから3次元目の移動だから2次元分である座標は無視しないとですよね

Re: マップエディターのファイルの読み込み方。

Posted: 2017年4月29日(土) 18:56
by みけCAT
CGame::MapData()関数が毎回MapObjのコピーを返しているので、せっかく読み込んだマップを投げ捨てて以降の処理で利用できなくなっています。
コピーではなく参照を返すようにするか、この関数を消して直接MapObjを使うようにするといいでしょう。
ただし、Source.cppの51行目でxのかわりにyをインクリメントしているので、落ちなくなってもフリーズするでしょう。

Re: マップエディターのファイルの読み込み方。

Posted: 2017年4月29日(土) 19:32
by keito94
データ関係は、コピーではなく参照を渡せばいいわけなんですね!!
一つC++の知識が増えました!!
ありがとうございます!!
オフトピック
僕がthisポインタを学んだことによって、炎上の原因にもなった問題が簡単に解決しました!!(あの時はC++の知識がほんとになかったもので…。)
あと、51行目のxの代わりにyをインクリメントしていた件についてはご指摘ありがとうございました!!

Re: マップエディターのファイルの読み込み方。

Posted: 2017年4月29日(土) 19:42
by 梅衣堂ひよ
オフトピック
テストしていて気付いたことですが、描画の際に気になった点が2つあります。
まず、チップサイズはヘッダーからマップ情報として読み込んでいるのに定数で用意していますね。
定数の方は消してマップ情報からチップのサイズを引っ張ったやつを使ってあげましょう。
それとfor文の条件の最大値ですね。
マップのサイズを与えればいいと思います。
画面外を描画しないようにするのはスクロールと合わせてちょっと工夫は必要ですが…

Re: マップエディターのファイルの読み込み方。

Posted: 2017年4月30日(日) 14:18
by keito94
あっ、解決押すの忘れてました!