ueoai533
@ueoai533

Are you sure you want to delete the question?

Leaving a resolved question undeleted may help others!

C言語 ゲームでのマップ切り替え

Q&A

Closed

解決したいこと

C言語で軽いゲームのようなものを作っています。いわゆる迷路ゲームのようなものです。
そこでマップの切り替えを行いたいのですがうまくいきません。
例えばソースコード中の二次元配列のmap[][100]をmap2[][100]に書き換える処理を考えたのですが、うまいことできません。どうすればできるでしょうか。
具体的には、ソースコード222行目にあるように、'N'キーを押したら別のマップに推移するようにしたいです。

該当するソースコード

#include <stdio.h>

#include <ncurses.h>  // 今回の表示全般に必要

#include <stdlib.h>   // system()に必要

#include <locale.h>   // setlocale()による表示日本語化に必要



// map[][]の要素やCOLOR_PAIR()を参照するときに使うあだ名

#define HERO  1

#define EXIT  2

#define WALL  3

#define ROAD  4

#define HAKO  5

#define KAGI  6

#define MESG 10



// ヒーローの上下左右のマスを参照するときのあだ名

#define HERO_UPPER  map[hero_y - 1][hero_x]  // ヒーローの1個上のマス

#define HERO_UPPER2 map[hero_y - 2][hero_x]  // ヒーローの2個上のマス

#define HERO_LOWER  map[hero_y + 1][hero_x]  // ヒーローの1個下のマス

#define HERO_LOWER2 map[hero_y + 2][hero_x]  // ヒーローの2個下のマス

#define HERO_LEFT   map[hero_y][hero_x - 1]  // ヒーローの1個左のマス

#define HERO_LEFT2  map[hero_y][hero_x - 2]  // ヒーローの2個左のマス

#define HERO_RIGHT  map[hero_y][hero_x + 1]  // ヒーローの1個右のマス

#define HERO_RIGHT2 map[hero_y][hero_x + 2]  // ヒーローの2個右のマス



// ゲーム中の状態

#define LOCKED   1  // 未回収の鍵があり出口が閉じている状態

#define UNLOCKED 2  // 鍵を全部回収して出口が開いた状態

#define FINISHED 3  // 出口から脱出してゲームをクリアした状態

#define GAMEOVER 4



// getch()でのキー入力の待ち時間(単位:ms)

// 永久ループのインターバルを兼ねている

#define GETCH_WAIT 100



// このゲームの配色を設定する

void setup_my_colors(void)

{

	// 使える色名はCOLOR_{BLACK|RED|GREEN|YELLOW|BLUE|MAGENTA|CYAN|WHITE}

	init_color(COLOR_BLACK,     0,    0,    0);  //RGB成分を0〜1000で指定

	init_color(COLOR_WHITE,  1000, 1000, 1000);

	init_color(COLOR_CYAN,    200,  800, 1000);

	init_color(COLOR_GREEN,   200, 1000,  300);

	init_color(COLOR_BLUE,    200,  300,  450);

	init_color(COLOR_YELLOW, 1000, 1000,  200);



	init_pair(HERO, COLOR_CYAN,   COLOR_BLACK);

	init_pair(EXIT, COLOR_GREEN,  COLOR_BLACK);

	init_pair(WALL, COLOR_BLUE,   COLOR_BLUE);

	init_pair(ROAD, COLOR_BLACK,  COLOR_BLACK);

	init_pair(HAKO, COLOR_YELLOW, COLOR_BLACK);

	init_pair(KAGI, COLOR_WHITE,  COLOR_BLACK);

	init_pair(MESG, COLOR_WHITE,  COLOR_BLACK);

}



void put_mesg_count(int y, int x, int n)

{

  attrset(COLOR_PAIR(MESG));

  mvprintw(y, x * 2, "残り歩数:%d", n);

}

// ヒーロー「大」を指定の座標に表示する関数(👻🐘🐧等で代替可)

void put_hero(int y, int x)

{

	attrset(COLOR_PAIR(HERO)); mvaddstr(y, x * 2, "大");

}



// 出口「且」を指定の座標に表示する関数(🚪等で代替可)

void put_exit(int y, int x)

{

	attrset(COLOR_PAIR(EXIT)); mvaddstr(y, x * 2, "且");

}



// 壁を指定の座標に表示する関数(全角空白、⛰等で代替可)

void put_wall(int y, int x)

