Re: ドットイートゲーム 敵の動きについて教えてください。

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

Re: ドットイートゲーム 敵の動きについて教えてください。

#1

投稿記事 by けいすけ » 8年前

以前、質問させていただいたドットイートゲームについて新たにお尋ねしたいことがあります。
先日、敵の表示がうまくいかないと、悩んでおりましたがその件についてはアドバイスをいただきうまくいきました。ありがとうございます。
今回は敵の動きについて、以下の点を改善したいです。
それと主人公の動き(キーボードを押してから動くのが遅い)も早くする方法があれば教えていただきたいです。

・敵がいきなり止まってしまう。(主人公に一回当たった時点で消えてしまう。)
・敵が消滅してしまう(マップの座標外にでてしまう?)

コード:

//////////////////////////////////////////////////////////////////////
//
// パックマンプログラム pacman.c
//
//    
////////////////////////////////////////////////////////////////////////
#pragma warning(disable:4996)
#include <stdio.h>
#include <stdlib.h> // system()
#include <windows.h> // Sleep()
#include <conio.h> // kbhit()

#define SIZE 15 // 一辺の長さ
#define EMPTY 0 // 何もない場所は0
#define WALL 1	// 壁
#define FOOD 2	// えさ(ドット)
#define PACMAN 3// 主人公

