キャッチゲーム

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

キャッチゲーム

#1

投稿記事 by からす » 2年前

DXライブラリで上から降ってくる星を網でとるというキャッチゲームを作っています。
その上から降ってくる星の部分が現在画像のようになっていて、1つづつ降ってくるようにしたいのですが上手くいきません。
STAR_NUMを利用してループしているところで画像のようになっているのではないかと思います。
C言語はやさしいCの内容はほぼできる程度です。よろしくお願いします。
(<img>の使い方が分からなかったので画像リンクです)
https://d.kuku.lu/1c8a2851a

コード:

#include "DxLib.h"
#include <stdlib.h>
#include <time.h>

#define STAR_NUM 50

struct Position
{
	int x;
	int y;
	int isExist; //存在したらTRUE、いなかったらFALSE
};

struct Position a; //虫取り網の場所
struct Position s[STAR_NUM]; //星の場所
struct Position w; //アイテムの場所

int count;
int amispeed;
int score;
int star_num = STAR_NUM;
int Cr = GetColor(255, 255, 255);

//初期化
void init()
{
	int i;
	score = 0; //取った星の数
	a.x = 130, a.y = 300; //網の座標

	for (i = 0; i < STAR_NUM; i++)
	{
		s[i].isExist = TRUE;
		s[i].x = 0;
		s[i].y = 0;
	}
}

//以下3つ 星の発生、落下
//空いている配列の値を返す
int star_pre()
{
	int i;
	for (i = 0; i < STAR_NUM; i++)
	{
		if (s[i].isExist == FALSE)
			return i;
	}
	return -1;
}
//星の発生場所
void star_enter()
{
	int i;

	srand((unsigned)time(NULL));

	i = star_pre(); //空いている弾情報格納要素番号
	if (i != -1)
	{
		s[i].isExist = TRUE; //弾の存在フラグを立てる
		s[i].x = 0 + rand() % 585; //乱数の生成
		s[i].y = -10; //y座標は0
	}
}
//星の落下と当たり判定
void star_main()
{
	int i;

	if (count % 60 == 0) //60カウントに一回
		star_enter(); //発射
	for (i = 0; i < STAR_NUM; i++)
	{
		if (s[i].isExist == TRUE)
		{
			s[i].y += 5;
			if (s[i].y > a.y + 65 && s[i].x >= a.x + 125 && s[i].x <= a.x + 200 ) //かごの中にあるか?
				s[i].isExist = FALSE; //弾を消す
			else if (s[i].y == 480) //画面の中にあるか?
				s[i].isExist = FALSE; //弾を消す
			else
				s[i].isExist = TRUE;
		}
	}
}

//網の操作
void MoveAmi()
{
	amispeed = 20;

	if (CheckHitKey(KEY_INPUT_D) == 1 || CheckHitKey(KEY_INPUT_RIGHT) == 1)
		a.x += amispeed; //右
	if (CheckHitKey(KEY_INPUT_A) == 1 || CheckHitKey(KEY_INPUT_LEFT) == 1)
		a.x -= amispeed; //左

	if (a.x <= -150)
		a.x = -150;
	if (a.x >= 410)
		a.x = 410;
};

