Skip to main content

第3回 まずコスコマンを動かしてみよう

第3回 まずコスコマンを動かしてみよう

前回は、Mapの数字を文字へ変えてステージを表示しました。

今回は、Mapの中にいるコスコマンを探して、まず実際に動かします。

ここでは、まだ壁や荷物の判定は作りません。

**「Mapを書き換えると、画面上の@も動いて見える」**ことを先に確認します。

今回の流れ

Mapからコスコマンの位置を探す
        ↓
押したキーから移動先を決める
        ↓
元のマスを床へ戻す
        ↓
移動先をコスコマンのマスにする
        ↓
Mapを描くと、@が移動して見える

1.位置をxとyでまとめよう

Mapの位置は、

x = 横
 y = 縦

の2つで表せます。

そこで、Mapの前へPoint構造体を作って座標を表します。
C言語ではこうやって2つ以上値があるもの(座標とか、キャラのパラメータとか)を構造体で表します。
(もう知ってるよね!)

struct Point
{
    int x;
    int y;
};

これで、

Point pos;

という1つの変数の中へ、xとyをまとめて持てます。

今回追加する関数

Pointを使えるようになったので、今回使う3つの関数を先に宣言します。

DrawStageText()の宣言の下へ、まとめて追加してください。

OBJNAMEの値をもう一度確認しよう

OBJNAMEは、Mapの1マスに何が入っているかを表す型です。

名前 値 意味
FLOOR 0 何もない床
WALL 1 壁
LUGG 2 荷物
GOAL 3 ゴール
HUMAN 4 コスコマン
HUMAN_ON_GOAL 5 ゴールに乗っているコスコマン
LUGG_ON_GOAL 6 ゴールに乗っている荷物
OUTSIDE 7 Mapの外側

SetObjtoMap()のobjには、この表にあるFLOORやHUMANなどを渡します。

Point GetPlayerPos(Map* map);
void SetObjtoMap(OBJNAME obj, Point pos, Map* map);
int IsKeyPressed(int key);

役割は次の通りです。

GetPlayerPos() → Mapからコスコマンの位置を探して、Point形で返す!
SetObjtoMap()  → 指定したMapの1マスを書き換える(書き換えられるのはマップだけ)
IsKeyPressed() → キーを押した瞬間だけ1を返す

2.Mapからコスコマンを探そう

このゲームでは、コスコマンの位置を別の変数へ常に保存しておくのではなく、コスコマン自身もMapの1マスとして持っています。 当然、そうじゃない考え方(マップとそこにあるオブジェクトやキャラクターを別管理)もありますが、今回は、いろいろ作業のしやすさから、すべてをマップ1つで管理という方法をとってみます(勉強のためって話もある)

床、壁、ゴール、荷物、コスコマンまでMapの数字で表すので、Mapを見るだけでその時点のゲーム状態が分かります。

そのため、移動するときはMap全体を調べて、現在のコスコマンの位置を探します。
マップ全探索して、コスコマンがいる場所('@'か'+'、数字なら4か5)を見つけたらそこの座標(配列の2次元インデックス)を返せばいいね。

穴埋め GetPlayerPos()を作ろう

DrawStageText()の下へ追加してください。

Point GetPlayerPos(Map* map)
{
    Point result = { -1, -1 };

    // ステージの上の行から下の行まで探す
    for (int y = 0; y < ____________________; y++)
    {
        // 1行を左端から右端まで探す
        for (int x = 0; x < ____________________; x++)
        {
            // 今調べているマスの数字を取り出す
            int obj = ________________________________;

            // コスコマンのマスを見つけたか調べる
            if (obj == ____________________)
            {
                // 見つけた横位置を結果へ保存する
                result.x = ____________________;

                // 見つけた縦位置を結果へ保存する
                result.y = ____________________;
                return result;
            }
        }
    }

    return result;
}

ヒント

Mapを表示するときと同じ二重ループを使えます。今回は@を表すマスを見つけたら、そのときのxとyを返します。

模範解答を見る
Point GetPlayerPos(Map* map)
{
    Point result = { -1, -1 };

    for (int y = 0; y < map->stage_height; y++)
    {
        for (int x = 0; x < map->stage_width; x++)
        {
            int obj = map->Dat[y][x];

            if (obj == HUMAN)
            {
                result.x = x;
                result.y = y;
                return result;
            }
        }
    }

    return result;
}

