ページ 11

RPGのアイテム処理を実装してみたのですが…

Posted: 2011年6月22日(水) 15:38
by ゆきなす
↓前回の記事です
http://dixq.net/forum/viewtopic.php?f=3&t=8715

前回改善してうまく動くようになったプログラムを実装し、それにセーブ・ロード機能を付けたのですが以下のエラー

" ItemProcessingCustom.exe の 0x0078bc4f でハンドルされていない例外が発生しました: 0xC0000005: 場所 0x016c5e08 を読み込み中にアクセス違反が発生しました。 "

が、出て動作を停止してしまいます。(ごく稀に通ることがあります)
また、動作停止後デバッグすると " output.c " が開きますが見てもよくわかりませんでした。

以下プログラムです。(必要なものだけ残していますが、エラー内容は変わりませんでした。また、DxLibを使っています)

コード:

//WinMain.cpp
#include "WinMain.h"
#include "GameApplication.h"

static CGameAppli *Game;
int ProcessLoop();

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
	ChangeWindowMode(TRUE);//ウィンドウモード変更
	
	if(DxLib_Init() == -1 || SetDrawScreen(DX_SCREEN_BACK) != 0) //初期化と裏画面設定
	{
		return -1;
	}
	
	Game = new CGameAppli();
	
	int GraphicStage = 0;

	// while(裏画面を表画面に反映, メッセージ処理, 画面クリア)
	while(ProcessLoop() ==0)
	{
		switch(GraphicStage)
		{
		case 0:
			Game->GameInitialize();
			Game->GameLoad();
			GraphicStage = 10;
			break;

		case 10:
			Game->GameEquipment();
			break;
		}

		ScreenFlip();
		
		//エスケープが入力 or GraphicStage 999 されたらブレイク
		if(CheckHitKey(KEY_INPUT_ESCAPE) == 1 || GraphicStage == 999)
			break;
	}

	Game->GameSave();//セーブ関数

	SAFE_DELETE(Game);
	
	DxLib_End(); // DXライブラリ終了処理
	return 0;
}

int ProcessLoop()
{
	//プロセス処理がエラーなら-1を返す
    if(ProcessMessage() != 0)
		return -1;
	
	//画面クリア処理がエラーなら-1を返す
    if(ClearDrawScreen() != 0)
		return -1;

	return 0;
}

コード:

//WinMain.h
#ifndef WINMAIN_H_
#define WINMAIN_H_

#include "DxLib.h"

#include <windows.h>
#include <winbase.h>
#include <windowsx.h>

#include <math.h>
#include <time.h>
#include <tchar.h>
#include <uuids.h>
#include <stdio.h>
#include <wchar.h>
#include <crtdbg.h>
#include <stdlib.h>
#include <malloc.h>
#include <memory.h>
#include <strmif.h>
#include <control.h>
#include <process.h>

#include "StructBox.h"

#define SAFE_DELETE(x) {if(x){delete (x); (x) = NULL;}}
#define SAFE_DELETE_ARRAY(x) {if(x){delete[] (x); (x) = NULL;}}
#define SAFE_RELEASE(x) {if(x){(x)->Release(); (x) = NULL;}}

#endif

コード:

//GameAppli.cpp
#include "GameApplication.h"

//------CGameAppli初期化関数------
CGameAppli::CGameAppli()
{
	PlayerObj = new CPlayerObj();
	ItemObj = new CItemObj();
}

//------CGameAppliデストラクタ関数------
CGameAppli::~CGameAppli()
{
	SAFE_DELETE(PlayerObj);
	SAFE_DELETE(ItemObj);
}

//------CGameAppliゲーム関係初期化関数------
void CGameAppli::GameInitialize()
{
	ItemObj->ItemGetInitialize(PlayerObj);
}

//------CGameAppli装備関数------
void CGameAppli::GameEquipment()
{
	PlayerObj->PlayerMainAction();
	ItemObj->ItemMainAction(PlayerObj);
}

//------CGameSave関数------
void CGameAppli::GameSave()
{
	ItemObj->DataSave(PlayerObj);
}