void Draw()
{
	int i;

	//画像読み込み
	static int BackHandle = LoadGraph("back.png"); //640×480
	static int Handle1 = LoadGraph("mushitoriami.png"); //150×150
	static int Handle2 = LoadGraph("star_yellow.png"); //55×51
	static int Handle3 = LoadGraph("star5.png"); //70×314

	//描画
	DrawGraph(0, 0, BackHandle, TRUE);
	DrawGraph(a.x, a.y, Handle1, TRUE);
	for (i = 0; i < STAR_NUM; i++)
	{
		if (s[i].isExist == FALSE)
			continue;
		DrawGraph(s[i].x, s[i].y, Handle2, TRUE);
	}

	DrawFormatString(0, 0, Cr, "SCORE:%d", score);
};

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
	// ウインドウモードに変更
	ChangeWindowMode(TRUE);

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

	init();

	while (1)
	{
		DrawFormatString(40, 100, Cr, "PUSH ENTER.");
		//エンターキーが押されるまで待機
		while (!CheckHitKey(KEY_INPUT_RETURN))
		{
			//メッセージループに代わる処理をする
			if (ProcessMessage() == -1)
			{
				DxLib_End(); //DXライブラリ使用の終了処理
				return 0; //ソフトの終了
			}
		}
		for (int time = 5000; time >= 0; time--)
		{
			//網
			MoveAmi();
			//星
			star_main();
			
			//描画
			Draw();
			DrawFormatString(0, 20, Cr, "TIME:%d", time);

			WaitTimer(50);

			ClearDrawScreen();
		}
	}

	WaitKey(); //キーの入力待ち(『WaitKey』を使用)

	DxLib_End(); //DXライブラリ使用の終了処理

	return 0; //ソフトの終了 
}

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

Re: キャッチゲーム

#2

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

star_main 関数において、

コード:

	if (count % 60 == 0) //60カウントに一回
		star_enter(); //発射
としていますが、 count を更新している部分が無いので、
count は明示的に初期化されていないグローバル変数の初期値の 0 のまま変化せず、
0 を 60 で割った余りは 0 なので、 star_enter 関数が毎回呼び出されます。
さらに、start_enter 関数において、

コード:

	srand((unsigned)time(NULL));
を毎回呼び出しており、 time 関数の戻り値は秒単位の環境が多いため、
短い間隔で呼び出すと rand 関数の戻り値が同じになる可能性が高くなってしまいます。

というわけで、
・count の更新処理を追加する
・srand 関数を毎回呼び出さず、プログラムの開始時に1回だけ呼び出すようにする
と改善すると考えられます。

rand 関数のかわりに GetRand 関数を使うのもいいかもしれません。

また、WinMain 関数内の for ループの中にメッセージ処理が無いのもよくないかもしれません。
(本当はこのようにゲームの状態によって実行するメインループを変えること自体があまりよくないとみなされますが、
まあそれの改善は余裕があれば…)
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

アバター
あたっしゅ
記事: 663
登録日時: 13年前
住所: 東京23区
連絡を取る:

Re: キャッチゲーム

#3

投稿記事 by あたっしゅ » 2年前

東上☆海美☆「
.png ファイルがないので、DrawBox() や DrawLine() で、描いています。

init() の中で、isExist を TRUE で初期化していたので、
最初は、星 STAR_NUM 個が、一つに重なって、落下していたみみ。

star_main() の中をいじったみみ。

あと、srand((unsigned)time(NULL)); と LoadGraph() をメインループの外に出しましたみみ。

コード:

//
// https://dixq.net/forum/viewtopic.php?f=3&t=21286&sid=cf6acf862baddc1c27a0c7b82fb3da4d
// キャッチゲーム - ミクプラ(ja)
//
#include "DxLib.h"
#include <stdlib.h>
#include <time.h>

//#define STAR_NUM 50
#define STAR_NUM 5

struct Position
{
	int x;
	int y;
	int isExist; //存在したらTRUE、いなかったらFALSE
};

struct Position a; //虫取り網の場所
struct Position s[STAR_NUM]; //星の場所
struct Position w; //アイテムの場所

int count;
int amispeed;
int score;
int star_num = STAR_NUM;
int Cr = GetColor(255, 255, 255);

// Draw() から移動
int BackHandle; //640×480
int Handle1; //150×150
int Handle2; //55×51
int Handle3; //70×314
// Draw() から移動 終わり