位置を表示して確認

ゲームループのDrawStageText()の下へ、一時的に次の2行を追加してみましょう。

Point playerPos = GetPlayerPos(&currentMap);
DrawFormatString(24, 560, GetColor(255, 255, 0), "PLAYER x=%d y=%d", playerPos.x, playerPos.y);

最初のステージでは、@の位置に対応した数字が表示されます。

デバッグ用なので確認できたら、この2行はいったん消すかコメント化してください。


3.Mapの1マスを書き換える関数を作ろう

移動とは、画面上の文字を直接動かすことではありません。

Mapの中身を、

移動前 移動後
元のマス:HUMAN → 元のマス:FLOOR
移動先:FLOOR → 移動先:HUMAN

のように書き換えます。
(@_→_@になるってことね)

Mapを書き換える小さな関数を作ります。 んで、以下のように使います。 例えば、

これが    こうなる移動
######     ######
#    #     #    #
#@   #  →  # @  #  
#    #   #    #
######     ######
移動前    移動後
Point PlayerPos; //現在のプレイヤーの位置 (移動前 (1,2))
playerPos.x = 1;
playerPos.y = 2;
Point nextPos; //次のプレイヤーの位置(移動後(2,2))
nextPos.x = 2;
nextPos.y = 2;
SetObjtoMap(HUMAN, nextPos, &currentMap); //2,2の座標をHUMAN(=4)に変更
SetObjtoMap(FLOOR, playerPos, &currentMap); //1,2の座標をFLOOR(=0)に変更 

これで、マップの上でプレイヤーの移動が表現できます。(元居た場所のプレイヤー消して、次の場所に移してるだけだよね)

先ほど宣言したSetObjtoMap()の中身を作ります。

穴埋め 指定したマスを書き換えよう

GetPlayerPos()の下へ追加してください。

void SetObjtoMap(OBJNAME obj, Point pos, Map* map)
{
    // 指定されたy行・x列のマスへ、新しいオブジェクト番号を入れる
    map->Dat[____________________][____________________] = ____________________;
}

ヒント

Point posにはxとyがあります。書き換える値は引数objで受け取っています。

模範解答を見る
void SetObjtoMap(OBJNAME obj, Point pos, Map* map)
{
    map->Dat[pos.y][pos.x] = obj;
}

4.キーを押した瞬間だけ反応させよう

ここは、マップとかプレイヤーじゃなく、ゲームのシステム側の話になります。
ゲームループは1秒間に何十回も繰り返されます。

CheckHitKey()だけで判定すると、ループ中何度も判定され、キーを少し長く押しただけで何マスも進むことがあります。

そこで、

前のフレーム:押していない
今のフレーム:押している

ときだけ1を返す関数を用意します。(押した瞬間をキャッチ)

判定の仕組み

CheckHitKey(key)の結果は、キーが押されていなければ0、押されていれば0以外になります。

currentには今の状態、previous[key]には1フレーム前の状態を入れて比べます。

前の状態 previous[key] 今の状態 current 判定結果 pressed 状態
0 0 0 押されていないまま
0 0以外 1 今、押した瞬間
0以外 0以外 0 押し続けている
0以外 0 0 キーを離した

1になるのは、今は押されている、そして前は押されていなかったという二つの条件が同時に成り立つときだけです。

処理は次の順番で組み立てます。

  1. CheckHitKey(key)で今の状態をcurrentへ入れる。
  2. 判定結果のpressedを、まず0にしておく。
  3. currentが0ではなく、previous[key]が0なら、pressedを1にする。
  4. 判定が終わってから、currentをprevious[key]へ保存する。
  5. pressedを返す。

4番を判定より先に行うと、前の状態が今の状態で上書きされてしまい、二つを比べられません。必ず判定のあとで保存します。

この流れに沿って、先ほど宣言したIsKeyPressed()の中身を作ります。

穴埋め 押した瞬間を調べよう

SetObjtoMap()の下へ追加してください。

int IsKeyPressed(int key)
{
    static int previous[256] = { 0 }; //前に押されたキーはここに保存されている

    // 今のフレームで、指定キーが押されているか調べる
    int current = ________________________________;

    // まず「押した瞬間ではない」として、結果を0にしておく
    int pressed = 0;

    // 条件1:今の状態currentが0ではない(今は押されている)
    // 条件2:前の状態previous[key]が0と等しい(前は押されていない)
    // 両方の条件が成り立ったときだけ、結果を1に変える
    if (____________________________ && ____________________________)
    {
        pressed = 1;
    }

    // 次のフレーム用に、今の状態を保存する
    previous[key] = ____________________;
    return pressed;
}