//------CGameLoad関数------
void CGameAppli::GameLoad()
{
	ItemObj->DataLoad(PlayerObj);
}
下に続きます。

Re: RPGのアイテム処理を実装してみたのですが…

Posted: 2011年6月22日(水) 15:44
by ゆきなす
続きになります。

コード:

//PlayerObj.cpp
#include "PlayerObject.h"

CPlayerObj::CPlayerObj()
{
	//PLAYER_HAVE_ITEM(P_Item) を NULL初期化
	memset(&P_Item, NULL, sizeof(PLAYER_HAVE_ITEM));

	LoadDivGraph("Player.png", 12, 4, 3, 73, 73, Playerimge[0]);
}

CPlayerObj::~CPlayerObj()
{
}

void CPlayerObj::PlayerMainAction()
{
	//表示
	DrawRotaGraphF(320, 240, 1.0f, 0.0f, Playerimge[0][0], TRUE);
}

コード:

//ItemObj.cpp
#include "ItemObject.h"

//コンスト
CItemObj::CItemObj()
{
	LoadDivGraph("Player.png", 12, 4, 3, 73, 73, Itemimge[0]);
}

//デストラ
CItemObj::~CItemObj(){}

void CItemObj::ItemGetInitialize(CPlayerObj *PlayerObj)
{
	//----------アイテム情報初期化----------
	for(int i = 0 ; i < HAVE_MAX ; i++)
	{
		ItemDataInitialize(PlayerObj, i);
	}

	//----------アイテム取得----------
	for(int i = 0 ; i < 12 ; i++)
	{
		AddItem(PlayerObj, i);
	}

	//----------装備アイテム取得----------
	ItemEquipment(PlayerObj, 0, 0);		//装備欄[0] に 所持アイテム[0]
	ItemEquipment(PlayerObj, 1, 1);		//装備欄[1] に 所持アイテム[1]
	ItemEquipment(PlayerObj, 2, 2);		//装備欄[2] に 所持アイテム[2]
	ItemEquipment(PlayerObj, 3, 3);		//装備欄[3] に 所持アイテム[3]

	//所持アイテムの表示位置設定
	for(int i = 0 ; i < HAVE_MAX ; i++)
	{
		ItemImgeRight[i] = 500;
		ItemFontRight[i] = 550;
	}

	//装備アイテムの表示位置設定
	for(int i = 0 ; i < Equipment_MAX ; i++)
	{
		ItemImgeLeft[i] = 50;
		ItemFontLeft[i] = 100;
	}
}