//初期化
void init0()
{
	// star_enter() から移動
	srand((unsigned)time(NULL));

	// Draw() から移動
	//画像読み込み
	BackHandle = LoadGraph("back.png"); //640×480
	Handle1 = LoadGraph("mushitoriami.png"); //150×150
	Handle2 = LoadGraph("star_yellow.png"); //55×51
	Handle3 = LoadGraph("star5.png"); //70×314
	// Draw() から移動 終わり

	// 描画先画面を裏画面にセット
	SetDrawScreen(DX_SCREEN_BACK);
}


//初期化
void init()
{
	int i;

	score = 0; //取った星の数
	a.x = 130, a.y = 300; //網の座標

	for (i = 0; i < STAR_NUM; i++)
	{
		//s[i].isExist = TRUE;
		s[i].isExist = FALSE;
		s[i].x = 0;
		s[i].y = 0;
	}
}


//以下3つ 星の発生、落下
//空いている配列の値を返す
int
star_pre()
{
	int i;
	for (i = 0; i < STAR_NUM; i++)
	{
		if (s[i].isExist == FALSE)
			return i;
	}
	return -1;
}


//星の発生場所
void
star_enter()
{
	int i;

	//srand((unsigned)time(NULL));

	i = star_pre(); //空いている弾情報格納要素番号
	if (i != -1)
	{
		s[i].isExist = TRUE; //弾の存在フラグを立てる
		s[i].x = 0 + rand() % 585; //乱数の生成
		s[i].y = -10; //y座標は0
	}
}


//星の落下と当たり判定
void
star_main()
{
	int i;

	//if (count % 60 == 0) //60カウントに一回
	if (count++ % 20 == 0) //20 カウントに一回
			star_enter(); //発射

	for (i = 0; i < STAR_NUM; i++)
	{
		if (s[i].isExist == TRUE)
		{
			s[i].y += 5;
			if (s[i].y > a.y + 65 && s[i].x >= a.x + 125 && s[i].x <= a.x + 200) { //かごの中にあるか?
				s[i].isExist = FALSE; //弾を消す
     			score += 1;
		    }
			//else if (s[i].y == 480) //画面の中にあるか?
			if (s[i].y == 480) //画面の中にあるか?
				s[i].isExist = FALSE; //弾を消す
			//else
			//	s[i].isExist = TRUE;
		}
	}
}

//網の操作
void MoveAmi()
{
	amispeed = 20;

	if (CheckHitKey(KEY_INPUT_D) == 1 || CheckHitKey(KEY_INPUT_RIGHT) == 1)
		a.x += amispeed; //右
	if (CheckHitKey(KEY_INPUT_A) == 1 || CheckHitKey(KEY_INPUT_LEFT) == 1)
		a.x -= amispeed; //左

	if (a.x <= -150)
		a.x = -150;
	if (a.x >= 410)
		a.x = 410;
};


void
Draw()
{
	int i;

	//画像読み込み
	//static int BackHandle = LoadGraph("back.png"); //640×480
	//static int Handle1 = LoadGraph("mushitoriami.png"); //150×150
	//static int Handle2 = LoadGraph("star_yellow.png"); //55×51
	//static int Handle3 = LoadGraph("star5.png"); //70×314

	//描画
	DrawGraph(0, 0, BackHandle, TRUE);

	//DrawGraph(a.x, a.y, Handle1, TRUE);
	DrawBox(a.x, a.y, a.x + 150, a.y + 150, GetColor(128, 128, 128), TRUE);
	DrawLine(a.x+150, a.y +  50, a.x + 200, a.y + 150, GetColor(128, 128, 128), TRUE);
	DrawLine(a.x+200, a.y + 150, a.x + 250, a.y +  50, GetColor(128, 128, 128), TRUE);

	for (i = 0; i < STAR_NUM; i++)
	{
		if (s[i].isExist == FALSE)
			continue;
		//DrawGraph(s[i].x, s[i].y, Handle2, TRUE);
		DrawBox(s[i].x, s[i].y, s[i].x+50, s[i].y+50, GetColor( 255, 255, 0 ), TRUE );
	}

	DrawFormatString(0, 0, Cr, "SCORE:%d", score);
};


