コンストラクタでvectorを用いてのLoadGraphについて

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
ねの

コンストラクタでvectorを用いてのLoadGraphについて

#1

投稿記事 by ねの » 10年前

久しぶりに質問させていただきます。

前提として、今回お尋ねしたいのは、LoadGraph(LoadDivGraph)を用いて読み込んだ画像はDxライブラリ終了時までアドレスに記憶されるのか、ということです。

現在簡単なシューティングゲームを作っているのですが、画像をできるだけわかりやすく管理したいと思い、vectorを用いて画像のハンドルを保存することにしました。
基本的な物体をChara_Objとして、

コード:

class Chara_Obj{
pubic:
/*中略*/
        vector<int> graph;
}
として、画像データを持つvectorを持たせ、また別のヘッダに、
画像情報を格納するオブジェクトとして:Graphic_Data、
実際に読みこんだ画像を保存しておくオブジェクトとして GraphData_Objをつくりました。

コード:


class Graphic_data
{
public:
	vector<int> graph;
	int width, height;

	Graphic_data(char* name){
		// 読み込みに使う変数
		int X_num, Y_num = 1, Allnum;
		int handle;
		
		//一枚画像読み込み
		handle = LoadGraph(name, 0);

		//大きさから枚数を調べる 枚数分int領域確保
		GetGraphSize(handle, &width, &height);
		if (width % height == 0){
			X_num = Allnum = width / height;
			int num[20] = { 0 };

			// LoadDivGraphで枚数を分けて確保したint領域に読み込み その後vectorに入れていく
			LoadDivGraph(name, Allnum, X_num, Y_num, width, height, num, 0);
			for (int i = 0; i < Allnum; i++){
				graph.push_back(num[i]);
			}

			width = height;
		}
		else{
			graph.push_back(handle);
		}
	}
};

class GraphData_Obj{
public:
	/*ほしい画像データ列挙*/

	map<string, Graphic_data*> Menu;
	vector<Graphic_data*> Player;
	vector<Graphic_data*> Enemy;
	vector<Graphic_data*> Effect;


	GraphData_Obj(){
		///////////////////////////////////////////
		/*--------- 画像データ読み込み -----------*/
		///////////////////////////////////////////

		// 読み込みに使うファイル名変数
		char adress[256];

		/* 自機の画像読み込み */
		for (int i = 0;; i++){
			sprintf_s(adress, "Image/Player/%d.png", i);
			if ((LoadGraph(adress)) == -1)break;
			Graphic_data* tmp = new Graphic_data(adress);
			Player.push_back(tmp);
		}

		/* 敵の画像読み込み */
		for (int i = 0;;i++){
			sprintf_s(adress, "Image/Enemy/%d.png", i);
			if (( LoadGraph(adress)) == -1)break;
			Graphic_data* tmp = new Graphic_data(adress);
			Enemy.push_back(tmp);
		}

		/* 敵の弾画像読み込み */
		for (int i = 0;; i++){

			sprintf_s(adress, "Image/Enemy/Bullet/%d", i);
			if (( LoadGraph(adress)) == -1)break;
			Graphic_data* tmp = new Graphic_data(adress);
			Enemy.push_back(tmp);
		}

		/* メニューの画像読み込み */
		Menu["BackGround1"] = GetMapAdress("Image/Menu/BackGround.png");
		Menu["BackGround2"] = GetMapAdress("Image/Menu/BackGround1.png");

		char name[256];

		for (int i = 0;; i++){
			sprintf_s(adress, "Image/Menu/StageGraph/%d.png", i);
			if (LoadGraph(adress) == -1)break;
			sprintf_s(name, "StageGraph%d", i);
			Menu[name] = GetMapAdress(adress);
		}
	}
	// Mapを用いたコンテナに画像を登録するための関数(アドレスをすぐに返すため一行で登録が可能)
	Graphic_data* GetMapAdress(string data_address){
		char name[256];
		sprintf_s(name, data_address.c_str());
		Graphic_data* tmp = new Graphic_data(name);
		return tmp;
	}
};

class String_Obj{
public:
	string str;
	int x, y,width,height, size;
	int num;
	int color;