//----------アイテム装備関数----------
int CItemObj::ItemEquipment(CPlayerObj *PlayerObj, int PlayerEquipBox, int PlayerItemBox)
{
	//P_Equip[i].ItemNo が "65535" なら 所持アイテム[i]からデータをコピーして、所持アイテム[i].Have--;
	if(PlayerObj->P_Item.P_Equip[PlayerEquipBox].ItemNo == 65535)
	{
		PlayerObj->P_Item.P_Equip[PlayerEquipBox].ItemNo = PlayerObj->P_Item.P_Data[PlayerItemBox].ItemNo;
		PlayerObj->P_Item.P_Equip[PlayerEquipBox].ItemImage = PlayerObj->P_Item.P_Data[PlayerItemBox].ItemImage;
		PlayerObj->P_Item.P_Equip[PlayerEquipBox].ItemName = PlayerObj->P_Item.P_Data[PlayerItemBox].ItemName;
		PlayerObj->P_Item.P_Equip[PlayerEquipBox].ItemType = PlayerObj->P_Item.P_Data[PlayerItemBox].ItemType;
		PlayerObj->P_Item.P_Data[PlayerItemBox].Have--;
	}

	//P_Equip[i].ItemNo が "65535" 以外なら P_Equip[i]の装備アイテムにアイテム入手処理を行い ( 所持アイテムに戻し )
	//所持アイテム[i]からデータをコピーして、所持アイテム[i].Have--;
	else if(PlayerObj->P_Item.P_Equip[PlayerEquipBox].ItemNo != 65535)
	{
		AddItem(PlayerObj, PlayerObj->P_Item.P_Equip[PlayerEquipBox].ItemNo);

		PlayerObj->P_Item.P_Equip[PlayerEquipBox].ItemNo = PlayerObj->P_Item.P_Data[PlayerItemBox].ItemNo;
		PlayerObj->P_Item.P_Equip[PlayerEquipBox].ItemImage = PlayerObj->P_Item.P_Data[PlayerItemBox].ItemImage;
		PlayerObj->P_Item.P_Equip[PlayerEquipBox].ItemName = PlayerObj->P_Item.P_Data[PlayerItemBox].ItemName;
		PlayerObj->P_Item.P_Equip[PlayerEquipBox].ItemType = PlayerObj->P_Item.P_Data[PlayerItemBox].ItemType;
		PlayerObj->P_Item.P_Data[PlayerItemBox].Have--;
	}

	return true;
}
//----------アイテム情報コピー関数----------
void CItemObj::ItemDataGet(CPlayerObj *PlayerObj, int PlayerItemBox, int GetItem)
{
	//ItemData.h の RPG_ITEM_DATA DataItem[] から P_Data[i] へ データをコピー
	PlayerObj->P_Item.P_Data[PlayerItemBox].ItemNo = DataItem[GetItem].ItemNo;
	PlayerObj->P_Item.P_Data[PlayerItemBox].ItemImage = DataItem[GetItem].ItemImage;
	PlayerObj->P_Item.P_Data[PlayerItemBox].ItemName = DataItem[GetItem].ItemName;
	PlayerObj->P_Item.P_Data[PlayerItemBox].ItemType = DataItem[GetItem].ItemType;
	PlayerObj->P_Item.P_Data[PlayerItemBox].Have += DataItem[GetItem].Have;
}
//----------アイテム情報初期化関数----------
void CItemObj::ItemDataInitialize(CPlayerObj *PlayerObj, int PlayerItemBox)
{
	//P_Data[i] の データを指定数に設定 ( 初期化として考えています )
	PlayerObj->P_Item.P_Data[PlayerItemBox].ItemNo = 65535;
	PlayerObj->P_Item.P_Data[PlayerItemBox].ItemImage = -1;
	PlayerObj->P_Item.P_Data[PlayerItemBox].ItemName = NULL;
	PlayerObj->P_Item.P_Data[PlayerItemBox].ItemType = -1;
	PlayerObj->P_Item.P_Data[PlayerItemBox].Have = 0;
}
//----------アイテム取得/複数所持関数----------
int CItemObj::AddItem(CPlayerObj *PlayerObj, int GetItem)
{
	int CopyGetItem = GetItem;

	//現在の ( PlayerObj->P_Item.HaveItem + 1 ) までループ
	//( 現在の所持アイテム数 と 一つ下 を調べる)
	for(int i = 0 ; i < (PlayerObj->P_Item.HaveItem + 1) ; i++)
	{
		//PlayerObj->P_Item.P_Data[i].ItemNo が CopyGetItem と同じのを発見なら
		if(PlayerObj->P_Item.P_Data[i].ItemNo == CopyGetItem)
		{
			//PlayerObj->P_Item.P_Data[i].Have に ++
			PlayerObj->P_Item.P_Data[i].Have++;
			return true;
		}

		//そうでないなら新規追加
		else if(PlayerObj->P_Item.P_Data[i].ItemNo == 65535)
		{
			ItemDataGet(PlayerObj, i, GetItem);

			//P_Item.HaveItem++ 後、その場所を初期化
			PlayerObj->P_Item.HaveItem++;
			ItemDataInitialize(PlayerObj, PlayerObj->P_Item.HaveItem);
			return true;
		}
	}

	return false;
}
//----------アイテムメイン処理----------
void CItemObj::ItemMainAction(CPlayerObj *PlayerObj)
{
	//( PlayerObj->P_Item.HaveItem + 1 ) までループ
	for(int i = 0 ; i < (PlayerObj->P_Item.HaveItem + 1) ; i++)
	{
		//P_Data[i].ItemNo が "65535" なら P_Data[i].ItemName を "なし"
		if(PlayerObj->P_Item.P_Data[i].ItemNo == 65535)
		{
			PlayerObj->P_Item.P_Data[i].ItemName = "なし";
			//break;
		}

		//P_Data[i].ItemImage の 数値でアイテムイメージの変更
		DrawRotaGraphF(ItemImgeRight[i], 50 + (i * 32), 1.0f, 0.0f, Itemimge[0][PlayerObj->P_Item.P_Data[i].ItemImage], TRUE);

		//P_Data[i].ItemImage の 数値でアイテムネームの変更
		DrawFormatString(ItemFontRight[i], 50 + (i * 32), GetColor(255, 255, 255), "%s * %d", PlayerObj->P_Item.P_Data[i].ItemName, PlayerObj->P_Item.P_Data[i].Have);
	}

	for(int i = 0 ; i < Equipment_MAX ; i++)
	{
		//P_Equip[i].ItemNo が "65535" なら P_Equip[i].ItemName を "なし"
		if(PlayerObj->P_Item.P_Equip[i].ItemNo == 65535)
		{
			PlayerObj->P_Item.P_Equip[i].ItemName = "なし";
		}

		//P_Equip[i].ItemImage の 数値でアイテムイメージの変更
		DrawRotaGraphF(ItemImgeLeft[i], 50 + (i * 32), 1.0f, 0.0f, Itemimge[0][PlayerObj->P_Item.P_Equip[i].ItemImage], TRUE);

		//P_Equip[i].ItemImage の 数値でアイテムネームの変更
		DrawFormatString(ItemFontLeft[i], 50 + (i * 32), GetColor(255, 255, 255), "%s", PlayerObj->P_Item.P_Equip[i].ItemName);
	}

	//P_Item.HaveItem数の表示
	DrawFormatString(265, 0, GetColor(255, 0, 255), "HaveItem %d", PlayerObj->P_Item.HaveItem);
}