int WINAPI 
WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
	// ウインドウモードに変更
	ChangeWindowMode(TRUE);

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

	//init();
	init0();

	while (1)
	{
		DrawFormatString(40, 100, Cr, "PUSH ENTER.");
		ScreenFlip();

		//エンターキーが押されるまで待機
		while (!CheckHitKey(KEY_INPUT_RETURN))
		{
			//メッセージループに代わる処理をする
			if (ProcessMessage() == -1)
			{
				DxLib_End(); //DXライブラリ使用の終了処理

				return 0; //ソフトの終了
			}
		}


		init();
		int time = 5000;

		while (ScreenFlip() == 0 && ProcessMessage() == 0 && ClearDrawScreen() == 0)
		//for (int time = 5000; time >= 0; time--)
		{
			if (--time < 0) {
				break;
			}

			//網
			MoveAmi();
			//星
			star_main();

			//描画
			Draw();
			DrawFormatString(0, 20, Cr, "TIME:%d", time);

			WaitTimer(50);

			//ClearDrawScreen();
		}
	}

	//WaitKey(); //キーの入力待ち(『WaitKey』を使用)

	DxLib_End(); //DXライブラリ使用の終了処理

	return 0; //ソフトの終了 
}


// end.
VTuber:
東上☆海美☆(とうじょう・うみみ)
http://atassyu.php.xdomain.jp/vtuber/index.html
レスがついていないものを優先して、レスするみみ。時々、見当外れなレスしみみ。

中の人:
手提鞄あたッしュ、[MrAtassyu] 手提鞄屋魚有店
http://ameblo.jp/mratassyu/
Pixiv: 666303
Windows, Mac, Linux, Haiku, Raspbery Pi, Jetson Nano, 電子ブロック 持ち。

からす

Re: キャッチゲーム

#4

投稿記事 by からす » 2年前

みけCAT さんが書きました:
2年前
star_main 関数において、

コード:

	if (count % 60 == 0) //60カウントに一回
		star_enter(); //発射
としていますが、 count を更新している部分が無いので、
count は明示的に初期化されていないグローバル変数の初期値の 0 のまま変化せず、
0 を 60 で割った余りは 0 なので、 star_enter 関数が毎回呼び出されます。
さらに、start_enter 関数において、

コード:

	srand((unsigned)time(NULL));
を毎回呼び出しており、 time 関数の戻り値は秒単位の環境が多いため、
短い間隔で呼び出すと rand 関数の戻り値が同じになる可能性が高くなってしまいます。

というわけで、
・count の更新処理を追加する
・srand 関数を毎回呼び出さず、プログラムの開始時に1回だけ呼び出すようにする
と改善すると考えられます。

rand 関数のかわりに GetRand 関数を使うのもいいかもしれません。

また、WinMain 関数内の for ループの中にメッセージ処理が無いのもよくないかもしれません。
(本当はこのようにゲームの状態によって実行するメインループを変えること自体があまりよくないとみなされますが、
まあそれの改善は余裕があれば…)
なるほど…大事なcountが抜け落ちてました...修正します。ありがとうございました...!

からす

Re: キャッチゲーム

#5

投稿記事 by からす » 2年前

あたっしゅ さんが書きました:
2年前
東上☆海美☆「
.png ファイルがないので、DrawBox() や DrawLine() で、描いています。

init() の中で、isExist を TRUE で初期化していたので、
最初は、星 STAR_NUM 個が、一つに重なって、落下していたみみ。

star_main() の中をいじったみみ。

あと、srand((unsigned)time(NULL)); と LoadGraph() をメインループの外に出しましたみみ。

コード:

//
// https://dixq.net/forum/viewtopic.php?f=3&t=21286&sid=cf6acf862baddc1c27a0c7b82fb3da4d
// キャッチゲーム - ミクプラ(ja)
//
#include "DxLib.h"
#include <stdlib.h>
#include <time.h>