{

	attrset(COLOR_PAIR(WALL)); mvaddstr(y, x * 2, " ");

}



// 道を指定の座標に表示する関数(全角空白)

void put_road(int y, int x)

{

	attrset(COLOR_PAIR(ROAD)); mvaddstr(y, x * 2, " ");

}



// 箱「田」を指定の座標に表示する関数(📦🗿💩等で代替可)

void put_hako(int y, int x)

{

	attrset(COLOR_PAIR(HAKO)); mvaddstr(y, x * 2, "田");

}



// 鍵「¶」を指定の座標に表示する関数(🔑🗝💍等で代替可)

void put_kagi(int y, int x)

{

	attrset(COLOR_PAIR(KAGI)); mvaddstr(y, x * 2, "¶");

}



// 一般的なメッセージを指定の座標に表示する関数

void put_mesg_main(int y, int x, char s[])

{

	attrset(COLOR_PAIR(MESG)); mvaddstr(y, x * 2, s);

}



// 未回収の鍵の個数を指定の座標に表示する関数

void put_mesg_kagi(int y, int x, int n)

{

	attrset(COLOR_PAIR(MESG));

	mvprintw(y, x * 2, "Uncollected ¶ × %d", n);

	// 🔑🗝💍等を使う場合はここの絵文字↑もそれに合わせること

}



int main(void)