	String_Obj(int point_x,int point_y,string data, int DrawSize, int DrawColor){
		x = point_x;
		y = point_y;
		str = data;
		size = DrawSize;
		num = str.size();
		width = size * num;
		height = size;
		color = DrawColor;
	}
	void Draw(){
		int NowSize = GetFontSize();
		SetFontSize(size);
		DrawFormatString(x - width / 2,y - height / 2,color,str.c_str());
	}
};
その後、ゲーム全体を管理するGame_ObjにGraphData_Obj* Graph を置き、Game_Objのコンストラクタで
GraphData_Obj* Graph = new GraphData_Obj;
としてインスタンスを生成してやりました。
その後、Game_Obj内のその他のオブジェクトがGraphのアドレスを持つようにして、どのクラスからでも画像データにアクセスしやすくしました。

しかし、あとでほかのクラスから画像情報(vector)をもらい描画しようとしても、表示されません。
デバッグをしてみたところ、LoadGraphで得たvectorのデータ(intのハンドル)をもらうことには成功しているようなのです。
色々試行錯誤してみたのですが、どうにも理由がわかりません。
お知恵を貸していただければありがたいです。

ふじ

Re: コンストラクタでvectorを用いてのLoadGraphについて

#2

投稿記事 by ふじ » 10年前

ソース全体が見れないので推測ですが、
DxLib_Init()を実行する前にLoadGraph()を行っていませんか?
DXライブラリの関数は基本的にDxLib_Init()の後に実行しなければなりません。

もしそうでしたら、コンストラクタではなくGraphic_dataクラス内にinit()等といった関数を設け
その中でLoadGraph()を記述するといいと思います。
そしてDxLib_Init()を実行した後にGraphic_dataのinit()を実行するという形なら動くかと思います。

ねの

Re: コンストラクタでvectorを用いてのLoadGraphについて

#3

投稿記事 by ねの » 10年前

>ふじ さん
最初にソースを書いていた時はそこを間違っていて読み込みに失敗していたのですが、現状ではそこは直されています。
LoadGraphでは失敗したときに-1が返ってきますが、今のソースだとハンドルらしきものがきちんと帰ってきているようです。ただ、画像が表示されないのでどうにもお手上げな状況です。

ソースが不足しているようなので、すべて載せさせていただきたいと思います。

Image.h

コード:

/***********************************/
//			画像処理クラス         //
/***********************************/

class Graphic_data
{
public:
	vector<int> graph;
	int width, height;

	Graphic_data(char* name){
		// 読み込みに使う変数
		int X_num, Y_num = 1, Allnum;
		int handle;
		
		//一枚画像読み込み
		handle = LoadGraph(name, 0);

		//大きさから枚数を調べる 枚数分int領域確保
		GetGraphSize(handle, &width, &height);
		if (width % height == 0){
			X_num = Allnum = width / height;
			int num[20] = { 0 };

			// LoadDivGraphで枚数を分けて確保したint領域に読み込み その後vectorに入れていく
			LoadDivGraph(name, Allnum, X_num, Y_num, width, height, num, 0);
			for (int i = 0; i < Allnum; i++){
				graph.push_back(num[i]);
			}

			width = height;
		}
		else{
			graph.push_back(handle);
		}
	}
};

class GraphData_Obj{
public:
	/*ほしい画像データ列挙*/

	map<string, Graphic_data*> Menu;
	vector<Graphic_data*> Player;
	vector<Graphic_data*> Enemy;
	vector<Graphic_data*> Effect;


	void Init(){
		///////////////////////////////////////////
		/*--------- 画像データ読み込み -----------*/
		///////////////////////////////////////////

		// 読み込みに使うファイル名変数
		char adress[256];

		/* 自機の画像読み込み */
		for (int i = 0;; i++){
			sprintf_s(adress, "Image/Player/%d.png", i);
			if ((LoadGraph(adress)) == -1)break;
			Graphic_data* tmp = new Graphic_data(adress);
			Player.push_back(tmp);
		}

		/* 敵の画像読み込み */
		for (int i = 0;;i++){
			sprintf_s(adress, "Image/Enemy/%d.png", i);
			if (( LoadGraph(adress)) == -1)break;
			Graphic_data* tmp = new Graphic_data(adress);
			Enemy.push_back(tmp);
		}

		/* 敵の弾画像読み込み */
		for (int i = 0;; i++){

			sprintf_s(adress, "Image/Enemy/Bullet/%d", i);
			if (( LoadGraph(adress)) == -1)break;
			Graphic_data* tmp = new Graphic_data(adress);
			Enemy.push_back(tmp);
		}

		/* メニューの画像読み込み */
		Menu["BackGround1"] = GetMapAdress("Image/Menu/BackGround.png");
		Menu["BackGround2"] = GetMapAdress("Image/Menu/BackGround1.png");

		char name[256];

		for (int i = 0;; i++){
			sprintf_s(adress, "Image/Menu/StageGraph/%d.png", i);
			if (LoadGraph(adress) == -1)break;
			sprintf_s(name, "StageGraph%d", i);
			Menu[name] = GetMapAdress(adress);
		}
	}
	// Mapを用いたコンテナに画像を登録するための関数(アドレスをすぐに返すため一行で登録が可能)
	Graphic_data* GetMapAdress(string data_address){
		char name[256];
		sprintf_s(name, data_address.c_str());
		Graphic_data* tmp = new Graphic_data(name);
		return tmp;
	}
};