//セーブ関数
void CItemObj::DataSave(CPlayerObj *PlayerObj)
{
	FILE *fp;

	fopen_s(&fp, "Data.txt", "wb");

	if(fp == NULL)
		return;

	fwrite(&PlayerObj->P_Item, sizeof(PLAYER_HAVE_ITEM), 1, fp);

	fclose(fp);
}

//ロード関数
void CItemObj::DataLoad(CPlayerObj *PlayerObj)
{
	FILE *fp;

	fopen_s(&fp, "Data.txt", "rb");
	
	fread(&PlayerObj->P_Item, sizeof(PLAYER_HAVE_ITEM), 1, fp);
	
	fclose(fp);
}

コード:

//ItemObj.h
#ifndef ITEMOBJECT_H_
#define ITEMOBJECT_H_

#include "WinMain.h"
#include "ItemData.h"
#include "PlayerObject.h"

class CItemObj
{
private:
	int Itemimge[1][12];
	int ItemFontRight[HAVE_MAX], ItemImgeRight[HAVE_MAX], ItemFontLeft[Equipment_MAX], ItemImgeLeft[Equipment_MAX];

public:
	CItemObj();
	~CItemObj();

	void ItemGetInitialize(CPlayerObj *PlayerObj);
	int ItemEquipment(CPlayerObj *PlayerObj, int PlayerEquipBox, int PlayerItemBox);
	void ItemDataGet(CPlayerObj *PlayerObj, int PlayerItemBox, int GetItem);
	void ItemDataInitialize(CPlayerObj *PlayerObj, int PlayerItemBox);
	int AddItem(CPlayerObj *PlayerObj, int GetItem);

	void ItemMainAction(CPlayerObj *PlayerObj);
	void GetItemName(CPlayerObj *PlayerObj, int ItemNo);

	void DataSave(CPlayerObj *PlayerObj);
	void DataLoad(CPlayerObj *PlayerObj);
};

#endif

コード:

//ItemData.h
#ifndef ITEMDATA_H_
#define ITEMDATA_H_

#include "StructBox.h"