{

	// マップ1 ここから ------------------------------

	int map_size_y = 13;  // マップの縦幅

	int map_size_x = 16;  // マップの横幅

	int hero_y = 5;       // ヒーローのy座標の初期値

	int hero_x = 13;      // ヒーローのx座標の初期値

	int uncollected_keys = 4;  // 拾わなくてはならない鍵の個数

	int count = 70;



	// 2:出口、3:壁、4:道、5:箱、6:鍵

	int map[][100] = {  // とりあえずガバッっと大きめに横100で確保してある

		{ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4 },

		{ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4 },

		{ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4 },

		{ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4 },

		{ 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4 },

		{ 4, 3, 6, 4, 4, 3, 3, 4, 3, 4, 4, 6, 3, 4, 3, 4 },

		{ 4, 3, 3, 5, 4, 4, 4, 4, 4, 5, 4, 4, 5, 5, 3, 4 },

		{ 4, 3, 6, 4, 3, 5, 4, 5, 4, 3, 5, 4, 5, 4, 3, 4 },

		{ 4, 3, 3, 4, 4, 5, 4, 5, 4, 3, 4, 5, 3, 4, 3, 4 },

		{ 4, 3, 2, 5, 4, 4, 3, 4, 5, 4, 4, 5, 4, 4, 3, 4 },

		{ 4, 3, 3, 4, 4, 4, 4, 5, 4, 6, 3, 3, 5, 4, 3, 4 },

		{ 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4 },

		{ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4 },

	};

	// マップ1 ここまで ------------------------------





	// マップ2 ここから ------------------------------

	int map_size_y = 16;  // マップの縦幅

	int map_size_x = 19;  // マップの横幅

	int hero_y = 8;       // ヒーローのy座標の初期値

	int hero_x = 6;       // ヒーローのx座標の初期値

	int uncollected_keys = 9;  // 拾わなくてはならない鍵の個数



	// 2:出口、3:壁、4:道、5:箱、6:鍵

	int map[][100] = {  // とりあえずガバッっと大きめに横100で確保してある

		{ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4 },

		{ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4 },

		{ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4 },

		{ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4 },

		{ 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4 },

		{ 4, 3, 4, 5, 4, 4, 4, 4, 3, 3, 3, 3, 4, 5, 4, 3, 4 },

		{ 4, 3, 4, 5, 3, 3, 4, 4, 4, 4, 5, 4, 4, 4, 6, 3, 4 },

		{ 4, 3, 6, 4, 3, 6, 5, 5, 5, 6, 3, 3, 4, 5, 4, 3, 4 },

		{ 4, 3, 5, 3, 4, 3, 4, 2, 5, 3, 3, 6, 3, 3, 5, 3, 4 },

		{ 4, 3, 4, 4, 4, 3, 5, 4, 4, 5, 4, 5, 4, 4, 6, 3, 4 },

		{ 4, 3, 6, 3, 5, 5, 5, 3, 4, 3, 4, 3, 4, 3, 3, 3, 4 },

		{ 4, 3, 3, 6, 4, 5, 4, 4, 4, 3, 4, 3, 6, 3, 3, 3, 4 },

		{ 4, 3, 3, 4, 3, 4, 3, 3, 3, 3, 4, 4, 5, 4, 3, 3, 4 },

		{ 4, 3, 4, 4, 5, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 4 },

		{ 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4 },

		{ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4 },

	};

	// マップ2 ここまで ------------------------------

***/



/***

	// マップ3 ここから ------------------------------

	int map_size_y = 18;  // マップの縦幅

	int map_size_x = 20;  // マップの横幅

	int hero_y = 15;      // ヒーローのy座標の初期値

	int hero_x = 2;       // ヒーローのx座標の初期値

	int uncollected_keys = 9;  // 拾わなくてはならない鍵の個数



	// 2:出口、3:壁、4:道、5:箱、6:鍵

	int map[][100] = {  // とりあえずガバッっと大きめに横100で確保してある

		{ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4 },

		{ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4 },

		{ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4 },

		{ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4 },

		{ 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4 },

		{ 4, 3, 4, 4, 4, 4, 4, 3, 3, 3, 3, 6, 3, 4, 4, 4, 4, 4, 3, 4 },

		{ 4, 3, 4, 3, 3, 3, 4, 3, 6, 4, 5, 4, 6, 4, 4, 3, 3, 4, 3, 4 },

		{ 4, 3, 4, 3, 3, 3, 4, 3, 4, 3, 4, 3, 6, 3, 3, 3, 4, 4, 3, 4 },

		{ 4, 3, 4, 3, 4, 4, 4, 3, 4, 3, 4, 3, 4, 4, 4, 4, 4, 4, 3, 4 },

		{ 4, 3, 4, 3, 5, 5, 5, 4, 4, 3, 4, 3, 5, 3, 3, 5, 5, 5, 3, 4 },

		{ 4, 3, 2, 3, 4, 6, 4, 3, 3, 3, 4, 4, 5, 6, 3, 4, 6, 4, 3, 4 },

		{ 4, 3, 4, 3, 5, 5, 5, 4, 4, 3, 3, 4, 3, 4, 4, 5, 5, 5, 3, 4 },

		{ 4, 3, 4, 3, 4, 4, 4, 3, 4, 3, 4, 4, 3, 3, 3, 4, 4, 4, 3, 4 },

		{ 4, 3, 4, 3, 3, 3, 4, 3, 4, 3, 4, 3, 3, 3, 3, 3, 3, 4, 3, 4 },

		{ 4, 3, 4, 3, 3, 3, 4, 3, 6, 4, 4, 5, 4, 4, 4, 3, 3, 4, 3, 4 },

		{ 4, 3, 4, 4, 4, 4, 4, 3, 3, 3, 3, 6, 3, 4, 4, 4, 4, 4, 3, 4 },

		{ 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4 },

		{ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4 },

	};

	// マップ3 ここまで ------------------------------

***/



	int state = LOCKED;  // 状態(LOCKED, UNLOCKED, FINISHEDのいずれか)

	int ch, y, x;



	setlocale(LC_ALL,"");  // 非ASCII文字(日本語や絵文字)の表示に必要

	initscr();             // ncursesを使用する際、最初におこなう初期化

	start_color();         // カラー表示を開始する

	setup_my_colors();     // このゲームの配色を設定

	curs_set(0);           // カーソルを非表示にする

	noecho();              // キー入力を非表示にする

	cbreak();              // Enterキー無しでキー入力を受け取る

	keypad(stdscr, TRUE);  // カーソルキーを有効にする

	                       //(上・下・左・右: KEY_{UP|DOWN|LEFT|RIGHT})

	timeout(GETCH_WAIT);   // getch()で読み取るキー入力の待ち時間(単位:ms)



	// キーのオートリピートを止める(ゲーム終了時に元に戻すこと)

	system("/usr/bin/xset r off");



	while (1)

	{

		ch = getch();  // キー入力を読み取る(Enter不要)



		if ((ch == 'q') || (ch == 'Q')) { break; }  // while(1)から抜ける

		if ((ch == 'n') || (ch == 'N')) 

		{  }

		

		if (state != FINISHED)  // ゲームがまだクリアされていない状態ならば

		{

			if (ch == KEY_UP)

			{

				switch (HERO_UPPER)

				{

					case ROAD:

						hero_y--;

						count--;

						break;

					case EXIT:

						hero_y--;

						count--;

						if (uncollected_keys == 0) { state = FINISHED; }  // クリア

						break;

					case HAKO:

						if (HERO_UPPER2 == ROAD)

						{

							HERO_UPPER2 = HAKO;

							HERO_UPPER = ROAD;

							hero_y--;

							count--;

						}

						break;

					case KAGI:

						HERO_UPPER = ROAD;

						hero_y--;

						count--;

						uncollected_keys--;

						if (uncollected_keys == 0) { state = UNLOCKED; }  // 出口開く

						break;

				}  // switch (HERO_UPPER) ここまで

			}  // if (ch == KEY_UP) ここまで

			else if (ch == KEY_DOWN)

			{

				switch (HERO_LOWER)

				{

					case ROAD:

						hero_y++;

						count--;

						break;

					case EXIT:

						hero_y++;

						count--;

						if (uncollected_keys == 0) { state = FINISHED; }  // クリア

						break;

					case HAKO:

						if (HERO_LOWER2 == ROAD)

						{

							HERO_LOWER2 = HAKO;

							HERO_LOWER = ROAD;

							hero_y++;

							count--;

						}

						break;

					case KAGI:

						HERO_LOWER = ROAD;

						hero_y++;

						count--;

						uncollected_keys--;

						if (uncollected_keys == 0) { state = UNLOCKED; }  // 出口開く

						break;

				}  // switch (HERO_LOWER) ここまで

			}  // else if (ch == KEY_DOWN) ここまで

			else if (ch == KEY_LEFT)

			{

				switch (HERO_LEFT)

				{

					case ROAD:

						hero_x--;

						count--;

						break;

					case EXIT:

						hero_x--;

						count--;

						if (uncollected_keys == 0) { state = FINISHED; }  // クリア

						break;

					case HAKO:

						if (HERO_LEFT2 == ROAD)

						{

							HERO_LEFT2 = HAKO;

							HERO_LEFT = ROAD;

							hero_x--;

							count--;

						}

						break;

					case KAGI:

						HERO_LEFT = ROAD;

						hero_x--;

						count--;

						uncollected_keys--;

						if (uncollected_keys == 0) { state = UNLOCKED; }  // 出口開く

						break;

				}  // switch (HERO_LEFT) ここまで

			}  // else if (ch == KEY_LEFT) ここまで

			else if (ch == KEY_RIGHT)

			{

				switch (HERO_RIGHT)

				{

					case ROAD:

						hero_x++;

						count--;

						break;

					case EXIT:

						hero_x++;

						count--;

						if (uncollected_keys == 0) { state = FINISHED; }  // クリア

						break;

					case HAKO:

						if (HERO_RIGHT2 == ROAD)

						{

							HERO_RIGHT2 = HAKO;

							HERO_RIGHT = ROAD;

							hero_x++;

							count--;

						}

						break;

					case KAGI:

						HERO_RIGHT = ROAD;

						hero_x++;

						count--;

						uncollected_keys--;

						if (uncollected_keys == 0) { state = UNLOCKED; }  // 出口開く

						break;

				}  // switch (HERO_RIGHT) ここまで

			}  // else if (ch == KEY_RIGHT) ここまで

		}  // if (state != FINISHED) ここまで



		// 表示のための処理開始

		clear();  // 画面を消去する

		          // 画面がちらつくようならerase();に変更してみる



		for (y = 0; y < map_size_y; y++)

		{

			for (x = 0; x < map_size_x; x++)

			{

				switch (map[y][x])

				{

					case WALL:

						put_wall(y, x);

						break;

					case ROAD:

						put_road(y, x);

						break;

					case HAKO:

						put_hako(y, x);

						break;

					case KAGI:

						put_kagi(y, x);

						break;

					case EXIT:

						put_exit(y, x);

						break;

				}  // switch (map[y][x]) ここまで

			}  // for (x = 0;〜) ここまで

		}  // for (y = 0;〜) ここまで



		put_hero(hero_y, hero_x);

		if(count <= 0)

		{

			count = 0;

		}

		put_mesg_count(2, 1, count);



		switch (state)

		{

			case LOCKED:  // まだ未回収の鍵があって出口が閉じている状態

				put_mesg_main(0, 1, "Pick up all key and go to the exit!");

				break;

			case UNLOCKED:  // 鍵を全部回収して出口が開いた状態

				put_mesg_main(0, 1, "Hurry to the exit!");

				break;

			case FINISHED:  // 出口から脱出してゲームをクリアした状態

				put_mesg_main(0, 1, "Finished!! Quit \"q\"");

				break;

			case GAMEOVER:

        		put_mesg_main(0, 1, "Gameover!! Quit \"q\"");

        		break;

		}  // switch (state) ここまで

		

		if(count == 0)

		{

			state = GAMEOVER;

		}



		put_mesg_kagi(1, 1, uncollected_keys);



		refresh();  // このrefresh()によって実際に画面が再表示される

		// 表示のための処理終わり



	}  // while(1)の永久ループはここまで





	// while(1)の永久ループから抜けた後のゲーム終了の処理

	endwin();  // ncurserを使った時の後始末

	system("/usr/bin/xset r rate 500 33");  // キーのオートリピートを初期値に戻す



	return 0;

}