int main()
{
	int wait_time = 300;
	int x, y;
	int food_count = 0;//エサの個数
	int cx, cy;//パックマン
	int ex, ey;//敵
	int key;
	int point = 0;
	int kx, ky;//自機の座標を保存するための変数
	int mx, my;//敵機の座標を保存するための変数
	int life = 3;
	// 最も重要なデータ 1 は壁、えさは2、空白は 0
	// field[y][x]となるので注意!
	int field[SIZE][SIZE] =
	{
		{ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 },
		{ 1,2,2,2,2,2,2,2,2,2,2,2,2,2,1 },
		{ 1,2,1,1,1,2,1,1,1,2,1,2,1,2,1 },
		{ 1,2,1,0,1,2,1,0,1,2,1,2,2,2,1 },
		{ 1,2,1,1,1,2,1,1,1,2,1,2,1,2,1 },
		{ 1,2,2,2,2,2,2,2,2,2,2,2,2,2,1 },
		{ 1,1,1,1,1,0,1,0,1,1,1,0,1,1,1 },
		{ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
		{ 1,1,1,1,1,2,1,2,2,1,1,2,1,1,1 },
		{ 1,2,2,2,2,2,1,2,1,1,1,2,2,2,1 },
		{ 1,2,1,1,1,1,0,0,0,0,2,2,1,2,1 },
		{ 1,2,2,2,0,2,2,1,2,1,2,0,1,2,1 },
		{ 1,2,1,1,1,0,1,1,2,2,2,1,1,2,1 },
		{ 1,2,2,2,2,2,2,2,2,1,2,2,2,2,1 },
		{ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 },
	};

	//まずはゲーム開始前の処理
	// パックマンを初期位置に配置
	cx = 7, cy = 7; //好きな場所に配置する!
	ex = 1, ey = 1;
	//エサの個数を数える
	for (y = 0; y < SIZE; y++)
	{
		for (x = 0; x < SIZE; x++)
		{
			if (field[y][x] == FOOD)
				food_count++;
		}

	}

	while (1)
	{
		// まずはPACMANの動き
		// キーが押されていたら、キーコードを取得する
		// 4なら左、6なら右、8なら上、2なら下
		if (kbhit()) // キーが入力されていれば真
		{
			key = getch();//一文字入力、エコーバックなし
			kx = cx, ky = cy;//自機の座標を保存する
			mx = ex; my = ey;//敵機の座標を保存する
			switch (key)
			{
			case '4':
				cx--;
				break;
			case '6':
				cx++;
				break;
			case '2':
				cy++;
				break;
			case '8':
				cy--;
				break;
			}

			cx = (cx + SIZE) % SIZE; //ワープ			
			switch (field[cy][cx])
			{
			case WALL://壁
				cx = kx, cy = ky; //動けないので cx, cy を元に戻す
								  //壁に当たると自機の座標に元の座標を代入する
				break;
			case FOOD:
				field[cy][cx] = 0;//餌を食べると餌が消える
				point += 10;      //得点加算
				food_count--;     //餌の数を減らしていく
				break;
			}
			if (cx == ex && cy == ey)
				life--;
		}
		//自機に向かう (敵機の動き)
		
		if (ex > cx) 
			ex--;
		else if (ex < cx) 
			ex++;
		else if (ey > cy) 
			ey--;
		else if (ey < cy) 
			ey++;

		if (field[ey][ex] == WALL)  //もし敵機が壁に当たったらもとの座標に戻す。
		{
			ex = mx; ey = my;
		}
		ex = (ex + SIZE) % SIZE; //ワープ
		// ここまでPACMANの動き

		

		 // 画面表示
		for (y = 0; y < SIZE; y++) {
			for (x = 0; x < SIZE; x++) {
				if (x == cx && y == cy)
					printf("C ");
				else if (x == ex && y == ey)
					printf("◇");
				else if (field[y][x] == FOOD)
					printf(". ");
				else if (field[y][x] == WALL)
					printf("■");
				else
					printf(" ");
			}
			printf("\n");
		}

		// すべての場所で field[y][x] の値を元に表示


		printf("\n");
		printf(" (cx,cy) = (%d, %d)\n", cx, cy);
		printf(" point: %d\n", point);
		printf("  LIFE: %d\n", life);

		if (life == 0)
		{
			printf("┏ ┏━┓┏━┓┏┳┓┳━┓ ┏━┓┳  ┳┳━┓┳━┓ ┓\n");
			printf("┃ ┃  ┓┣━┫┃┃┃┣┫   ┃  ┃┃  ┃┣┫  ┣┳┛ ┃\n");
			printf("┗ ┗━┛┻  ┻┻  ┻┻━┛ ┗━┛┗━┛┻━┛┻┗┛ ┛\n");
			Sleep(wait_time);
			break;
		}

		if (food_count == 0)       //餌を食べきったとき(=CRER)の画面表示
		{
			printf("┏ ┏━┓┏━┓┏┳┓┳━┓ ┏━┓ ┳━┓ ┳━┓ ┳━┓ ┓\n");
			printf("┃ ┃  ┓┣━┫┃┃┃┣┫   ┃     ┣┳┛ ┣┫   ┣┳┛ ┃\n");
			printf("┗ ┗━┛┻  ┻┻  ┻┻━┛ ┗━┛ ┻┗┛ ┻━┛ ┻┗┛ ┛\n");
			Sleep(wait_time);
			break;

		}
		Sleep(wait_time);// 1秒間(wait_timeミリ秒)何もしない
		system("cls");// 画面消去
	}
	return 0;
}


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

Re: ドットイートゲーム 敵の動きについて教えてください。

#2

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

けいすけ さんが書きました:敵が消滅してしまう(マップの座標外にでてしまう?)
とりあえずざっと読んですぐに気づくこととしては、
キーを1回も押していない状態で敵が壁に入ると、未初期化の自動変数であるため値が不定であるmx, myの値がex, eyに代入され、
その値が後で計算に使用されるので、未定義動作になりそうですね。

敵機の座標を保存するのは、入力に関係なく敵機が動こうとする直前(このコードでは111行目の前)にするべきでしょう。
オフトピック
「マップの座標外にでてしまう?」という仮説を立てたら、デバッガを使う、printf()で出力する、assert()で条件のチェックをする、などの方法で検証してみるといいでしょう。
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

けいすけ

Re: ドットイートゲーム 敵の動きについて教えてください。

#3

投稿記事 by けいすけ » 8年前

みけCAT さんが書きました:
けいすけ さんが書きました:敵が消滅してしまう(マップの座標外にでてしまう?)
敵機の座標を保存するのは、入力に関係なく敵機が動こうとする直前(このコードでは111行目の前)にするべきでしょう。
オフトピック
「マップの座標外にでてしまう?」という仮説を立てたら、デバッガを使う、printf()で出力する、assert()で条件のチェックをする、などの方法で検証してみるといいでしょう。
おっしゃったとおり、敵機が動こうとする直前(111行の前)で敵機の座標を代入するように次のように改善しました。

コード:

//自機に向かう (敵機の動き)
		mx = ex; my = ey;//敵機の座標を保存する

		if (ex > cx)
			ex--;
		else if (ex < cx)
			ex++;
		else if (ey > cy)
			ey--;
		else if (ey < cy)
			ey++;
		
		if (field[ey][ex] == WALL)  //もし敵機が壁に当たったらもとの座標に戻す。
		{
			ex = mx; ey = my;
		}
		ex = (ex + SIZE) % SIZE; //ワープ

		if (cx == ex && cy == ey) {
			life--;
			ex = 7, ey = 7;
			Sleep(wait_time);
		}
すると、敵がいきなり消滅するというのはなくなりました。
しかし新たに、
・敵が急に止まってしまう 
という問題点を発見しました。素人の私なりに考えたのですが、自機に近づこうとするときに壁に当たってしまって、当たり判定があるからその場所からうごけないのかなと思いました。違う原因なのかもしれませんが、どうすれば敵を常時動かす(自機に向かって)ことができるのでしょうか。

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

Re: Re: ドットイートゲーム 敵の動きについて教えてください。

#4

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

例えば、「動ける(壁でない)方向のうち、移動後の距離が一番自機に近い方向に移動する」というアルゴリズムにするのはどうでしょうか?
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

けいすけ

Re: Re: ドットイートゲーム 敵の動きについて教えてください。

#5

投稿記事 by けいすけ » 8年前

みけCAT さんが書きました:例えば、「動ける(壁でない)方向のうち、移動後の距離が一番自機に近い方向に移動する」というアルゴリズムにするのはどうでしょうか?
すいません。どういうコードを書いていいか全くわからないので、ヒントだけでもいただけないでしょうか。

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

Re: Re: ドットイートゲーム 敵の動きについて教えてください。

#6

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

このようなアルゴリズムにするとよさそうだと思います。

コード:

// 動ける(壁でない)座標の集合を求める
移動先候補集合 ← {}
for 次の座標 in 壁がなければ動ける各移動先
  if 次の座標が壁でない
    移動先候補集合 ← 移動先候補集合 ∪ {次の座標}
  endif
next

// その中から一番自機に近いものを選ぶ
最短移動先 ← {}
最短移動先の自機との距離 ← ∞
for 移動先候補座標 in 移動先候補集合
  この候補の距離 ← 移動先候補座標と自機の位置との距離
  if この候補の距離 < 最短移動先の自機との距離
    最短移動先 ← {移動先候補座標}
    最短移動先の自機との距離 ← この候補の距離
  else if この候補の距離 == 最短移動先の自機との距離
    最短移動先 ← 最短移動先 ∪ {移動先候補座標}
  endif
next

// 最終的に移動先を決定する
移動先 ← 最短移動先の中のランダムな要素
座標同士の距離は、適当にやるならユークリッド距離やマンハッタン距離、
きちんとやるならワーシャル・フロイド法や幅優先探索で求めた最も少ない手数で移動する時の手数を使うといいでしょう。
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

アバター
usao
記事: 1889
登録日時: 12年前
連絡を取る:

Re: ドットイートゲーム 敵の動きについて教えてください。

#7

投稿記事 by usao » 8年前

オフトピック
まともな答えではないかもしれませんが…

>自機に近づこうとするときに壁に当たってしまって

が問題なのであれば,
「(それほど積極的には)自機に近づこうとしなければ」問題自体が無くなる(or簡単になる),という考え方は有りでしょうか?

たとえば,
道が一本道なうちはただただ道なりに進み,
進める先が二股以上に分かれ場所に来たときにだけ,
何かしら適当に簡単な「敵と自機との位置関係を使った判定」を行ってどちらに進むかを決める,とか.

けいすけ

Re: ドットイートゲーム 敵の動きについて教えてください。

#8

投稿記事 by けいすけ » 8年前

usao さんが書きました:
オフトピック
まともな答えではないかもしれませんが…

>自機に近づこうとするときに壁に当たってしまって

が問題なのであれば,
「(それほど積極的には)自機に近づこうとしなければ」問題自体が無くなる(or簡単になる),という考え方は有りでしょうか?

たとえば,
道が一本道なうちはただただ道なりに進み,
進める先が二股以上に分かれ場所に来たときにだけ,
何かしら適当に簡単な「敵と自機との位置関係を使った判定」を行ってどちらに進むかを決める,とか.
確かに、そのアイデアもありますね!!とても参考になります。
ありがとうございます。
ちなみにどういったコードになりますでしょうか?

閉鎖

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