//#define STAR_NUM 50
#define STAR_NUM 5

struct Position
{
	int x;
	int y;
	int isExist; //存在したらTRUE、いなかったらFALSE
};

struct Position a; //虫取り網の場所
struct Position s[STAR_NUM]; //星の場所
struct Position w; //アイテムの場所

int count;
int amispeed;
int score;
int star_num = STAR_NUM;
int Cr = GetColor(255, 255, 255);

// Draw() から移動
int BackHandle; //640×480
int Handle1; //150×150
int Handle2; //55×51
int Handle3; //70×314
// Draw() から移動 終わり


//初期化
void init0()
{
	// star_enter() から移動
	srand((unsigned)time(NULL));

	// Draw() から移動
	//画像読み込み
	BackHandle = LoadGraph("back.png"); //640×480
	Handle1 = LoadGraph("mushitoriami.png"); //150×150
	Handle2 = LoadGraph("star_yellow.png"); //55×51
	Handle3 = LoadGraph("star5.png"); //70×314
	// Draw() から移動 終わり

	// 描画先画面を裏画面にセット
	SetDrawScreen(DX_SCREEN_BACK);
}


//初期化
void init()
{
	int i;

	score = 0; //取った星の数
	a.x = 130, a.y = 300; //網の座標

	for (i = 0; i < STAR_NUM; i++)
	{
		//s[i].isExist = TRUE;
		s[i].isExist = FALSE;
		s[i].x = 0;
		s[i].y = 0;
	}
}


//以下3つ 星の発生、落下
//空いている配列の値を返す
int
star_pre()
{
	int i;
	for (i = 0; i < STAR_NUM; i++)
	{
		if (s[i].isExist == FALSE)
			return i;
	}
	return -1;
}


//星の発生場所
void
star_enter()
{
	int i;

	//srand((unsigned)time(NULL));

	i = star_pre(); //空いている弾情報格納要素番号
	if (i != -1)
	{
		s[i].isExist = TRUE; //弾の存在フラグを立てる
		s[i].x = 0 + rand() % 585; //乱数の生成
		s[i].y = -10; //y座標は0
	}
}


//星の落下と当たり判定
void
star_main()
{
	int i;

	//if (count % 60 == 0) //60カウントに一回
	if (count++ % 20 == 0) //20 カウントに一回
			star_enter(); //発射

	for (i = 0; i < STAR_NUM; i++)
	{
		if (s[i].isExist == TRUE)
		{
			s[i].y += 5;
			if (s[i].y > a.y + 65 && s[i].x >= a.x + 125 && s[i].x <= a.x + 200) { //かごの中にあるか?
				s[i].isExist = FALSE; //弾を消す
     			score += 1;
		    }
			//else if (s[i].y == 480) //画面の中にあるか?
			if (s[i].y == 480) //画面の中にあるか?
				s[i].isExist = FALSE; //弾を消す
			//else
			//	s[i].isExist = TRUE;
		}
	}
}

//網の操作
void MoveAmi()
{
	amispeed = 20;

	if (CheckHitKey(KEY_INPUT_D) == 1 || CheckHitKey(KEY_INPUT_RIGHT) == 1)
		a.x += amispeed; //右
	if (CheckHitKey(KEY_INPUT_A) == 1 || CheckHitKey(KEY_INPUT_LEFT) == 1)
		a.x -= amispeed; //左

	if (a.x <= -150)
		a.x = -150;
	if (a.x >= 410)
		a.x = 410;
};