### 自分で試したこと
           
if ((ch == 'n') || (ch == 'N')) 
{  
        int map_size_y = 16;  // マップの縦幅
		int map_size_x = 19;  // マップの横幅
		int hero_y = 8;       // ヒーローのy座標の初期値
		int hero_x = 6;       // ヒーローのx座標の初期値
		int uncollected_keys = 9;  // 拾わなくてはならない鍵の個
        int i;
		for(i = 0; i < sizeof(map2) / sizeof(map2[0][0]); i++)
	{
		map[i][i] = map2[i][i];
	}
}
のようにしましたが実行した際に「segmantation fault(コアダンプ)」と表示されてしまいました。
0

3Answer

すべてのマップの横のサイズを固定値100で確保しているのであれば、

	int map1[][100] = {
		// ...
	};

	int map2[][100] = {
		// ...
	};

	int (*map)[100] = map1;

と書いておいて、切り替えたくなったら

	if ((ch == 'n') || (ch == 'N')) 
	{  
		map_size_y = 16;  // マップの縦幅
		map_size_x = 19;  // マップの横幅
		hero_y = 8;       // ヒーローのy座標の初期値
		hero_x = 6;       // ヒーローのx座標の初期値
		uncollected_keys = 9;  // 拾わなくてはならない鍵の個
		map = map2;
	}