ヒント

今の状態はCheckHitKey(key)で調べます。「今は押されている」はcurrent != 0、「前は押されていない」はprevious[key] == 0です。最後の空欄には、次の判定で前の状態として使う値を入れます。

模範解答を見る
int IsKeyPressed(int key)
{
    static int previous[256] = { 0 };
    int current = CheckHitKey(key);
    int pressed = 0;
    if (current != 0 && previous[key] == 0)
    {
        pressed = 1;
    }

    previous[key] = current;
    return pressed;
}

5.まず右へ1マスだけ動かそう

いきなり4方向を書かず、最初はDキーだけにします。 さっき上のサンプルでやった右に1マス動く移動を書くだけだね。
ゲームループのClearDrawScreen();の下へ追加してください。

Point playerPos = GetPlayerPos(&currentMap);
Point nextPos = playerPos;

if (IsKeyPressed(KEY_INPUT_D))
{
    // Dキーなので、現在位置から右へ1マス進んだ位置を作る
    nextPos.x = ________________________________;

    // 移動先をコスコマンのマスに書き換える
    SetObjtoMap(____________________, nextPos, &currentMap);

    // 元いたマスを床へ戻す
    SetObjtoMap(____________________, playerPos, &currentMap);
}

ヒント

右へ1マスはxが1増えます。移動後は、移動先にコスコマン、元の位置に床を置きます。

模範解答を見る
Point playerPos = GetPlayerPos(&currentMap);
Point nextPos = playerPos;

if (IsKeyPressed(KEY_INPUT_D))
{
    nextPos.x = playerPos.x + 1;
    SetObjtoMap(HUMAN, nextPos, &currentMap);
    SetObjtoMap(FLOOR, playerPos, &currentMap);
}

実行して確認

Dキーを1回押すたびに@が右へ1マス動けば成功です。

ここで起きているのは、文字そのものを動かしているのではなく、Mapの数字を書き換えた結果、次の描画で@の場所が変わっているということです。


6.WASD全部書いてみようz

右へ動く仕組みができたので、上下左右へ増やします。

さきほどのDキーだけの処理を、次の処理へ置き換えてください。

Point playerPos = GetPlayerPos(&currentMap);
Point nextPos = playerPos;
int move = 0;

if (IsKeyPressed(KEY_INPUT_W))
{
    // 上へ1マス進むので、縦位置を1つ減らす
    nextPos.y = ________________________________;
    move = 1;
}
else if (IsKeyPressed(KEY_INPUT_A))
{
    // 左へ1マス進むので、横位置を1つ減らす
    nextPos.x = ________________________________;
    move = 1;
}
else if (IsKeyPressed(KEY_INPUT_S))
{
    // 下へ1マス進むので、縦位置を1つ増やす
    nextPos.y = ________________________________;
    move = 1;
}
else if (IsKeyPressed(KEY_INPUT_D))
{
    // 右へ1マス進むので、横位置を1つ増やす
    nextPos.x = ________________________________;
    move = 1;
}

if (move &&
    nextPos.x >= 0 && nextPos.x < currentMap.stage_width &&
    nextPos.y >= 0 && nextPos.y < currentMap.stage_height)
{
    // 移動先へコスコマンを置く
    SetObjtoMap(____________________, nextPos, &currentMap);

    // 元の位置を床へ戻す
    SetObjtoMap(____________________, playerPos, &currentMap);
}

ヒント

Mapでは上へ行くほどyが小さく、下へ行くほどyが大きくなります。左右はxを変えます。

模範解答を見る
Point playerPos = GetPlayerPos(&currentMap);
Point nextPos = playerPos;
int move = 0;

if (IsKeyPressed(KEY_INPUT_W))
{
    nextPos.y = playerPos.y - 1;
    move = 1;
}
else if (IsKeyPressed(KEY_INPUT_A))
{
    nextPos.x = playerPos.x - 1;
    move = 1;
}
else if (IsKeyPressed(KEY_INPUT_S))
{
    nextPos.y = playerPos.y + 1;
    move = 1;
}
else if (IsKeyPressed(KEY_INPUT_D))
{
    nextPos.x = playerPos.x + 1;
    move = 1;
}

