★関数(再帰呼び出し8)★


今回もゲームの一部を作ってみましょう。

今回の題材はシミュレーションゲームです。

「ファイアーエムブレム」シリーズなどをイメージしてもらえれば良いですね。

※最近のシリーズはプレイしていませんので、どうなっているのか分かりませんが・・・

で、どこを再現するのかというと「ユニットの移動範囲」を調べる、というところです。


例えば、マップが↓のようにあったとします。

□□□□□□□□□□□□□
□□□□□□□□□□□□□
□□□□□□□□□□□□□
□□□□□□□□□□□□□
□□□□□□□□□□□□□
□□□□□□□□□□□□□
□□□□□□□□□□□□□
□□□□□□□□□□□□□
□□□□□□□□□□□□□
□□□□□□□□□□□□□
□□□□□□□□□□□□□
□□□□□□□□□□□□□
□□□□□□□□□□□□□

プレイヤーユニットが真ん中にいるとします。

□□□□□□□□□□□□□
□□□□□□□□□□□□□
□□□□□□□□□□□□□
□□□□□□□□□□□□□
□□□□□□□□□□□□□
□□□□□□□□□□□□□
□□□□□□P□□□□□□
□□□□□□□□□□□□□
□□□□□□□□□□□□□
□□□□□□□□□□□□□
□□□□□□□□□□□□□
□□□□□□□□□□□□□
□□□□□□□□□□□□□

このユニットの移動量(歩数)は5(歩)だとすると移動範囲は、

□□□□□□□□□□□□□
□□□□□□・□□□□□□
□□□□□・・・□□□□□
□□□□・・・・・□□□□
□□□・・・・・・・□□□
□□・・・・・・・・・□□
□・・・・・P・・・・・□
□□・・・・・・・・・□□
□□□・・・・・・・□□□
□□□□・・・・・□□□□
□□□□□・・・□□□□□
□□□□□□・□□□□□□
□□□□□□□□□□□□□

このようになります。


さらに地形によって、消費移動量が変わったり、通行不可な地形があったりします。

2Dグラフィックスが使えないので、地形を↓のように設定します。

記号移動量説明
草原
砂地
岩場
×通行不可

これを元にマップを設定してみます。

■■■■■□□□□□□□□
■■■■□□□□□□□□□
■■□□□□□□□□△□□
■□□□□□□□□□□□□
□□□□□□□□□□□□□
□□□□□□□□□□□□□
□□□□□□□□□□□□□
□□□□□□□□□□□□□
□□□□□□□□□□□□■
××××□□□□□■■■■
□□□□□□□□□■△△■
□□□×□□□□□□△■□
□□□×□□□□□△■■□

プレイヤーユニットの位置を↓のように設定すると、

■■■■■□□□□□□□□
■■■■□□□□□□□□□
■■□□□□□□□□△□□
■□□□□□□□□□□□□
□□□□□□□□□□□□□
□□□□□□□□□□□□□
□□□□□□□□□□□□□
□□□□□□□□□□□□□
□□□□□□□□□□□□■
××××□□□□□■■■■
□□□□□□P□□■△△■
□□□×□□□□□□△■□
□□□×□□□□□△■■□

移動範囲は↓になります。

■■■■■□□□□□□□□
■■■■□□□□□□□□□
■■□□□□□□□□△□□
■□□□□□□□□□□□□
□□□□□□□□□□□□□
□□□□□□・□□□□□□
□□□□□・・・□□□□□
□□□□・・・・・□□□□
□□□・・・・・・・□□■
××××・・・・・・■■■
□・・・・・P・・・△△■
□□・×・・・・・・△■□
□□□×・・・・・△■■□

まずは、定数やmain関数を作ります。

プレイヤーユニットの座標と移動量も定義しておきましょう。

<sample program 154-01>

#include <stdio.h>

#define ROW 13
#define COL 13

#define MAP_KIND 4