のように書く、という方法もあります。

1Like

Comments

  1. @ueoai533

    Questioner

    本当にありがとうございました。どこがおかしいのかを説明してくださったおかげで、ちゃんと理解しながらプログラミングを組めました。

memcpyを使ってみました。

コアダンプの原因は、範囲外アクセスでしょう。落ち着いて考えていただければわかると思います。

0Like
int map[][100] = {
    {...},
    ...
    {...}
}

の部分は縦18、横100マスのフィールドを生成していますよね。
つまりmap[0][0], map[0][1]……, map[0][99],…………, map[17][99]の1800個です。

ここで、C言語はmapの縦横のサイズを覚えていません。mapと言う変数に割り当てられている総メモリ数(1800×sizeof(int))は記憶していますが、それが18×100×sizeof(int)なのか36×50×sizeof(int)なのかは保持されていません。

つまり、sizeof(map2) / sizeof(map2[0][0])は1800になります。18にはなりませんので注意しましょう。

その上でこのコードを見てみましょう。

for(i = 0; i < sizeof(map2) / sizeof(map2[0][0]); i++){
	map[i][i] = map2[i][i];
}

これだと、map[0][0]=map[0][0]、map[1][1]=map[1][1]、中略、map[1799][1799]=map[1799][1799]となります。当然、望んでいる結果にはなりません。

ではどう書くべきかと言うと、「sizeofは使わず、完全に定数にする」しか方法は無いと思います。

#define MAP_MAX_WIDTH 100
#define MAP_MAX_HEIGHT 100

中略

int map[MAP_MAX_HEIGHT][MAP_MAX_WIDTH] = {
    {...},
    ...
    {...}
}

中略

int y, x;
for(y = 0; y < MAP_MAX_HEIGHT; y++){
    for(x = 0; x < MAP_MAX_HEIGHT; x++){
	    map[y][x] = map2[y][x];
    }
}
0Like

Comments

  1. sizeof(map2) / sizeof(map2[0]) が 18 になり、
    sizeof(map2[0]) / sizeof(map2[0][0]) が 100 になるので、
    「sizeofは使わず、完全に定数にする」以外の方法もないことはありません。

Your answer might help someone💌