if (move &&
    nextPos.x >= 0 && nextPos.x < currentMap.stage_width &&
    nextPos.y >= 0 && nextPos.y < currentMap.stage_height)
{
    SetObjtoMap(HUMAN, nextPos, &currentMap);
    SetObjtoMap(FLOOR, playerPos, &currentMap);
}

実行して、わざと問題を起こそう

WASDで動けるようになりました。

では、上にある荷物やゴールへそのまま進んでみてください。

今のプログラムは、移動先が何であっても、

SetObjtoMap(HUMAN, nextPos, &currentMap);

で上書きします。

📺 第3回の完成版を動画で確認

そのため、荷物もゴールも壁も消せてしまいます。

これはバグですが、今はそのままで構いません。

今は移動先の種類を調べていないため、この問題が起きます。

次回は、まず4方向移動の計算をちょっとスマートに整理してみます。


7.第3回の完成コード

第3回の完成コードを見る
#include "DxLib.h"

#define SCREEN_WIDTH  720
#define SCREEN_HEIGHT 600

#define MAX_STAGE_WIDTH  10
#define MAX_STAGE_HEIGHT 10

#define TEXT_SIZE 32
#define TEXT_STAGE_X 40
#define TEXT_STAGE_Y 90

enum OBJNAME
{
    FLOOR = 0,
    WALL = 1,
    LUGG = 2,
    GOAL = 3,
    HUMAN = 4,
    HUMAN_ON_GOAL = 5,
    LUGG_ON_GOAL = 6,
    OUTSIDE = 7
};

struct Point
{
    int x;
    int y;
};

struct Map
{
    int stage_width;
    int stage_height;
    int Dat[MAX_STAGE_HEIGHT][MAX_STAGE_WIDTH];
};

const char OBJECT_CHAR[] = {
    ' ',
    '#',
    '$',
    '.',
    '@',
    '+',
    '*',
    ' '
};