void
Draw()
{
	int i;

	//画像読み込み
	//static int BackHandle = LoadGraph("back.png"); //640×480
	//static int Handle1 = LoadGraph("mushitoriami.png"); //150×150
	//static int Handle2 = LoadGraph("star_yellow.png"); //55×51
	//static int Handle3 = LoadGraph("star5.png"); //70×314

	//描画
	DrawGraph(0, 0, BackHandle, TRUE);

	//DrawGraph(a.x, a.y, Handle1, TRUE);
	DrawBox(a.x, a.y, a.x + 150, a.y + 150, GetColor(128, 128, 128), TRUE);
	DrawLine(a.x+150, a.y +  50, a.x + 200, a.y + 150, GetColor(128, 128, 128), TRUE);
	DrawLine(a.x+200, a.y + 150, a.x + 250, a.y +  50, GetColor(128, 128, 128), TRUE);

	for (i = 0; i < STAR_NUM; i++)
	{
		if (s[i].isExist == FALSE)
			continue;
		//DrawGraph(s[i].x, s[i].y, Handle2, TRUE);
		DrawBox(s[i].x, s[i].y, s[i].x+50, s[i].y+50, GetColor( 255, 255, 0 ), TRUE );
	}

	DrawFormatString(0, 0, Cr, "SCORE:%d", score);
};


int WINAPI 
WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
	// ウインドウモードに変更
	ChangeWindowMode(TRUE);

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

	//init();
	init0();

	while (1)
	{
		DrawFormatString(40, 100, Cr, "PUSH ENTER.");
		ScreenFlip();

		//エンターキーが押されるまで待機
		while (!CheckHitKey(KEY_INPUT_RETURN))
		{
			//メッセージループに代わる処理をする
			if (ProcessMessage() == -1)
			{
				DxLib_End(); //DXライブラリ使用の終了処理

				return 0; //ソフトの終了
			}
		}


		init();
		int time = 5000;

		while (ScreenFlip() == 0 && ProcessMessage() == 0 && ClearDrawScreen() == 0)
		//for (int time = 5000; time >= 0; time--)
		{
			if (--time < 0) {
				break;
			}

			//網
			MoveAmi();
			//星
			star_main();

			//描画
			Draw();
			DrawFormatString(0, 20, Cr, "TIME:%d", time);

			WaitTimer(50);

			//ClearDrawScreen();
		}
	}

	//WaitKey(); //キーの入力待ち(『WaitKey』を使用)

	DxLib_End(); //DXライブラリ使用の終了処理

	return 0; //ソフトの終了 
}


// end.
いくつかの修正部分を教えて下さりありがとうございます!参考に修正改良していこうと思います!

naohiro
記事: 8
登録日時: 2年前

Re: キャッチゲーム

#6

投稿記事 by naohiro » 2年前

DXライブラリを利用しているのになぜ <stdlib.h>のrand関数を使うのでしょうか?
DXライブラリを利用しているのに rand関数は一切利用してはなりません。
SRand関数/GetRand関数を代わりに使わなければなりません。

アバター
usao
記事: 1887
登録日時: 11年前

Re: キャッチゲーム

#7

投稿記事 by usao » 2年前

> DXライブラリを利用しているのに rand関数は一切利用してはなりません。
> SRand関数/GetRand関数を代わりに使わなければなりません。

本当にそのような話があるのですか?
(少なくとも公式らしきリファレンスの GetRand, SRand の部分にはそのような要件は記載されていないように見えます.)

本当にそんなク〇みたいな制約があるとすれば,当然ながらどこか(公式みたいなところ?)で相応の注意書き等がありそうに思いますので,
そういった情報ソースを提示されると良いのではないでしょうか.

アバター
usao
記事: 1887
登録日時: 11年前

Re: キャッチゲーム

#8

投稿記事 by usao » 2年前

オフトピック
GetRand のリファレンスらしきページ(https://dxlib.xsrv.jp/function/dxfunc_other.html)には

> 解説  乱数を得ます。この関数は 0 から RandMax で指定した数値のどれかの数値を返します。

くらいの説明しかない様子.
これだけを読む限りでは,個人的には「そもそも何のためにこんなものがわざわざ用意されているの?」と首をかしげたくなります.
こんなん,「rand でなくこっちを使おう」とか思えない.

(ひょっとしたらどこか他のところに詳細情報があるのかもしれませんが)

返信

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