第15章 マップチップを使った背景表示

15-2 マップデータをファイルで管理する

マップデータを配列の初期値として管理するのは非常に効率が悪い。

static int MapData[MAP_HEIGHT][MAP_WIDTH] = {	// マップデータ
    02, 02, 02, 02, 00, 00, 00, 00, 00, 00, 00, 02, 02, 02, 02, 02, 02, 02, 02, 02,
    02, 00, 00, 00, 00, 00, 00, 21, 02, 00, 00, 02, 02, 02, 02, 02, 02, 02, 02, 02,
    ・
    ・
    ・
    02, 02, 02, 02, 02, 02, 02, 02, 02, 02, 02, 02, 02, 02, 02, 00, 02, 02, 02, 02
};

※マップデータを修正するには、プログラムファイルを開かなければならないため、面倒!!

ここでは、マップデータをテキストファイルで管理し、配列に読み込む方法を考える。

1. 管理しやすいデータの持ち方を考える

一番簡単なデータの入力方法は次のようなプログラムだろう。

while (fscanf(fp, "%d", &data) != EOF)
{
    --- dataを配列に格納 ---
}

しかしこれでは、データ1件ごとに改行しなければならず、テキストファイルを見ると何が何だか分からなくなってしまう。

02
02
02
02
00
00
00
00
00
00
00
02
02
02
02
02
02
02
02
02 ←ここまでで横1列分のデータ

では、テキストデータ1行で20件(横1列分)と決めて、1回の読み込みで1行分読み込むとどうなるだろうか。

while (
    fscanf(fp, "%d,%d,・・・,%d"
        &data[0],amp;data[1],・・・,data[19]) != EOF)
{
    --- dataを配列に格納 ---
}

これならテキストデータは見やすくなるが、今度はプログラムが気持ち悪い。しかも、もっと大きなマップデータを扱う場合、現実的とは言いがたい。では、どのようにデータを管理し、プログラムで読み込めばいいのだろうか?

いろいろな考え方があると思うが、どのようなマップデータでも対応できるように考えると、次の方法を提案する。

  1. マップデータをテキストファイルで作成する。これはユーザが目で見て分かりやすいように1行分のデータをカンマ区切りで持つ。
  2. 1で作成したデータを、プログラムで読み込みやすい形式に変換し、別ファイルに出力する。
  3. 2で作成したデータを使ってゲームプログラムから読み込む。

2の処理を行うプログラムを作成しなければならないが、いろんな場面で使えるため、非常に便利である。

2. データ変換プログラムの例

マップデータは横20列、縦15行とし、カンマ区切りで作成したとする。このデータを読み込み、1データ1行に変換するプログラムは次のようになる(Console Application)。

/*
	マップデータを読み込み、ゲームデータ用のファイルを作成する
*/
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>

void main(void)
{
    char InputFileName[] = "MapData.txt";
    char OutputFileName[] = "MapData2.txt";
    FILE *fp1, *fp2;
    char data[4];
    int c, i;

    if ((fp1 = fopen(InputFileName, "r")) == NULL)
    {
        printf("入力ファイル「%s」のオープンに失敗しました。処理を中止します。\n", InputFileName);
        exit(1);
    }
    fp2 = fopen(OutputFileName, "w");

    i = 0;
    while ((c = getc(fp1)) != EOF)
    {
        if (isdigit(c))
        {
            data[i] = (char)c;
            i++;
        }
        else
        {
            data[i] = '\0';
            fprintf(fp2, "%d\n", atoi(data));
            printf("%d\n", atoi(data));	// 画面確認用
            i = 0;
        }
    }

    fclose(fp2);
    fclose(fp1);

}

※数字以外の文字が来たら、それまでの文字が1件のデータと考え、数値に変換して出力する

このプログラムは入力データファイル名を「MapData.txt」、出力データファイル名を「MapData2.txt」と固定している。入力データファイルをダウンロードし、プロジェクト内に配置して実行すると、出力デーファイルが作成される。

3. 作成したデーファイルをゲームプログラムから読み込む

2で作成したデータファイルを読み込み、マップチップデータとして利用するよう、第15章1で作成したプログラムを修正する。
複数のマップを扱うことも考え、ゲーム開始前初期化処理で読み込むことにする。

まず、マップデータ格納要配列の初期化がいらなくなる。

static int MapData[MAP_HEIGHT][MAP_WIDTH]; // マップデータ

次に、データファイルからデータを読み込み、マップデータ用配列に格納する関数を用意する。

//-----------------------------------------------------------------------------
// 関数名 : MapDataRead()
// 機能概要: マップデータの読み込み
//-----------------------------------------------------------------------------
static bool MapDataRead(void)
{
    FILE *fp;
    char FileName[] = "MapData2.txt";
    int x, y;

    if ((fp = fopen(FileName, "r")) == NULL)
    {
        OutputDebugString("MapData Read Error\n");
        return false;
    }

    for (y=0 ; y<MAP_HEIGHT ; y++)
    {
        for (x=0 ; x<MAP_WIDTH ; x++)
            fscanf(fp, "%d", &MapData[y][x]);
    }

    fclose(fp);
    return true;

}

※もちろん、プロトタイプ宣言を忘れてはいけない。

この関数は、ゲーム開始時初期化処理で次のように呼び出すのがよいだろう。

//-----------------------------------------------------------------------------
// 関数名 : GameInit()
// 機能概要: スタート処理初期化
//-----------------------------------------------------------------------------
void GameInit(HWND hWnd)
{
    int x, y;

    //------------------------------------------------------- 各変数の初期化
    MapDataRead(); /* マップデータを読み込み、配列にセットする */

    /* マップデータを元に、表示する矩形をセットする */
    for (y=0 ; y<MAP_HEIGHT ; y++)
    {
        for (x=0 ; x<MAP_WIDTH ; x++)
        {
            rcMapChip[y][x].left = (MapData[y][x] % MAP_WIDTH) * 32;
            rcMapChip[y][x].top = (MapData[y][x] / MAP_WIDTH) * 32;
            rcMapChip[y][x].right = rcMapChip[y][x].left + 32;
            rcMapChip[y][x].bottom = rcMapChip[y][x].top + 32;
        }
    }
    /* 自キャラ(カーソルキーで移動) */
    StatusInit(&MyChara, 0, 0, 40, 40, 310, 230, 4, 4);
    /* ゴキブリ(動かない) */
    StatusInit(&Goki, 64, 394, 96, 426, 400, 300, 0, 0);

    //------------------------------------------------------- フレームナンバーセット
    g_FrameNo = GAME_FRAME;

}

15章2練習問題(必須問題)

第15章1で作成したプログラムに上記修正を行い、マップデータをテキストファイルから読み込めるようにしなさい。


[ TOP ]