const Map START_STAGE = {
    10, 7,
    {
        { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
        { 1, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
        { 1, 0, 0, 0, 3, 0, 0, 0, 0, 1 },
        { 1, 0, 0, 0, 2, 0, 0, 0, 0, 1 },
        { 1, 0, 0, 0, 4, 0, 0, 0, 0, 1 },
        { 1, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
        { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }
    }
};

void LoadStage(Map* map);
void DrawStageText(Map* map, int fontHandle);
Point GetPlayerPos(Map* map);
void SetObjtoMap(OBJNAME obj, Point pos, Map* map);
int IsKeyPressed(int key);

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
    Map currentMap;
    int fontHandle;

    ChangeWindowMode(TRUE);
    SetGraphMode(SCREEN_WIDTH, SCREEN_HEIGHT, 32);
    SetMainWindowText("Costco Man");

    if (DxLib_Init() != 0)
        return -1;

    SetDrawScreen(DX_SCREEN_BACK);

    fontHandle = CreateFontToHandle("MS ゴシック", TEXT_SIZE, 1);
    LoadStage(&currentMap);

    while (ProcessMessage() == 0)
    {
        ClearDrawScreen();

        Point playerPos = GetPlayerPos(&currentMap);
        Point nextPos = playerPos;
        int move = 0;

        if (IsKeyPressed(KEY_INPUT_W))
        {
            nextPos.y = playerPos.y - 1;
            move = 1;
        }
        else if (IsKeyPressed(KEY_INPUT_A))
        {
            nextPos.x = playerPos.x - 1;
            move = 1;
        }
        else if (IsKeyPressed(KEY_INPUT_S))
        {
            nextPos.y = playerPos.y + 1;
            move = 1;
        }
        else if (IsKeyPressed(KEY_INPUT_D))
        {
            nextPos.x = playerPos.x + 1;
            move = 1;
        }

        if (move &&
            nextPos.x >= 0 && nextPos.x < currentMap.stage_width &&
            nextPos.y >= 0 && nextPos.y < currentMap.stage_height)
        {
            SetObjtoMap(HUMAN, nextPos, &currentMap);
            SetObjtoMap(FLOOR, playerPos, &currentMap);
        }

        DrawString(24, 24, "COSTCO MAN", GetColor(255, 255, 255));
        DrawStageText(&currentMap, fontHandle);

        ScreenFlip();
    }

    DeleteFontToHandle(fontHandle);
    DxLib_End();
    return 0;
}

void LoadStage(Map* map)
{
    map->stage_width = START_STAGE.stage_width;
    map->stage_height = START_STAGE.stage_height;

    for (int y = 0; y < map->stage_height; y++)
    {
        for (int x = 0; x < map->stage_width; x++)
        {
            map->Dat[y][x] = START_STAGE.Dat[y][x];
        }
    }
}

void DrawStageText(Map* map, int fontHandle)
{
    char line[MAX_STAGE_WIDTH + 1];

    for (int y = 0; y < map->stage_height; y++)
    {
        for (int x = 0; x < map->stage_width; x++)
        {
            int obj = map->Dat[y][x];
            line[x] = OBJECT_CHAR[obj];
        }

        line[map->stage_width] = '\0';

        DrawStringToHandle(
            TEXT_STAGE_X,
            TEXT_STAGE_Y + y * TEXT_SIZE,
            line,
            GetColor(255, 255, 255),
            fontHandle);
    }
}

Point GetPlayerPos(Map* map)
{
    Point result = { -1, -1 };

    for (int y = 0; y < map->stage_height; y++)
    {
        for (int x = 0; x < map->stage_width; x++)
        {
            int obj = map->Dat[y][x];

            if (obj == HUMAN)
            {
                result.x = x;
                result.y = y;
                return result;
            }
        }
    }

    return result;
}

void SetObjtoMap(OBJNAME obj, Point pos, Map* map)
{
    map->Dat[pos.y][pos.x] = obj;
}

int IsKeyPressed(int key)
{
    static int previous[256] = { 0 };
    int current = CheckHitKey(key);
    int pressed = 0;
    if (current != 0 && previous[key] == 0)
    {
        pressed = 1;
    }

    previous[key] = current;
    return pressed;
}
第3回の変更点入り完成コードを見る

このコードは、上の説明なし完成コードと同じ処理です。今回追加・変更した場所を中心に、役割や理由もコメントで確認できます。

  • Pointで位置を表し、Map全体からコスコマンを探す処理
  • W・A・S・Dごとに移動先を直接計算する最初の移動処理
  • 移動先の種類をまだ調べていないため、壁なども上書きできる段階であること
#include "DxLib.h"

#define SCREEN_WIDTH  720
#define SCREEN_HEIGHT 600

#define MAX_STAGE_WIDTH  10
#define MAX_STAGE_HEIGHT 10

#define TEXT_SIZE 32
#define TEXT_STAGE_X 40
#define TEXT_STAGE_Y 90

enum OBJNAME
{
    FLOOR = 0,
    WALL = 1,
    LUGG = 2,
    GOAL = 3,
    HUMAN = 4,
    HUMAN_ON_GOAL = 5,
    LUGG_ON_GOAL = 6,
    OUTSIDE = 7
};

// 【解説・今回追加】xとyを組にして、現在位置や移動先を1つの値として扱う。
struct Point
{
    int x;
    int y;
};

struct Map
{
    int stage_width;
    int stage_height;
    int Dat[MAX_STAGE_HEIGHT][MAX_STAGE_WIDTH];
};

const char OBJECT_CHAR[] = {
    ' ',
    '#',
    '$',
    '.',
    '@',
    '+',
    '*',
    ' '
};

const Map START_STAGE = {
    10, 7,
    {
        { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
        { 1, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
        { 1, 0, 0, 0, 3, 0, 0, 0, 0, 1 },
        { 1, 0, 0, 0, 2, 0, 0, 0, 0, 1 },
        { 1, 0, 0, 0, 4, 0, 0, 0, 0, 1 },
        { 1, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
        { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }
    }
};

void LoadStage(Map* map);
void DrawStageText(Map* map, int fontHandle);
// 【解説・今回追加】Mapからコスコマンを探す関数。見つけた座標をPointで返す。
Point GetPlayerPos(Map* map);
// 【解説・今回追加】指定した座標へ、床やコスコマンなどの値を書き込む関数。
void SetObjtoMap(OBJNAME obj, Point pos, Map* map);
// 【解説・今回追加】キーを押し続けても1回だけ移動するよう、押した瞬間を調べる。
int IsKeyPressed(int key);

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
    Map currentMap;
    int fontHandle;

    ChangeWindowMode(TRUE);
    SetGraphMode(SCREEN_WIDTH, SCREEN_HEIGHT, 32);
    SetMainWindowText("Costco Man");

    if (DxLib_Init() != 0)
        return -1;

    SetDrawScreen(DX_SCREEN_BACK);

    fontHandle = CreateFontToHandle("MS ゴシック", TEXT_SIZE, 1);
    LoadStage(&currentMap);

    while (ProcessMessage() == 0)
    {
        ClearDrawScreen();

        // 【解説・流れ】移動を計算する前に、Map全体から現在のコスコマンを探す。
        Point playerPos = GetPlayerPos(&currentMap);
        // 【解説・理由】現在位置を残したまま、別のPointで移動先を作る。
        Point nextPos = playerPos;
        int move = 0;

        // 【解説・今回追加】まずは方向ごとにxまたはyを直接増減させ、4方向移動を完成させる。
        if (IsKeyPressed(KEY_INPUT_W))
        {
            nextPos.y = playerPos.y - 1;
            move = 1;
        }
        else if (IsKeyPressed(KEY_INPUT_A))
        {
            nextPos.x = playerPos.x - 1;
            move = 1;
        }
        else if (IsKeyPressed(KEY_INPUT_S))
        {
            nextPos.y = playerPos.y + 1;
            move = 1;
        }
        else if (IsKeyPressed(KEY_INPUT_D))
        {
            nextPos.x = playerPos.x + 1;
            move = 1;
        }

        // 【解説・判定】キー入力があり、移動先がMap内にあるときだけ書き換える。
        // 【解説・注意】この回では移動先の種類を見ていないため、壁・荷物・ゴールも上書きできる。
        if (move &&
            nextPos.x >= 0 && nextPos.x < currentMap.stage_width &&
            nextPos.y >= 0 && nextPos.y < currentMap.stage_height)
        {
            // 【解説・流れ】移動先へHUMANを書き、元の位置をFLOORへ戻して移動を表現する。
            SetObjtoMap(HUMAN, nextPos, &currentMap);
            SetObjtoMap(FLOOR, playerPos, &currentMap);
        }

        DrawString(24, 24, "COSTCO MAN", GetColor(255, 255, 255));
        DrawStageText(&currentMap, fontHandle);

        ScreenFlip();
    }

    DeleteFontToHandle(fontHandle);
    DxLib_End();
    return 0;
}

void LoadStage(Map* map)
{
    map->stage_width = START_STAGE.stage_width;
    map->stage_height = START_STAGE.stage_height;

    for (int y = 0; y < map->stage_height; y++)
    {
        for (int x = 0; x < map->stage_width; x++)
        {
            map->Dat[y][x] = START_STAGE.Dat[y][x];
        }
    }
}

void DrawStageText(Map* map, int fontHandle)
{
    char line[MAX_STAGE_WIDTH + 1];

    for (int y = 0; y < map->stage_height; y++)
    {
        for (int x = 0; x < map->stage_width; x++)
        {
            int obj = map->Dat[y][x];
            line[x] = OBJECT_CHAR[obj];
        }

        line[map->stage_width] = '\0';

        DrawStringToHandle(
            TEXT_STAGE_X,
            TEXT_STAGE_Y + y * TEXT_SIZE,
            line,
            GetColor(255, 255, 255),
            fontHandle);
    }
}

// 【解説・役割】二重forでMapを左上から調べ、HUMANを見つけた時点で座標を返す。
Point GetPlayerPos(Map* map)
{
    Point result = { -1, -1 };

    for (int y = 0; y < map->stage_height; y++)
    {
        for (int x = 0; x < map->stage_width; x++)
        {
            int obj = map->Dat[y][x];

            if (obj == HUMAN)
            {
                result.x = x;
                result.y = y;
                return result;
            }
        }
    }

    return result;
}

// 【解説・役割】Pointのyを行、xを列としてDatへ書き込む。
void SetObjtoMap(OBJNAME obj, Point pos, Map* map)
{
    map->Dat[pos.y][pos.x] = obj;
}

int IsKeyPressed(int key)
{
    // 【解説・理由】前フレームのキー状態を関数呼び出し後も残し、現在との差から押した瞬間を求める。
    static int previous[256] = { 0 };
    int current = CheckHitKey(key);
    int pressed = 0;
    if (current != 0 && previous[key] == 0)
    {
        pressed = 1;
    }

    previous[key] = current;
    return pressed;
}