class String_Obj{
public:
	string str;
	int x, y,width,height, size;
	int num;
	int color;

	String_Obj(int point_x,int point_y,string data, int DrawSize, int DrawColor){
		x = point_x;
		y = point_y;
		str = data;
		size = DrawSize;
		num = str.size();
		width = size * num;
		height = size;
		color = DrawColor;
	}
	void Draw(){
		int NowSize = GetFontSize();
		SetFontSize(size);
		DrawFormatString(x - width / 2,y - height / 2,color,str.c_str());
	}
};
Object.h

コード:


/*宣言*/
enum { MENU, STAGE }GAME_STATE;

class Effect_Obj;
class Menu_Obj;
class Game_Obj;
class Stage_Obj;
class GraphData_Obj;

class Chara_Obj{
public:
	int x, y, r;
	double x_speed, y_speed, x_vector, y_vector,extend, extend_x, extend_y, angle;

	vector<int> graph;
	int graph_count = 0;
	int width, height;

	Chara_Obj();
	Chara_Obj(int point_x,int point_y,Graphic_data graph_data);

	virtual void Draw();
};

class Game_Obj{
public:
	/*他のオブジェクトへのポインタ*/
	Menu_Obj* Menu;
	Stage_Obj* Stage;
	GraphData_Obj* Graph;
	list<Effect_Obj*> EffectList; // 画面全体にかかるエフェクトを処理する。

	Game_Obj();
	void Main();
	void Calc();
};

class Menu_Obj{
public:
	GraphData_Obj* Graph;

	String_Obj* Title;
	Chara_Obj* GameStart;
	Chara_Obj* Plane;

	Menu_Obj(GraphData_Obj* Graph);
	void Main();
	void Calc();
	void Draw();
};

class Stage_Obj{
public:
	GraphData_Obj* Graph;
	list<Chara_Obj*> EnemyList;
	list<Chara_Obj*> EnemyOrderList;
	virtual void Calc();
	virtual void Main();
};

Chara_Obj.h

コード:

Chara_Obj::Chara_Obj(){
	/*初期化*/
	graph_count = 0;
	angle = 0;
	extend_x = 1;
	extend_y = 1;
	extend = 1;
	x_vector = 0;
	y_vector = 0;
	x_speed = 0;
	y_speed = 0;
	x = 0;
	y = 0;
	r = 0;
}

Chara_Obj::Chara_Obj(int point_x,int point_y,Graphic_data graph_data){
	this->Chara_Obj::Chara_Obj();
	graph = graph_data.graph;
	width = graph_data.width;
	height = graph_data.height;
	x = point_x;
	y = point_y;
}

void Chara_Obj::Draw(){

	/* アニメーション */
	graph_count++;
	if (graph.size() == graph_count)graph_count = 0;

	if (graph.size() != 0){
		DrawRotaGraph(x, y, extend, angle, graph.at(graph_count), true, 0);
	}
}

Menu_Obj.h

コード:

Menu_Obj::Menu_Obj(GraphData_Obj* Graph_Source){
	Graph = Graph_Source;

	GameStart = new Button(WINDOW_WIDTH/2,WINDOW_HEIGHT /4 *3,"GameStart", 24, WHITE);
	Title = new String_Obj(WINDOW_WIDTH / 2, WINDOW_HEIGHT / 4, "MISSON SHOOT", 50, WHITE);

	Plane = new Chara_Obj(WINDOW_WIDTH/2, WINDOW_HEIGHT/5*2,*(Graph->Player.at(0)));
}