#define POSITION_X 6
#define POSITION_Y 6

#define STEP_MAX 5

enum {
    GRASS,          /* 草原 */
    SAND,           /* 砂地 */
    ROCK,           /* 岩場 */
    IMPASSABLE = 9, /* 通行不可 */
};

int main(void)
{
    int map[ROW][COL] = {
        { 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0 },
        { 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
        { 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0 },
        { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
        { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
        { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
        { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
        { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
        { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
        { 9, 9, 9, 9, 0, 0, 0, 0, 0, 1, 1, 1, 1 },
        { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 2, 1 },
        { 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 2, 1, 0 },
        { 0, 0, 0, 9, 0, 0, 0, 0, 0, 2, 1, 1, 0 },
    };

    return 0;
}

ここから関数を追加していきます。

前回同様、プログラム全体は最後に載せます。

1.マップを表示する

関数名 ShowMap
戻り値 なし
引 数 マップ
機 能 マップの表示
void ShowMap(const int map[ROW][COL], const int x, const int y)
{
    const char PLAYER[3] = "P";

    const char GRAPHIC[MAP_KIND][3] = { "□", "■", "△", "×" };

    int i;
    int j;

    for (i = 0; i < ROW; i++) {
        for (j = 0; j < COL; j++) {
            if (i == y && j == x) {
                printf(PLAYER);
            }
            else {
                if (map[i][j] < IMPASSABLE) {
                  printf("%s", GRAPHIC[map[i][j]]);
                }
                else {
                    printf("%s", GRAPHIC[MAP_KIND - 1]);
                }
            }
        }
        printf("\n");
    }

    printf("\n");
}

プレイヤーユニットを表示するため、プレイヤーユニットがいる座標の時はマップを表示しないようにしています。

2Dグラフィックスが使えれば、画像を重ねて表示出来るので、このようなプログラムにはなりません。

コンソールですので、文字の上に文字を重ねて表示出来ませんから面倒なことになってるのです。

また、「通行不可(IMPASSABLE)」のところもif文で分けています。

「通行不可」の番号は9ですから、そのままGRAPHIC配列に適用すると、配列の範囲を超えてしまいます。

「通行不可」の番号を3にすれば?と思うかも知れませんが、以下の理由で9にしました。

  今回の移動量である5よりも大きい番号にする必要があった

  地形の追加を考えて少し大きめの番号にしたい

で、9にしたのですが表示する時に調整する必要が出てきたということです。

コードを見ると、

printf("%s", GRAPHIC[MAP_KIND - 1]);

となっています。

printf("%s", GRAPHIC[3]);

でも良さそうですが、地形を追加する度にプログラムを書き換えなければなりません。

※仕様が確定してないことが問題なのですが・・・


main関数の追加点を書いておきます。

<sample program 154-02>

int main(void)
{
    int map[ROW][COL] = {
        { 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0 },
        { 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
        { 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0 },
        { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
        { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
        { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
        { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
        { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
        { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
        { 9, 9, 9, 9, 0, 0, 0, 0, 0, 1, 1, 1, 1 },
        { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 2, 1 },
        { 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 2, 1, 0 },
        { 0, 0, 0, 9, 0, 0, 0, 0, 0, 2, 1, 1, 0 },
    };

    ShowMap(map, POSITION_X, POSITION_Y);

    return 0;
}

<実行結果>

■■■■■□□□□□□□□
■■■■□□□□□□□□□
■■□□□□□□□□△□□
■□□□□□□□□□□□□
□□□□□□□□□□□□□
□□□□□□□□□□□□□
□□□□□□P□□□□□□
□□□□□□□□□□□□□
□□□□□□□□□□□□■
××××□□□□□■■■■
□□□□□□□□□■△△■
□□□×□□□□□□△■□
□□□×□□□□□△■■□

続行するには何かキーを押してください・・・

2.移動範囲の初期化

移動範囲を調べるために、マップと同じ大きさの配列を用意します。

その配列を初期化する関数を作りましょう。

関数名 ClearMoveArea
戻り値 なし
引 数 移動範囲を格納する配列
機 能 移動範囲の初期化
void ClearMoveArea(int moveArea[ROW][COL])
{
    int i;
    int j;

    for (i = 0; i < ROW; i++) {
        for (j = 0; j < COL; j++) {
            moveArea[i][j] = IMPASSABLE;
        }
    }
}

初期値は「通行不可(IMPASSABLE)」にします。

<sample program 154-03>

int main(void)
{
    int map[ROW][COL] = {
        { 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0 },
        { 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
        { 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0 },
        { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
        { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
        { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
        { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
        { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
        { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
        { 9, 9, 9, 9, 0, 0, 0, 0, 0, 1, 1, 1, 1 },
        { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 2, 1 },
        { 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 2, 1, 0 },
        { 0, 0, 0, 9, 0, 0, 0, 0, 0, 2, 1, 1, 0 },
    };

    int moveArea[ROW][COL];

    ShowMap(map, POSITION_X, POSITION_Y);

    ClearMoveArea(moveArea);

    return 0;
}

3.移動範囲を表示する

先に移動範囲の表示関数を作っておきます。

関数名 ShowMoveArea
戻り値 なし
引 数 移動範囲を格納する配列
機 能 移動範囲の表示
void ShowMoveArea(const int moveArea[ROW][COL])
{
    int i;
    int j;

    for (i = 0; i < ROW; i++) {
        for (j = 0; j < COL; j++) {
            printf("%2d", moveArea[i][j]);
        }
        printf("\n");
    }

    printf("\n");
}

単純に数値として表示するだけです。

<sample program 154-04>

int main(void)
{
    int map[ROW][COL] = {
        { 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0 },
        { 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
        { 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0 },
        { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
        { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
        { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
        { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
        { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
        { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
        { 9, 9, 9, 9, 0, 0, 0, 0, 0, 1, 1, 1, 1 },
        { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 2, 1 },
        { 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 2, 1, 0 },
        { 0, 0, 0, 9, 0, 0, 0, 0, 0, 2, 1, 1, 0 },
    };

    int moveArea[ROW][COL];

    ShowMap(map, POSITION_X, POSITION_Y);

    ClearMoveArea(moveArea);

    ShowMoveArea(moveArea);

    return 0;
}

<実行結果>

■■■■■□□□□□□□□
■■■■□□□□□□□□□
■■□□□□□□□□△□□
■□□□□□□□□□□□□
□□□□□□□□□□□□□
□□□□□□□□□□□□□
□□□□□□P□□□□□□
□□□□□□□□□□□□□
□□□□□□□□□□□□■
××××□□□□□■■■■
□□□□□□□□□■△△■
□□□×□□□□□□△■□
□□□×□□□□□△■■□

 9 9 9 9 9 9 9 9 9 9 9 9 9
 9 9 9 9 9 9 9 9 9 9 9 9 9
 9 9 9 9 9 9 9 9 9 9 9 9 9
 9 9 9 9 9 9 9 9 9 9 9 9 9
 9 9 9 9 9 9 9 9 9 9 9 9 9
 9 9 9 9 9 9 9 9 9 9 9 9 9
 9 9 9 9 9 9 9 9 9 9 9 9 9
 9 9 9 9 9 9 9 9 9 9 9 9 9
 9 9 9 9 9 9 9 9 9 9 9 9 9
 9 9 9 9 9 9 9 9 9 9 9 9 9
 9 9 9 9 9 9 9 9 9 9 9 9 9
 9 9 9 9 9 9 9 9 9 9 9 9 9
 9 9 9 9 9 9 9 9 9 9 9 9 9

続行するには何かキーを押してください・・・

2回に分けます。


次へ

戻る

目次へ