#define NOT_ITEM 65535
#define ITEM_NORMAL 1
#define ITEM_EQUIP 2
#define ITEM_EVENT 3

static const RPG_ITEM_DATA DataItem[] =
{
	//ItemNo, ItemImg, ItemName, ItemType, GetHave
	{0, 6, "草", NOT_ITEM, 5,},
	{1, 0, "薬草", ITEM_NORMAL, 5,},
	{2, 3, "回復薬", ITEM_NORMAL, 5,},
	{3, 4, "剣", ITEM_EQUIP, 5,},
	{4, 7, "盾", ITEM_EQUIP, 5,},
	{5, 8, "宝石", ITEM_EQUIP, 5,},
	{6, 6, "海苔", ITEM_EQUIP, 5,},
	{7, 0, "くるみ", ITEM_EQUIP, 5,},
	{8, 3, "みかん", ITEM_NORMAL, 5,},
	{9, 4, "箱", ITEM_NORMAL, 5,},
	{10, 7, "敷物", ITEM_NORMAL, 5,},
	{11, 8, "玉石", ITEM_NORMAL, 5,}
};

#endif

コード:

//struct.h
#ifndef STRUCTBOX_H_
#define STRUCTBOX_H_

#define Equipment_MAX 4
#define HAVE_MAX 15

struct RPG_ITEM_DATA
{
	int ItemNo;
	int ItemImage;
	char *ItemName;
	int ItemType;
	int Have;
};


struct PLAYER_HAVE_ITEM
{
	int HaveItem;

	RPG_ITEM_DATA P_Data[HAVE_MAX];
	RPG_ITEM_DATA P_Equip[Equipment_MAX];
};

#endif
以上です。
どこがおかしいのか御指導お願いします。

Re: RPGのアイテム処理を実装してみたのですが…

Posted: 2011年6月22日(水) 15:50
by ゆきなす
連続で申し訳ありません。

ItemObject.h の void GetItemName(CPlayerObj *PlayerObj, int ItemNo) は 消し忘れです。
そして、WinMain.cpp の Game->GameLoad() を コメントアウトするとエラーが出なくなります。

Re: RPGのアイテム処理を実装してみたのですが…

Posted: 2011年6月22日(水) 19:03
by h2so5
気になったことを書きます。
RPG_ITEM_DATA構造体にItemNameというポインタがありますね。

ポインタということはつまり、これは文字データの実体ではなくアドレスを保持しているだけです。
データをセーブしたときと、ロードした時で文字データのアドレスが等しい保証はあるでしょうか?

Re: RPGのアイテム処理を実装してみたのですが…

Posted: 2011年6月22日(水) 20:02
by dic
私も前回の質問のときに char * というのが気になっていましたが

下のソースで、違いがわかっていればいいですが

コード:

#include <stdio.h>
#include <string.h>

char	*str1 = "フラワー";
char	*str2 = "お酒";

char	str3[80] = "フラワー";
char	str4[80] = "お酒";

int main()
{
	printf( "%s\n", str1 );
	printf( "%s\n", str2 );

//	strcpy( str2, str1 );	//	実行エラー

	printf( "%s\n", str3 );
	printf( "%s\n", str4 );

	strcpy( str4, str3 );

	return 0;
}

Re: RPGのアイテム処理を実装してみたのですが…

Posted: 2011年6月23日(木) 14:11
by ゆきなす
調べてみました。
char* は箱やら実態やらを用意するわけではないのですね。
今回のエラーは確保していない物にアクセスしようとしたことが原因でいいのでしょうか?

また、エラーは char *ItemName を char ItemName に、文字列のコピーに strcpy() を使用することで解決しました。

御相談に乗って頂きどうもありがとうございました。

Re: RPGのアイテム処理を実装してみたのですが…

Posted: 2011年6月23日(木) 15:09
by h2so5
ゆきなす さんが書きました: また、エラーは char *ItemName を char ItemName に、文字列のコピーに strcpy() を使用することで解決しました。
解決になっているのでしょうか?

私なら、ロード時にアイテム番号からアイテム名を復元するようにします。
アイテム名までセーブする必要はありません。