void Menu_Obj::Main(){
	Calc();
	Draw();
}
void Menu_Obj::Calc(){

}
void Menu_Obj::Draw(){
	// 背景の線を描画
	SetDrawBlendMode(DX_BLENDMODE_ALPHA, 50);
	for (int i = 30; i != WINDOW_WIDTH; i += 30){
		DrawLine(i, 0, i, WINDOW_HEIGHT, WHITE, 2);
	}
	for (int i = 30; i != WINDOW_HEIGHT; i += 30){
		DrawLine(0, i, WINDOW_WIDTH, i, WHITE, 2);
	}
	SetDrawBlendMode(DX_BLENDMODE_NOBLEND, 0);

	/* 文字表示 */
	Title->Draw();
	GameStart->Draw();

	Plane->Draw();
}
global.h

コード:

#include <math.h>
#include <vector>
#include <stdlib.h>
#include <algorithm>
#include <map>
/////////////////
/*  マクロ */
////////////////
/*色*/
#define RED 0xff00000
#define BLUE 0x0000ff
#define GREEN 0x00ff00
#define WHITE 0xffffff
#define BLACK 0x000000
#define YELLOW 0x00ffff
#define PURPLE 0xff00ff
#define WATERBLUE 0xffff00

/*値*/
#define ALPHABLEND_STRING 150	//アルファブレンド時の文字の値
#define ALPHABLEND_GRAPH 150	//アルファブレンド時の画像の値

/*ウィンドウサイズ関連*/
#define WINDOW_WIDTH 960
#define WINDOW_HEIGHT 540	


#include <list>
#include <String>
#include <fstream>

using namespace std;

// 全体に関する変数
static bool SceneChange = false;
static int Font00;

//画像処理系クラス
#include "Image.h"

//その他クラス
#include "Object.h"

#include "Chara_Obj.h"
#include "Menu.h"
#include "Game_Obj.h"
#include "Stage_Obj.h"


main.cpp

コード:

#define _CRT_SECURE_NO_WARNINGS
#include "DxLib.h"
#include "Input.h"
#include "Global.h"
#include <fstream>


/* main関数 */
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
	LPSTR lpCmdLine, int nCmdShow)
{
	_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);

	// ウィンドウモード設定
	SetGraphMode(WINDOW_WIDTH, WINDOW_HEIGHT, 32);

	ChangeWindowMode(true);
	SetBackgroundColor(5, 5, 35);

	if (DxLib_Init() == -1)	// DXライブラリ初期化処理
	{
		return -1;				// エラーが起きたら直ちに終了
	}

	// 裏画面処理
	SetDrawScreen(DX_SCREEN_BACK);
	//SetDrawMode(DX_DRAWMODE_BILINEAR);


	/*クラス宣言*/
	Game_Obj Game;
	DebugMode Debug;


	// フォント設定
	if (AddFontResourceEx("data/Font/shuttle/shuttle.ttf", FR_PRIVATE, NULL) > 0){
	}
	else{
		MessageBox(NULL, "フォント読み込み失敗", "", MB_OK);
	}
	ChangeFont("Gts.Shuttle", DX_CHARSET_DEFAULT);

	while (ProcessMessage() == 0 && ClearDrawScreen() == 0){
		// 全体にかかわる処理
		CheckKeyState();
		Debug.Calc();

		Game.Main();
		

		ScreenFlip();
	}

	RemoveFontResourceEx("data/Font/shuttle/shuttle.ttf", FR_PRIVATE, NULL);

	return 0;
}
煩雑なコードではありますが、なにとぞ参考になれば幸いです、

ふじ

Re: コンストラクタでvectorを用いてのLoadGraphについて

#4

投稿記事 by ふじ » 10年前

読み込んだグラフィックは、DeleteGraph等で明示的に開放しない限りは残りつづけると思うので、
やはりこのプログラムの中に誤りがあるのではないでしょうか。

ソースを読ませて頂きましたが私にはどこが原因なのか分かりません。
すでにやっていたら申し訳ありませんが、
とりあえずは、DxライブラリのprintfDx関数か、Visual Studioのブレークポイントを使って
画像をロードしたときに得たグラフィックハンドルと、画像を描画するときのグラフィックハンドルの値が
一致しているかを確認してみてはいかがでしょうか。
あまり力にはなれませんが、何かの手掛かりになれば幸いです。

アバター
みけCAT
記事: 6734
登録日時: 14年前
住所: 千葉県
連絡を取る:

Re: コンストラクタでvectorを用いてのLoadGraphについて

#5

投稿記事 by みけCAT » 10年前

オフトピック
直接関係ないかもしれないが、
LoadGraph関数の戻り値を捨てているのとnewしたオブジェクトをdeleteしていないので、メモリリーク祭りになりそうだなあ…
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

