第6回 荷物を押そう
第6回 荷物を押そう
前回は、コスコマンが床とゴールの上を歩き、壁や荷物の前で止まる処理を作りました。
今回は、コスコマンの前にある荷物を1マス押せるようにします。
今回できるようになること
この回を完成させると、次の動きができるようになります。
- 荷物の先が床なら、荷物を1マス押す
- 荷物の先がゴールなら、荷物をゴールへ押す
- 荷物の先が壁・別の荷物・Mapの外なら、何も動かさない
- コスコマンや荷物がゴールから移動しても、ゴールを消さない
荷物を押すときは、横に並んだ3マスを調べます。
コスコマン → 荷物 → 荷物の移動先

1.押せる場合と押せない場合に分けよう
コスコマンの1マス先が荷物だったら、さらにその先を調べます。
荷物の先がFLOORまたはGOAL
→ 押せる
それ以外
→ 押せない
処理も、この2つに分けます。
押せる場合
3マスを書き換える
押せない場合
何も変更しない
次の図では、押せる状態と押せない状態を、押す前と押したあとで確認できます。

押せない場合は、Mapを変更せずにMOVE_NONEを返します。
押せる場合は、次の3マスを書き換えます。
押す前
[ current ][ next ][ nextNext ]
コスコマン 荷物 荷物の移動先
押したあと
1.コスコマンが移動したあとのマスを、`FLOOR`または`GOAL`にする
2.荷物がいた場所へ、コスコマンを移す
3.荷物の移動先へ、荷物を移す
書き込む値は、それぞれのマスが普通の床だったか、ゴールだったかで決まります。
コスコマンがいた場所
currentがHUMAN なら FLOOR
currentがHUMAN_ON_GOAL なら GOAL
荷物がいた場所
nextがLUGG なら HUMAN
nextがLUGG_ON_GOAL なら HUMAN_ON_GOAL
荷物の移動先
nextNextがFLOORなら LUGG
nextNextがGOAL なら LUGG_ON_GOAL
2.荷物を押した結果を追加しよう
第5回のMoveResultには、歩いた結果まで入っています。
enum MoveResult
{
MOVE_NONE,
MOVE_WALK
};
荷物を押した結果として、次の2つを追加します。
enum MoveResult
{
MOVE_NONE,
MOVE_WALK,
MOVE_PUSH,
MOVE_PUSH_GOAL
};
それぞれの意味は次の通りです。
| 名前 | 意味 |
|---|---|
MOVE_PUSH |
荷物を普通の床へ押した |
MOVE_PUSH_GOAL |
荷物をゴールへ押した |
今回は移動結果として返します。後で効果音を付けるときは、この結果を使って音を選べます。
3.荷物押しを追加する場所を確認しよう
荷物を押す処理は、MoveObject()の中へ追加します。
次のコードは、第5回までに作った歩く処理と、今回追加する荷物押しの位置を示しています。
MoveResult MoveObject(Direction dir, Map* map)
{
Point playerPos = GetPlayerPos(map);
Point nextPos = {
playerPos.x + DIR_VECTOR[dir].x,
playerPos.y + DIR_VECTOR[dir].y
};
OBJNAME current = GetObjectNum(playerPos, map);
OBJNAME next = GetObjectNum(nextPos, map);
if (next == FLOOR || next == GOAL)
{
if (current == HUMAN_ON_GOAL)
SetObjtoMap(GOAL, playerPos, map);
else
SetObjtoMap(FLOOR, playerPos, map);
if (next == GOAL)
SetObjtoMap(HUMAN_ON_GOAL, nextPos, map);
else
SetObjtoMap(HUMAN, nextPos, map);
return MOVE_WALK;
}
// 今回は、このifと中の処理を追加する
if (next == LUGG || next == LUGG_ON_GOAL)
{
// 荷物を押す処理
}
return MOVE_NONE;
}
今回完成させるのは、if (next == LUGG || next == LUGG_ON_GOAL)の中です。
完成するまでに、次の処理を順番に作ります。
- 荷物のさらに先の位置を求める
- そのマスに何があるか調べる
- 押せない場合は、何も変更せずに終了する
- コスコマンが移動したあとのマスを、
FLOORまたはGOALにする - 荷物がいたマスへ、コスコマンを移す
- 荷物をその先へ移す
- 荷物を押した結果を返す
最初の状態は次の形です。この中身を、次の節から順番に完成させます。
if (next == LUGG || next == LUGG_ON_GOAL)
{
// 1.荷物のさらに先の位置を求める
// 2.そのマスに何があるか調べる
// 3.押せない場合は何も変更せずに終了する
// 4.コスコマンが移動したあとのマスを書き換える
// 5.荷物がいたマスへコスコマンを移す
// 6.荷物をその先へ移す
// 7.荷物を押した結果を返す
}
4.荷物のさらに先を求めよう
作る処理
荷物はnextPosにあります。そこから移動方向へもう1マス進んだ場所が、荷物の移動先nextNextPosです。
nextPos + DIR_VECTOR[dir] → nextNextPos
nextNextPosを求めたら、GetObjectNum()でそのマスの状態を取得し、nextNextへ入れます。
完成条件
nextがLUGGまたはLUGG_ON_GOALのときだけ処理するnextNextPosのxとyを求める- 荷物の移動先にある値を
nextNextへ入れる
次の空欄を埋めてください。
// 移動先が、普通の荷物またはゴール上の荷物か調べる
if (next == ____________________ || next == ____________________)
{
Point nextNextPos = {
// 荷物の位置から、移動方向へもう1マス進んだx座標
________________________________,
// 荷物の位置から、移動方向へもう1マス進んだy座標
________________________________
};
// 荷物のさらに先に何があるか調べる
OBJNAME nextNext = ________________________________;
}
ヒント
nextPos.xとnextPos.yへ、DIR_VECTOR[dir]のxとyをそれぞれ足します。マスの状態はGetObjectNum()で取得します。
模範解答を見る
// 移動先が、普通の荷物またはゴール上の荷物か調べる
if (next == LUGG || next == LUGG_ON_GOAL)
{
Point nextNextPos = {
// 荷物の位置から、移動方向へもう1マス進んだx座標
nextPos.x + DIR_VECTOR[dir].x,
// 荷物の位置から、移動方向へもう1マス進んだy座標
nextPos.y + DIR_VECTOR[dir].y
};
// 荷物のさらに先に何があるか調べる
OBJNAME nextNext = GetObjectNum(nextNextPos, map);
}
5.押せない場合の処理を作ろう
作る処理
荷物を置けるのは、普通の床かゴールだけです。
nextNextがFLOORまたはGOAL
→ この後の3マス書き換えへ進む
それ以外
→ Mapを変更せずMOVE_NONEを返す
この判定は、nextNextを取得した直後に行います。先にMapを書き換えると、押せない場合にも盤面が変わってしまいます。
完成条件
nextNextがFLOORでもGOALでもなければ、MOVE_NONEを返す- 押せない場合は、
SetObjtoMap()を一度も呼ばない nextNextがFLOORまたはGOALなら、その後の処理へ進む
次の空欄を埋めてください。
// 荷物の先がFLOORではないか調べる
if (nextNext != ____________________ &&
// 荷物の先がGOALでもないか調べる
nextNext != ____________________)
{
// どちらでもなければ、Mapを変更せず「押せない」を返す
return ____________________;
}
ヒント
「床ではない」かつ「ゴールでもない」なら押せません。押せないときの移動結果は
MOVE_NONEです。
模範解答を見る
// 荷物の先がFLOORでもGOALでもなければ押せない
if (nextNext != FLOOR && nextNext != GOAL)
{
// Mapを変更せず「押せない」を返す
return MOVE_NONE;
}
6.コスコマンが移動したあとのマスを書き換えよう
作る処理
荷物を押せることが分かったら、3マスの書き換えを始めます。
最初に、コスコマンが移動したあとのplayerPosを書き換えます。
currentがHUMAN
→ playerPosをFLOORにする
currentがHUMAN_ON_GOAL
→ playerPosをGOALにする
完成条件
currentがHUMAN_ON_GOALなら、playerPosへGOALを書く- それ以外なら、
playerPosへFLOORを書く
次の空欄を埋めてください。
// コスコマンがゴール上にいたか調べる
if (current == ____________________)
{
// コスコマンが移動したあとのplayerPosをGOALにする
SetObjtoMap(____________________, playerPos, map);
}
else
{
// コスコマンが移動したあとのplayerPosをFLOORにする
SetObjtoMap(____________________, playerPos, map);
}
ヒント
コスコマンがゴール上にいた場合、その場所から移動するとゴールだけが残ります。
模範解答を見る
// コスコマンがゴール上にいた場合
if (current == HUMAN_ON_GOAL)
{
// コスコマンが移動したあとのplayerPosをGOALにする
SetObjtoMap(GOAL, playerPos, map);
}
else
{
// コスコマンが移動したあとのplayerPosをFLOORにする
SetObjtoMap(FLOOR, playerPos, map);
}
7.荷物がいたマスへコスコマンを移そう
作る処理
荷物を押すと、コスコマンは荷物がいたnextPosへ移動します。
nextがLUGG
→ nextPosへHUMANを書く
nextがLUGG_ON_GOAL
→ nextPosへHUMAN_ON_GOALを書く
完成条件
nextがLUGG_ON_GOALなら、nextPosへHUMAN_ON_GOALを書く- それ以外なら、
nextPosへHUMANを書く
次の空欄を埋めてください。
// 荷物がゴール上にあったか調べる
if (next == ____________________)
{
// 荷物がいた場所へ、ゴール上のコスコマンを置く
SetObjtoMap(____________________, nextPos, map);
}
else
{
// 荷物がいた場所へ、普通のコスコマンを置く
SetObjtoMap(____________________, nextPos, map);
}
ヒント
ゴール上にあった荷物が移動すると、そのマスにはゴール上のコスコマンが入ります。
模範解答を見る
// 荷物がゴール上にあった場合
if (next == LUGG_ON_GOAL)
{
// 荷物がいた場所へ、ゴール上のコスコマンを置く
SetObjtoMap(HUMAN_ON_GOAL, nextPos, map);
}
else
{
// 荷物がいた場所へ、普通のコスコマンを置く
SetObjtoMap(HUMAN, nextPos, map);
}
8.荷物をその先へ移そう
作る処理
最後に、荷物をnextNextPosへ移します。
nextNextがFLOOR
→ nextNextPosへLUGGを書く
→ MOVE_PUSHを返す
nextNextがGOAL
→ nextNextPosへLUGG_ON_GOALを書く
→ MOVE_PUSH_GOALを返す
完成条件
- 移動先がゴールなら、ゴール上の荷物を書いて
MOVE_PUSH_GOALを返す - 移動先が普通の床なら、普通の荷物を書いて
MOVE_PUSHを返す
次の空欄を埋めてください。
// 荷物の移動先がゴールか調べる
if (nextNext == ____________________)
{
// 荷物の移動先へ、ゴール上の荷物を置く
SetObjtoMap(____________________, nextNextPos, map);
// ゴールへ荷物を押した結果を返す
return ____________________;
}
// 荷物の移動先へ、普通の荷物を置く
SetObjtoMap(____________________, nextNextPos, map);
// 普通の床へ荷物を押した結果を返す
return ____________________;
ヒント
ゴールへ荷物を置くと
LUGG_ON_GOALになります。普通の床へ置くとLUGGになります。
模範解答を見る
// 荷物の移動先がゴールの場合
if (nextNext == GOAL)
{
// 荷物の移動先へ、ゴール上の荷物を置く
SetObjtoMap(LUGG_ON_GOAL, nextNextPos, map);
// ゴールへ荷物を押した結果を返す
return MOVE_PUSH_GOAL;
}
// 荷物の移動先へ、普通の荷物を置く
SetObjtoMap(LUGG, nextNextPos, map);
// 普通の床へ荷物を押した結果を返す
return MOVE_PUSH;
9.完成した荷物押し処理を確認しよう
if (next == LUGG || next == LUGG_ON_GOAL)の中が、次の形になれば荷物押しの処理は完成です。
if (next == LUGG || next == LUGG_ON_GOAL)
{
// 1.荷物のさらに先の位置を求める
Point nextNextPos = {
nextPos.x + DIR_VECTOR[dir].x,
nextPos.y + DIR_VECTOR[dir].y
};
// 2.荷物のさらに先に何があるか調べる
OBJNAME nextNext = GetObjectNum(nextNextPos, map);
// 3.押せない場合は、Mapを変更せずに終了する
if (nextNext != FLOOR && nextNext != GOAL)
{
return MOVE_NONE;
}
// 4.コスコマンが移動したあとのマスを書き換える
if (current == HUMAN_ON_GOAL)
{
SetObjtoMap(GOAL, playerPos, map);
}
else
{
SetObjtoMap(FLOOR, playerPos, map);
}
// 5.荷物がいたマスへコスコマンを移す
if (next == LUGG_ON_GOAL)
{
SetObjtoMap(HUMAN_ON_GOAL, nextPos, map);
}
else
{
SetObjtoMap(HUMAN, nextPos, map);
}
// 6・7.荷物をゴールへ移し、結果を返す
if (nextNext == GOAL)
{
SetObjtoMap(LUGG_ON_GOAL, nextNextPos, map);
return MOVE_PUSH_GOAL;
}
// 6・7.荷物を普通の床へ移し、結果を返す
SetObjtoMap(LUGG, nextNextPos, map);
return MOVE_PUSH;
}
処理の順番を確認しよう
1マス先が荷物か調べる
↓
荷物のさらに先を調べる
↓
押せないなら、何も変更せず終了
↓
押せるなら、3マスを書き換える
↓
荷物を押した結果を返す
押せるかどうかの判定を、3マスの書き換えより先に行うことが重要です。
📺動画で確認しよう
実行して確認
次の動きをすべて確認してください。
- 荷物を普通の床へ押せる
- 荷物をゴールへ押すと
*になる - 荷物の先が壁なら、コスコマンも荷物も動かない
- 荷物の先が別の荷物なら、コスコマンも荷物も動かない
- ゴール上の荷物を押すと、荷物がいたマスのコスコマンが
+になる - ゴール上にいるコスコマンが荷物を押すと、移動したあとのマスが
.になる
これで、倉庫番の中心となる「荷物を押す」処理が完成しました。
次回は、クリア判定と複数ステージを追加して、文字版コスコマンを完成させます。
10.第6回の完成コード
第6回の完成コードを見る
#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[] = {
' ',
'#',
'$',
'.',
'@',
'+',
'*',
' '
};
enum Direction
{
DIR_UP,
DIR_LEFT,
DIR_DOWN,
DIR_RIGHT,
DIR_NONE
};
enum MoveResult
{
MOVE_NONE,
MOVE_WALK,
MOVE_PUSH,
MOVE_PUSH_GOAL
};
const Point DIR_VECTOR[] = {
{ 0, -1 },
{-1, 0 },
{ 0, +1 },
{+1, 0 },
{ 0, 0 }
};
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);
Direction GetDirection(void);
Point GetPlayerPos(Map* map);
OBJNAME GetObjectNum(Point pos, Map* map);
void SetObjtoMap(OBJNAME obj, Point pos, Map* map);
int IsKeyPressed(int key);
MoveResult MoveObject(Direction dir, Map* map);
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(¤tMap);
while (ProcessMessage() == 0)
{
ClearDrawScreen();
Direction dir = GetDirection();
if (dir != DIR_NONE)
MoveObject(dir, ¤tMap);
DrawString(24, 24, "COSTCO MAN", GetColor(255, 255, 255));
DrawStageText(¤tMap, 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);
}
}
Direction GetDirection(void)
{
// 4方向のキーが押された瞬間を調べる
int up = IsKeyPressed(KEY_INPUT_W);
int left = IsKeyPressed(KEY_INPUT_A);
int down = IsKeyPressed(KEY_INPUT_S);
int right = IsKeyPressed(KEY_INPUT_D);
if (up)
{
return DIR_UP;
}
if (left)
{
return DIR_LEFT;
}
if (down)
{
return DIR_DOWN;
}
if (right)
{
return DIR_RIGHT;
}
return DIR_NONE;
}
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 || obj == HUMAN_ON_GOAL)
{
result.x = x;
result.y = y;
return result;
}
}
}
return result;
}
OBJNAME GetObjectNum(Point pos, Map* map)
{
if (pos.x < 0 || pos.x >= map->stage_width || pos.y < 0 || pos.y >= map->stage_height)
return OUTSIDE;
return (OBJNAME)map->Dat[pos.y][pos.x];
}
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;
}
MoveResult MoveObject(Direction dir, Map* map)
{
Point playerPos = GetPlayerPos(map);
Point nextPos = {
playerPos.x + DIR_VECTOR[dir].x,
playerPos.y + DIR_VECTOR[dir].y
};
OBJNAME current = GetObjectNum(playerPos, map);
OBJNAME next = GetObjectNum(nextPos, map);
if (next == FLOOR || next == GOAL)
{
if (current == HUMAN_ON_GOAL)
SetObjtoMap(GOAL, playerPos, map);
else
SetObjtoMap(FLOOR, playerPos, map);
if (next == GOAL)
SetObjtoMap(HUMAN_ON_GOAL, nextPos, map);
else
SetObjtoMap(HUMAN, nextPos, map);
return MOVE_WALK;
}
if (next == LUGG || next == LUGG_ON_GOAL)
{
Point nextNextPos = {
nextPos.x + DIR_VECTOR[dir].x,
nextPos.y + DIR_VECTOR[dir].y
};
OBJNAME nextNext = GetObjectNum(nextNextPos, map);
if (nextNext != FLOOR && nextNext != GOAL)
return MOVE_NONE;
if (current == HUMAN_ON_GOAL)
SetObjtoMap(GOAL, playerPos, map);
else
SetObjtoMap(FLOOR, playerPos, map);
if (next == LUGG_ON_GOAL)
SetObjtoMap(HUMAN_ON_GOAL, nextPos, map);
else
SetObjtoMap(HUMAN, nextPos, map);
if (nextNext == GOAL)
{
SetObjtoMap(LUGG_ON_GOAL, nextNextPos, map);
return MOVE_PUSH_GOAL;
}
SetObjtoMap(LUGG, nextNextPos, map);
return MOVE_PUSH;
}
return MOVE_NONE;
}
第6回の変更点入り完成コードを見る
このコードは、上の説明なし完成コードと同じ処理です。今回追加・変更した場所を中心に、役割や理由もコメントで確認できます。
- プレイヤー・荷物・荷物の先という3マスを調べる押し処理
- ゴール上の荷物とプレイヤーを正しい複合状態へ書き換える順序
- 歩行・通常押し・ゴールへの押しをMoveResultで区別する理由
#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[] = {
' ',
'#',
'$',
'.',
'@',
'+',
'*',
' '
};
enum Direction
{
DIR_UP,
DIR_LEFT,
DIR_DOWN,
DIR_RIGHT,
DIR_NONE
};
enum MoveResult
{
MOVE_NONE,
MOVE_WALK,
// 【解説・今回追加】荷物を床へ押した場合と、ゴールへ押した場合を別の結果として返す。
MOVE_PUSH,
MOVE_PUSH_GOAL
};
const Point DIR_VECTOR[] = {
{ 0, -1 },
{-1, 0 },
{ 0, +1 },
{+1, 0 },
{ 0, 0 }
};
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);
Direction GetDirection(void);
Point GetPlayerPos(Map* map);
OBJNAME GetObjectNum(Point pos, Map* map);
void SetObjtoMap(OBJNAME obj, Point pos, Map* map);
int IsKeyPressed(int key);
MoveResult MoveObject(Direction dir, Map* map);
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(¤tMap);
while (ProcessMessage() == 0)
{
ClearDrawScreen();
Direction dir = GetDirection();
if (dir != DIR_NONE)
MoveObject(dir, ¤tMap);
DrawString(24, 24, "COSTCO MAN", GetColor(255, 255, 255));
DrawStageText(¤tMap, 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);
}
}
Direction GetDirection(void)
{
// 4方向のキーが押された瞬間を調べる
int up = IsKeyPressed(KEY_INPUT_W);
int left = IsKeyPressed(KEY_INPUT_A);
int down = IsKeyPressed(KEY_INPUT_S);
int right = IsKeyPressed(KEY_INPUT_D);
if (up)
{
return DIR_UP;
}
if (left)
{
return DIR_LEFT;
}
if (down)
{
return DIR_DOWN;
}
if (right)
{
return DIR_RIGHT;
}
return DIR_NONE;
}
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 || obj == HUMAN_ON_GOAL)
{
result.x = x;
result.y = y;
return result;
}
}
}
return result;
}
OBJNAME GetObjectNum(Point pos, Map* map)
{
if (pos.x < 0 || pos.x >= map->stage_width || pos.y < 0 || pos.y >= map->stage_height)
return OUTSIDE;
return (OBJNAME)map->Dat[pos.y][pos.x];
}
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;
}
MoveResult MoveObject(Direction dir, Map* map)
{
Point playerPos = GetPlayerPos(map);
Point nextPos = {
playerPos.x + DIR_VECTOR[dir].x,
playerPos.y + DIR_VECTOR[dir].y
};
OBJNAME current = GetObjectNum(playerPos, map);
OBJNAME next = GetObjectNum(nextPos, map);
if (next == FLOOR || next == GOAL)
{
if (current == HUMAN_ON_GOAL)
SetObjtoMap(GOAL, playerPos, map);
else
SetObjtoMap(FLOOR, playerPos, map);
if (next == GOAL)
SetObjtoMap(HUMAN_ON_GOAL, nextPos, map);
else
SetObjtoMap(HUMAN, nextPos, map);
return MOVE_WALK;
}
// 【解説・今回追加】1マス先が荷物なら、さらにその先が空いているかを調べる。
if (next == LUGG || next == LUGG_ON_GOAL)
{
// 【解説・流れ】移動先へ同じDIR_VECTORをもう一度足し、荷物の移動先を求める。
Point nextNextPos = {
nextPos.x + DIR_VECTOR[dir].x,
nextPos.y + DIR_VECTOR[dir].y
};
// 【解説・判定】荷物の先のマスもGetObjectNumで安全に取得する。
OBJNAME nextNext = GetObjectNum(nextNextPos, map);
// 【解説・判定】nextNextがFLOORでもGOALでもなければ、Mapを書き換えずMOVE_NONEを返す。
if (nextNext != FLOOR && nextNext != GOAL)
return MOVE_NONE;
// 【解説・書き換え1】currentがHUMAN_ON_GOALなら、コスコマンが移動したあとのplayerPosをGOALにする。
if (current == HUMAN_ON_GOAL)
SetObjtoMap(GOAL, playerPos, map);
else
// 【解説・書き換え1】currentがHUMANなら、コスコマンが移動したあとのplayerPosをFLOORにする。
SetObjtoMap(FLOOR, playerPos, map);
// 【解説・書き換え2】nextがLUGG_ON_GOALなら、荷物がいたnextPosへHUMAN_ON_GOALを書く。
if (next == LUGG_ON_GOAL)
SetObjtoMap(HUMAN_ON_GOAL, nextPos, map);
else
// 【解説・書き換え2】nextがLUGGなら、荷物がいたnextPosへHUMANを書く。
SetObjtoMap(HUMAN, nextPos, map);
// 【解説・書き換え3】nextNextがGOALなら、荷物の移動先へLUGG_ON_GOALを書く。
if (nextNext == GOAL)
{
SetObjtoMap(LUGG_ON_GOAL, nextNextPos, map);
return MOVE_PUSH_GOAL;
}
// 【解説・書き換え3】nextNextがFLOORなら、荷物の移動先へLUGGを書き、MOVE_PUSHを返す。
SetObjtoMap(LUGG, nextNextPos, map);
return MOVE_PUSH;
}
return MOVE_NONE;
}
No Comments