第12回 TITLE・PLAY・CLEARで仕上げよう
第12回 TITLE・PLAY・CLEARで仕上げよう
ここまでで、
- ステージのルール
- 画像表示
- 滑らかな移動
- 効果音
- BGM
まで完成しました。
最後は、ゲーム全体を
TITLE
PLAY
CLEAR
の3つの状態に分けて仕上げます。

今回の流れ
TITLE
↓ ENTER
PLAY
↓ クリア
CLEAR
↓ ENTER
次のPLAY または TITLE
1.今のゲーム状態を表そう
今どの画面なのかを、GameStateという列挙型で表します。
enum GameState
{
GAME_STATE_TITLE,
GAME_STATE_PLAY,
GAME_STATE_CLEAR
};
それぞれ、
GAME_STATE_TITLE
→ タイトル画面
GAME_STATE_PLAY
→ ゲームプレイ中
GAME_STATE_CLEAR
→ クリア画面
です。
WinMain()では、現在の状態をstateという変数で持ちます。
最初はタイトル画面なので、
GameState state = GAME_STATE_TITLE;
から始めます。
2.状態ごとの処理を関数へ分けよう
3つの状態では、更新処理と描画処理がそれぞれ違います。
TITLE
TitleUpdate()
TitleDraw()
PLAY
PlayUpdate()
PlayDraw()
CLEAR
ClearUpdate()
ClearDraw()
それぞれの役割は次の通りです。
TitleUpdate()は、ENTERが押されたらゲームを開始します。
TitleDraw()は、タイトル画面を描きます。
PlayUpdate()は、第11回まで作ってきたゲームの更新処理を行います。
PlayDraw()は、ゲーム画面を描きます。
ClearUpdate()は、ENTERが押されたら次のステージ、またはタイトルへ進みます。
ClearDraw()は、クリア画面を描きます。
先にプロトタイプ宣言を追加します。
void TitleUpdate(Map* map, int currentStage, GameSounds* sounds, GameState* state, int enterPressed);
void TitleDraw(void);
void PlayUpdate(Map* map, int currentStage, GameSounds* sounds, GameState* state, int retryPressed);
void PlayDraw(Map* map, int currentStage, GameImages* images);
void ClearUpdate(Map* map, int* currentStage, GameSounds* sounds, GameState* state, int enterPressed);
void ClearDraw(int currentStage);
では、状態ごとに作っていきましょう。
3.ゲームループを状態で切り替えよう
WinMain()では、switchを使って現在のstateに合う処理だけを呼びます。
ENTERとRキーは、どの状態でも同じゲームループ内で調べるので、switchより前で取得します。
// 状態に関係なく、ENTERとRは毎フレーム調べる
int enterPressed = IsKeyPressed(KEY_INPUT_RETURN);
int retryPressed = IsKeyPressed(KEY_INPUT_R);
switch (state)
{
case GAME_STATE_TITLE:
// タイトルの更新と描画
________________________________;
________________________________;
break;
case GAME_STATE_PLAY:
// ゲーム中の更新と描画
________________________________;
________________________________;
break;
case GAME_STATE_CLEAR:
// クリア画面の更新と描画
________________________________;
________________________________;
break;
}
ヒント
先ほど用意した、
TitleUpdate() / TitleDraw()
PlayUpdate() / PlayDraw()
ClearUpdate() / ClearDraw()
を、それぞれの状態で呼びます。
模範解答を見る
case GAME_STATE_TITLE:
TitleUpdate(¤tMap, currentStage, &sounds, &state, enterPressed);
TitleDraw();
break;
case GAME_STATE_PLAY:
PlayUpdate(¤tMap, currentStage, &sounds, &state, retryPressed);
PlayDraw(¤tMap, currentStage, &images);
break;
case GAME_STATE_CLEAR:
ClearUpdate(¤tMap, ¤tStage, &sounds, &state, enterPressed);
ClearDraw(currentStage);
break;
4.TITLEを作ろう
まず、タイトル画面を作ります。
TITLEでは、
画面を表示
↓
ENTERを待つ
↓
ENTERが押された
↓
ステージを読み込む
↓
移動情報をリセット
↓
BGMを開始
↓
PLAYへ切り替える
という処理をします。
TitleUpdate
void TitleUpdate(Map* map, int currentStage, GameSounds* sounds, GameState* state, int enterPressed)
{
if (enterPressed)
{
// 現在のステージを読み込む
________________________________;
// 前の移動情報を残さない
moveDir = DIR_NONE;
lastMoveResult = MOVE_NONE;
// ゲーム中のBGMを開始する
________________________________;
// 次のフレームからPLAYにする
*state = ________________________________;
}
}
ヒント
ステージ読み込み → LoadStage(map, currentStage)
BGMループ再生 → PlaySoundMem(sounds->bgm, DX_PLAYTYPE_LOOP, TRUE)
次の状態 → GAME_STATE_PLAY
模範解答を見る
LoadStage(map, currentStage);
PlaySoundMem(sounds->bgm, DX_PLAYTYPE_LOOP, TRUE);
*state = GAME_STATE_PLAY;
TitleDraw
タイトル画面は文字で表示します。
void TitleDraw(void)
{
DrawString(250, 215, "COSTCO MAN", GetColor(255, 255, 255));
DrawString(210, 270, "WAREHOUSE PUZZLE", GetColor(255, 220, 100));
DrawString(225, 340, "PRESS ENTER TO START", GetColor(200, 200, 200));
}
5.PLAYを作ろう
PLAYでは、第11回までのゲーム処理をそのまま使います。
大きく変える必要はありません。
処理の流れは、
移動アニメーション中?
YES → 終わるまで待つ
↓ NO
クリアしている?
YES → BGM停止
クリア音
CLEARへ
↓ NO
Rキーならステージを読み直す
↓
方向入力を調べる
↓
MoveObject()
↓
効果音
↓
移動できたら
moveStartPos / moveDir / moveStartTime / lastMoveResult を保存
です。
PlayUpdate
第11回のUpdateGame()を、PLAY専用のPlayUpdate()として使います。
void PlayUpdate(Map* map, int currentStage, GameSounds* sounds, GameState* state, int retryPressed)
{
// 移動アニメーション中なら、終わるまで次の処理へ進まない
if (GetMoveRate() < 1.0f)
return;
// アニメーションが終わったあとでクリア判定する
if (CheckClear(map))
{
StopSoundMem(sounds->bgm);
PlaySoundMem(sounds->clear, DX_PLAYTYPE_BACK, TRUE);
// 次のフレームからCLEARにする
*state = ________________________________;
return;
}
if (retryPressed)
{
LoadStage(map, currentStage);
moveDir = DIR_NONE;
lastMoveResult = MOVE_NONE;
return;
}
Direction dir = GetDirection();
if (dir != DIR_NONE)
{
// MoveObject()を呼ぶ前に、移動前の位置を取得する
Point startPos = GetPlayerPos(map);
MoveResult result = MoveObject(dir, map);
// 第11回で作った関数を呼び出す
PlayMoveSound(result, sounds);
if (result != MOVE_NONE)
{
moveStartPos = startPos;
moveDir = dir;
moveStartTime = GetNowCount();
lastMoveResult = result;
}
}
}
ヒント
クリアしたら移る状態は、
GAME_STATE_CLEAR
です。
模範解答を見る
*state = GAME_STATE_CLEAR;
PlayDraw
描画は、第10回までに作ったDrawStageImage()をそのまま使います。
void PlayDraw(Map* map, int currentStage, GameImages* images)
{
DrawFormatString(24, 20, GetColor(255, 255, 255), "STAGE %d / %d", currentStage + 1, STAGE_COUNT);
DrawString(300, 20, "WASD : MOVE R : RETRY", GetColor(200, 200, 200));
DrawStageImage(map, images);
}
6.CLEARを作ろう
CLEARでは、ENTERが押されたらステージ番号を1つ進めます。
ENTER
↓
currentStageを1増やす
↓
まだ次のステージがある?
├─ YES → 次のステージを読み込み、PLAYへ
└─ NO → 0へ戻してTITLEへ
ClearUpdate
void ClearUpdate(Map* map, int* currentStage, GameSounds* sounds, GameState* state, int enterPressed)
{
if (enterPressed)
{
(*currentStage)++;
if (*currentStage >= STAGE_COUNT)
{
// 全ステージ終了
*currentStage = 0;
moveDir = DIR_NONE;
lastMoveResult = MOVE_NONE;
// タイトルへ戻る
*state = ________________________________;
}
else
{
// 次のステージを始める
LoadStage(map, *currentStage);
moveDir = DIR_NONE;
lastMoveResult = MOVE_NONE;
// BGMを再び開始する
PlaySoundMem(sounds->bgm, DX_PLAYTYPE_LOOP, TRUE);
// ゲームへ戻る
*state = ________________________________;
}
}
}
ヒント
全ステージ終了 → GAME_STATE_TITLE
次のステージ → GAME_STATE_PLAY
模範解答を見る
*state = GAME_STATE_TITLE;
// 次のステージがある場合
*state = GAME_STATE_PLAY;
ClearDraw
void ClearDraw(int currentStage)
{
DrawString(295, 215, "CLEAR!", GetColor(255, 220, 80));
if (currentStage + 1 >= STAGE_COUNT)
{
DrawString(195, 285, "ALL STAGES CLEAR!", GetColor(255, 255, 255));
}
else
{
DrawFormatString(225, 285, GetColor(255, 255, 255), "STAGE %d CLEAR!", currentStage + 1);
}
DrawString(220, 350, "PRESS ENTER", GetColor(200, 200, 200));
}
7.状態とBGMの関係を確認しよう
BGMはPLAYの間だけ流します。
TITLE
BGMなし
↓ ENTER
PLAY
BGM開始
↓ CLEAR
CLEAR
BGM停止
↓ ENTER
次のPLAY
BGM再開
または
TITLE
BGMなし
そのため、
TitleUpdate()でPLAYへ入るときにBGM開始PlayUpdate()でCLEARへ入るときにBGM停止ClearUpdate()で次のPLAYへ入るときにBGM再開
となっています。
8.最初から最後まで遊んでみよう
最後に、ゲームを最初から最後まで確認します。
起動
↓
TITLE
↓ ENTER
PLAY
↓
移動・荷物押し・効果音・BGM
↓
ステージクリア
↓
CLEAR
↓ ENTER
次のPLAY
↓
全ステージクリア
↓
CLEAR
↓ ENTER
TITLE
確認する項目は次の通りです。
- 起動するとTITLEから始まる
- ENTERでPLAYへ進む
- PLAY中はBGMが流れる
- コスコマンと荷物が滑らかに動く
- 移動結果に合った効果音が鳴る
- クリアするとBGMが止まり、クリア音が鳴る
- CLEARからENTERで次のステージへ進む
- 最終ステージのあとTITLEへ戻る
- TITLEへ戻ったとき、前の移動アニメーションが残らない
これでコスコマン完成です。
9.第12回の完成コード
第12回の完成コードを見る
#include "DxLib.h"
#define SCREEN_WIDTH 720
#define SCREEN_HEIGHT 600
#define MAX_STAGE_WIDTH 10
#define MAX_STAGE_HEIGHT 10
#define STAGE_COUNT 3
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];
};
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 }
};
enum GameState
{
GAME_STATE_TITLE,
GAME_STATE_PLAY,
GAME_STATE_CLEAR
};
#define TILE_SIZE 64
#define STAGE_X 40
#define STAGE_Y 90
struct GameImages
{
int floor;
int wall;
int goal;
int luggage;
int human;
};
#define MOVE_TIME 200
Point moveStartPos = { 0, 0 };
Direction moveDir = DIR_NONE;
int moveStartTime = 0;
MoveResult lastMoveResult = MOVE_NONE;
struct GameSounds
{
int move;
int push;
int goal;
int clear;
int bgm;
};
const Map STAGES[STAGE_COUNT] = {
{
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 }
}
},
{
10, 7,
{
{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
{ 1, 0, 0, 0, 1, 0, 0, 0, 0, 1 },
{ 1, 0, 2, 0, 1, 0, 3, 0, 0, 1 },
{ 1, 0, 0, 2, 0, 0, 3, 0, 0, 1 },
{ 1, 0, 0, 0, 1, 1, 0, 0, 0, 1 },
{ 1, 0, 4, 0, 0, 0, 0, 0, 0, 1 },
{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }
}
},
{
10, 7,
{
{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
{ 1, 1, 1, 1, 0, 0, 1, 0, 4, 1 },
{ 1, 1, 1, 0, 0, 0, 1, 0, 0, 1 },
{ 1, 1, 1, 2, 0, 2, 0, 2, 0, 1 },
{ 1, 1, 1, 0, 2, 2, 1, 0, 0, 1 },
{ 1, 3, 3, 3, 3, 3, 0, 0, 1, 1 },
{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }
}
}
};
void LoadStage(Map* map, int stageNumber);
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 CheckClear(Map* map);
void LoadGameImages(GameImages* images);
void DeleteGameImages(GameImages* images);
float GetMoveRate(void);
Point GetMoveDrawPos(Point startPos, Direction dir, float rate);
void DrawStageImage(Map* map, GameImages* images);
void LoadGameSounds(GameSounds* sounds);
void DeleteGameSounds(GameSounds* sounds);
void PlayMoveSound(MoveResult result, GameSounds* sounds);
void TitleUpdate(Map* map, int currentStage, GameSounds* sounds, GameState* state, int enterPressed);
void TitleDraw(void);
void PlayUpdate(Map* map, int currentStage, GameSounds* sounds, GameState* state, int retryPressed);
void PlayDraw(Map* map, int currentStage, GameImages* images);
void ClearUpdate(Map* map, int* currentStage, GameSounds* sounds, GameState* state, int enterPressed);
void ClearDraw(int currentStage);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
Map currentMap;
GameImages images;
GameSounds sounds;
int currentStage = 0;
GameState state = GAME_STATE_TITLE;
ChangeWindowMode(TRUE);
SetGraphMode(SCREEN_WIDTH, SCREEN_HEIGHT, 32);
SetMainWindowText("Costco Man");
if (DxLib_Init() != 0)
return -1;
SetDrawScreen(DX_SCREEN_BACK);
LoadGameImages(&images);
LoadGameSounds(&sounds);
LoadStage(¤tMap, currentStage);
while (ProcessMessage() == 0)
{
ClearDrawScreen();
// 状態に関係なく、ENTERとRは毎フレーム調べる
int enterPressed = IsKeyPressed(KEY_INPUT_RETURN);
int retryPressed = IsKeyPressed(KEY_INPUT_R);
switch (state)
{
case GAME_STATE_TITLE:
TitleUpdate(¤tMap, currentStage, &sounds, &state, enterPressed);
TitleDraw();
break;
case GAME_STATE_PLAY:
PlayUpdate(¤tMap, currentStage, &sounds, &state, retryPressed);
PlayDraw(¤tMap, currentStage, &images);
break;
case GAME_STATE_CLEAR:
ClearUpdate(¤tMap, ¤tStage, &sounds, &state, enterPressed);
ClearDraw(currentStage);
break;
}
ScreenFlip();
}
StopSoundMem(sounds.bgm);
DeleteGameSounds(&sounds);
DeleteGameImages(&images);
DxLib_End();
return 0;
}
void LoadStage(Map* map, int stageNumber)
{
map->stage_width = STAGES[stageNumber].stage_width;
map->stage_height = STAGES[stageNumber].stage_height;
for (int y = 0; y < map->stage_height; y++)
{
for (int x = 0; x < map->stage_width; x++)
{
map->Dat[y][x] = STAGES[stageNumber].Dat[y][x];
}
}
}
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;
}
int CheckClear(Map* map)
{
for (int y = 0; y < map->stage_height; y++)
{
for (int x = 0; x < map->stage_width; x++)
{
if (map->Dat[y][x] == LUGG)
return 0;
}
}
return 1;
}
void LoadGameImages(GameImages* images)
{
images->floor = LoadGraph("images/floor.png");
images->wall = LoadGraph("images/wall.png");
images->goal = LoadGraph("images/goal.png");
images->luggage = LoadGraph("images/luggage.png");
images->human = LoadGraph("images/human.png");
}
void DeleteGameImages(GameImages* images)
{
DeleteGraph(images->floor);
DeleteGraph(images->wall);
DeleteGraph(images->goal);
DeleteGraph(images->luggage);
DeleteGraph(images->human);
}
float GetMoveRate(void)
{
if (moveDir == DIR_NONE)
return 1.0f;
int elapsed = GetNowCount() - moveStartTime;
float rate = (float)elapsed / MOVE_TIME;
if (rate < 0.0f)
rate = 0.0f;
if (rate > 1.0f)
rate = 1.0f;
return rate;
}
Point GetMoveDrawPos(Point startPos, Direction dir, float rate)
{
Point moveVector = DIR_VECTOR[dir];
Point drawPos = {
STAGE_X + startPos.x * TILE_SIZE + (int)(moveVector.x * TILE_SIZE * rate),
STAGE_Y + startPos.y * TILE_SIZE + (int)(moveVector.y * TILE_SIZE * rate)
};
return drawPos;
}
void DrawStageImage(Map* map, GameImages* images)
{
float rate = GetMoveRate();
Point moveVector = DIR_VECTOR[moveDir];
// 荷物を押したとき、荷物はプレイヤーの1マス先から動く
Point luggageStartPos = {
moveStartPos.x + moveVector.x,
moveStartPos.y + moveVector.y
};
// Map上では荷物はすでに1マス移動している
Point luggageEndPos = {
luggageStartPos.x + moveVector.x,
luggageStartPos.y + moveVector.y
};
for (int y = 0; y < map->stage_height; y++)
{
for (int x = 0; x < map->stage_width; x++)
{
int obj = map->Dat[y][x];
int drawX = STAGE_X + x * TILE_SIZE;
int drawY = STAGE_Y + y * TILE_SIZE;
if (obj == WALL)
DrawGraph(drawX, drawY, images->wall, TRUE);
else if (obj == GOAL || obj == HUMAN_ON_GOAL || obj == LUGG_ON_GOAL)
DrawGraph(drawX, drawY, images->goal, TRUE);
else
DrawGraph(drawX, drawY, images->floor, TRUE);
}
}
for (int y = 0; y < map->stage_height; y++)
{
for (int x = 0; x < map->stage_width; x++)
{
int obj = map->Dat[y][x];
int drawX = STAGE_X + x * TILE_SIZE;
int drawY = STAGE_Y + y * TILE_SIZE;
if (obj == HUMAN || obj == HUMAN_ON_GOAL)
{
if (rate < 1.0f)
{
Point drawPos = GetMoveDrawPos(moveStartPos, moveDir, rate);
drawX = drawPos.x;
drawY = drawPos.y;
}
DrawGraph(drawX, drawY, images->human, TRUE);
}
else if (obj == LUGG || obj == LUGG_ON_GOAL)
{
// 荷物を押した移動の途中で、今回動いた荷物だけ描画位置を変える
if (rate < 1.0f &&
(lastMoveResult == MOVE_PUSH ||
lastMoveResult == MOVE_PUSH_GOAL) &&
x == luggageEndPos.x &&
y == luggageEndPos.y)
{
Point drawPos = GetMoveDrawPos(luggageStartPos, moveDir, rate);
drawX = drawPos.x;
drawY = drawPos.y;
}
DrawGraph(drawX, drawY, images->luggage, TRUE);
}
}
}
}
void LoadGameSounds(GameSounds* sounds)
{
sounds->move = LoadSoundMem("sounds/move.wav");
sounds->push = LoadSoundMem("sounds/push.wav");
sounds->goal = LoadSoundMem("sounds/goal.wav");
sounds->clear = LoadSoundMem("sounds/clear.wav");
sounds->bgm = LoadSoundMem("sounds/bgm.wav");
}
void DeleteGameSounds(GameSounds* sounds)
{
DeleteSoundMem(sounds->move);
DeleteSoundMem(sounds->push);
DeleteSoundMem(sounds->goal);
DeleteSoundMem(sounds->clear);
DeleteSoundMem(sounds->bgm);
}
void PlayMoveSound(MoveResult result, GameSounds* sounds)
{
if (result == MOVE_WALK)
PlaySoundMem(sounds->move, DX_PLAYTYPE_BACK, TRUE);
else if (result == MOVE_PUSH)
PlaySoundMem(sounds->push, DX_PLAYTYPE_BACK, TRUE);
else if (result == MOVE_PUSH_GOAL)
PlaySoundMem(sounds->goal, DX_PLAYTYPE_BACK, TRUE);
}
void TitleUpdate(Map* map, int currentStage, GameSounds* sounds, GameState* state, int enterPressed)
{
if (enterPressed)
{
LoadStage(map, currentStage);
moveDir = DIR_NONE;
lastMoveResult = MOVE_NONE;
PlaySoundMem(sounds->bgm, DX_PLAYTYPE_LOOP, TRUE);
*state = GAME_STATE_PLAY;
}
}
void TitleDraw(void)
{
DrawString(250, 215, "COSTCO MAN", GetColor(255, 255, 255));
DrawString(210, 270, "WAREHOUSE PUZZLE", GetColor(255, 220, 100));
DrawString(225, 340, "PRESS ENTER TO START", GetColor(200, 200, 200));
}
void PlayUpdate(Map* map, int currentStage, GameSounds* sounds, GameState* state, int retryPressed)
{
if (GetMoveRate() < 1.0f)
return;
if (CheckClear(map))
{
StopSoundMem(sounds->bgm);
PlaySoundMem(sounds->clear, DX_PLAYTYPE_BACK, TRUE);
*state = GAME_STATE_CLEAR;
return;
}
if (retryPressed)
{
LoadStage(map, currentStage);
moveDir = DIR_NONE;
lastMoveResult = MOVE_NONE;
return;
}
Direction dir = GetDirection();
if (dir != DIR_NONE)
{
// 移動する前のプレイヤー位置を先に覚えておく
Point startPos = GetPlayerPos(map);
MoveResult result = MoveObject(dir, map);
PlayMoveSound(result, sounds);
if (result != MOVE_NONE)
{
moveStartPos = startPos;
moveDir = dir;
moveStartTime = GetNowCount();
lastMoveResult = result;
}
}
}
void PlayDraw(Map* map, int currentStage, GameImages* images)
{
DrawFormatString(24, 20, GetColor(255, 255, 255), "STAGE %d / %d", currentStage + 1, STAGE_COUNT);
DrawString(300, 20, "WASD : MOVE R : RETRY", GetColor(200, 200, 200));
DrawStageImage(map, images);
}
void ClearUpdate(Map* map, int* currentStage, GameSounds* sounds, GameState* state, int enterPressed)
{
if (enterPressed)
{
(*currentStage)++;
if (*currentStage >= STAGE_COUNT)
{
*currentStage = 0;
moveDir = DIR_NONE;
lastMoveResult = MOVE_NONE;
*state = GAME_STATE_TITLE;
}
else
{
LoadStage(map, *currentStage);
moveDir = DIR_NONE;
lastMoveResult = MOVE_NONE;
PlaySoundMem(sounds->bgm, DX_PLAYTYPE_LOOP, TRUE);
*state = GAME_STATE_PLAY;
}
}
}
void ClearDraw(int currentStage)
{
DrawString(295, 215, "CLEAR!", GetColor(255, 220, 80));
if (currentStage + 1 >= STAGE_COUNT)
DrawString(195, 285, "ALL STAGES CLEAR!", GetColor(255, 255, 255));
else
DrawFormatString(225, 285, GetColor(255, 255, 255), "STAGE %d CLEAR!", currentStage + 1);
DrawString(220, 350, "PRESS ENTER", GetColor(200, 200, 200));
}
No Comments