アバター
みけCAT
記事: 6734
登録日時: 14年前
住所: 千葉県
連絡を取る:

Re: コンストラクタでvectorを用いてのLoadGraphについて

#6

投稿記事 by みけCAT » 10年前

ねの さんが書きました:前提として、今回お尋ねしたいのは、LoadGraph(LoadDivGraph)を用いて読み込んだ画像はDxライブラリ終了時までアドレスに記憶されるのか、ということです。
アドレスは記憶領域を指定する「番号」なので、「アドレスに記憶」はされないと思います。
読み込んだ画像のデータは、DeleteGraph関数で削除したり、ChangeWindowMode関数を実行したりすると消えるはずなので、
DXライブラリ終了時まで記憶されるとは限りません。
ねの さんが書きました:LoadGraphでは失敗したときに-1が返ってきますが、今のソースだとハンドルらしきものがきちんと帰ってきているようです。ただ、画像が表示されないのでどうにもお手上げな状況です。
Image.hの26行目のLoadDivGraph関数の戻り値をチェックしていないですね。
さらに、ここでは画像全体の大きさであるwidthとheightをそのまま画像1枚あたりの大きさとして渡しています。
その結果、(width % height == 0 && width != height)のとき、グラフィックの分割に失敗するようです。
こちらの環境で実験を行った結果、この条件ではLoadDivGraph関数は-1を返し、画像は描画されませんでした。
Game_Obj::Main()の内容がわからないので確定はできませんが、これが原因ではないでしょうか?

コード:

#include <DxLib.h>

int WINAPI WinMain(HINSTANCE,HINSTANCE,LPSTR,int) {
	ChangeWindowMode(TRUE);
	if (DxLib_Init() == -1) return 1;
	SetDrawScreen(DX_SCREEN_BACK);
	int arr1[3];
	int arr2[3];
	int ret1, ret2;
	// 横300px, 縦100pxの読み込み可能で正しいPNG形式の画像ファイルtest.pngをカレントディレクトリに用意する
	ret1 = LoadDivGraph("test.png", 3, 3, 1, 300, 100, arr1); // 失敗する
	ret2 = LoadDivGraph("test.png", 3, 3, 1, 100, 100, arr2); // 成功するはず
	while (ProcessMessage() == 0 && ClearDrawScreen() == 0) {
		int clWhite = GetColor(255, 255, 255);
		DrawFormatString(10, 10, clWhite, "ret1 = %d", ret1);
		for (int i = 0; i < 3; i++) {
			DrawGraph(10 + 150 * i, 50, arr1[i], FALSE);
		}
		DrawFormatString(10, 210, clWhite, "ret2 = %d", ret2);
		for (int i = 0; i < 3; i++) {
			DrawGraph(10 + 150 * i, 250, arr2[i], FALSE);
		}
		ScreenFlip();
	}
	return 0;
}
添付ファイル
loaddivgraph_test.zip
実験用ソース・バイナリ・画像
(2.14 MiB) ダウンロード数: 97 回
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

ねの

Re: コンストラクタでvectorを用いてのLoadGraphについて

#7

投稿記事 by ねの » 10年前

>ふじ さん
printfDxを用いてLoadDivGraphで得た値を確認してみましたが、ハンドルは一致しているようでした。
ただ、実際のところ読み込みに失敗しているはずなので理由はよくわかりませんでした・・・

>みけCAT さん
それでした・・・
自分のプログラムの目的として、「画像の横÷縦が割り切れるなら、画像を正方形でとって分割できるものとして分割、一つのファイルにまとめる(アニメーションを一つのvectorにまとめる)」
というような処理にしようとしていたのですが、
LoadDibGraphに渡す引数のwidthとheightの値を、元の画像サイズそのままで入力していました。

しかし気になるのは、LoadDivGraphからは-1ではなく普通のハンドル(のように見える値)が返ってきたことです。
DxLib_Init()を行った後にインスタンスを生成して、ブレークポイントを使用して値の確認を行っていましたが、値はそれらしきものが入っていました。
(それが原因に気づかせなかった要因ともいえますが、そもそも引数を間違える怠慢が悪いですね)
そのあたりの動作は依然よくはわかりませんでしたが、とりあえず読み込みができるようになって一安心しました。

ふじさん、みけCATさん本当にありがとうございました。
オフトピック
メモリリークですが、みけCATさんの発言を受けて解消しました。こういう怠慢が今回の件につながってる気がします。

閉鎖

“C言語何でも質問掲示板” へ戻る