#3
by みけCAT » 5年前
基本的な考え方としては、
「今のフレームの状態と入力に基づき、描画を行い、次のフレームの状態を作る」です。
どの画面を表示するのかというのも「状態」ですし、
どのメニューが選択されているかというも「状態」です。
以下のコードのように、変数で状態を管理し、状態に応じて処理を分けると良いでしょう。
他のポイントとしては、(通常行われるように)毎フレーム画面を初期化する場合、
printfDxの出力はフレームをまたいで残るので状態が変わる直前に行い、
DrawFormatStringやDrawGraphの出力はそのフレームだけに反映されるので毎フレーム行います。
なお、画像は毎フレーム読み込むのではなく、ゲーム開始時に1回だけ読み込んでおくのがいいでしょう。
さらに、メニューを描画するループの回数が多すぎて範囲外へのアクセスが起こっていたので、修正しました。
また、自分の判断で適当にclsDx(printfDxの出力を消す)の呼び出しを入れました。
「ESCキーでそのままゲームはじめます」と出ているのにEnterキーを押さないと始まらない仕様、
および普通に考えると左が正しいのにA(右)を押さないと正解扱いにならない仕様は、一応残しました。
コード:
#include "DxLib.h"
////自作関数
int Menu();
int StartGame();
int Game();
int StartArasuzi();
int Arasuzi();
int ume();
int take();
int matu();
int Key[256];
int gpUpdateKey()
{
char key[256];
GetHitKeyStateAll(key);
for(int i = 0; i < 256; i++)
{
if ( key[i] != 0)
{
Key[i]++;
}
else
{
Key[i] = 0;
}
}
return 0;
}
///色
int White = GetColor(255, 255, 255);
int Red = GetColor(255, 0, 0);
///menu構造体
typedef struct {
int x, y;
char name[ 128 ];
}MenuElement_t;
///現在の画面
enum CurrentStatusEnum {
MENU,
ARASUZI,
GAME,
END
} currentStatus = MENU;
/////メニュー画面
////操作説明
int x = 100, y = 300;
MenuElement_t MenuElement[3]={
{80,100,"ゲーム説明"},{100,150,"ゲーム開始"},{100,200,"ゲーム終了"}
};
int SelectNum = 0;
///ゲーム画面の状態
enum GameSelectStatusEnum {
SELECT,
UME,
TAKE,
MATU
} gameSelectStatus = SELECT;
int gameStatus;
///画像
int Harami;
int Karubi;
int rose;
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int)
{
/// ウインドウモード
ChangeWindowMode(TRUE);
SetGraphMode(1024, 768,32);
// DXライブラリの初期化
DxLib_Init();
///裏画面設定
SetDrawScreen(DX_SCREEN_BACK);
///画像の読み込み
Harami = LoadGraph("ハラミ.png");
Karubi = LoadGraph("カルビ.png");
rose = LoadGraph("ロース.png");
PlayMusic("BGM.mp3", DX_PLAYTYPE_LOOP);
////裏画面を表画面反映&メッセージ処理&画面クリア&キーの状態更新
while (ScreenFlip() == 0 && ProcessMessage() == 0 && ClearDrawScreen() == 0 && gpUpdateKey() == 0)
{
switch (currentStatus) {
case MENU:
Menu();
break;
case ARASUZI:
Arasuzi();
break;
case GAME:
Game();
break;
case END:
break;
}
if (currentStatus == END) break;
}
// DXライブラリの後始末
DxLib_End();
// ソフトの終了
return 0;
}
int Menu(void)
{
DrawFormatString(50, 50, White, "【焼肉の神】");
////選択処理
if (Key[KEY_INPUT_DOWN] == 1)
{
SelectNum = (SelectNum + 1) % 3;
}
if ( Key[KEY_INPUT_UP] == 1)
{
SelectNum = (SelectNum + 2) % 3;
}
if (Key[KEY_INPUT_DOWN] == 1 || Key[KEY_INPUT_UP] == 1)
{
for (int i = 0; i < 3; i++)
{
if (i == SelectNum)
{
MenuElement[i].x = 80;
}
else
{
MenuElement[i].x = 100;
}
}
}
////ゲーム説明
if (Key[KEY_INPUT_RETURN] == 1 && MenuElement[SelectNum].y == 100)
{
StartArasuzi();
}
////ゲーム終了処理
if (Key[KEY_INPUT_RETURN] == 1 && MenuElement[SelectNum].y == 200)
{
currentStatus = END;
}
/////ゲーム画面処理
if (Key[KEY_INPUT_RETURN] == 1 && MenuElement[SelectNum].y == 150)
{
StartGame();
}
///描画処理
for (int i = 0; i < 3; i++)
{
DrawFormatString(MenuElement[i].x, MenuElement[i].y, GetColor(255, 255, 255), MenuElement[i].name);
}
DrawFormatString(x, y, White, "【下キーを押して選択、Enterキー押して動作】");
return 0;
}
int StartArasuzi(void)
{
clsDx();
printfDx("\n");
printfDx("◎焼肉の神は俺がなる!\n");
printfDx("あらすじ:\n");
printfDx("ある日、<焼肉の王>という焼肉屋さんは焼肉の神を求めていた―\n");
printfDx("「当店三つのコースのメニューそして出されている肉の種類を覚えるお客様は当店の永世VIPになります。\n");
printfDx(" つまり、いつでも当店のご利用は無料になります。」\n");
printfDx("\n");
printfDx("貧乏学生のあなたの戦いは、いざ開幕ーー\n");
printfDx("\n");
printfDx("◎操作説明:");
printfDx("各コースのメニューを覚えてください。\n");
printfDx("梅コース3品、竹コース4品、松コース5品っていう形で難易度が違います。\n");
printfDx("一品には二つ種類の肉が出されます。メニューに書いてある方を見分けて答えを入力しましょう。\n");
printfDx("\n");
printfDx("頑張って全コース制覇しましょう!\n");
printfDx("\n");
printfDx("【ESCキーでそのままゲームはじめます。】\n");
currentStatus = ARASUZI;
return 0;
}
int Arasuzi(void)
{
if (Key[KEY_INPUT_RETURN] == 1)
{
StartGame();
}
return 0;
}
int StartGame(void)
{
clsDx();
printfDx("コースを選んでください(対応するキーを押してください)\n");
printfDx("上キー.梅コース 右キー.竹コース SPACEキー.松コース\n");
currentStatus = GAME;
gameSelectStatus = SELECT;
gameStatus = 0;
return 0;
}
int Game(void)
{
switch (gameSelectStatus) {
case SELECT:
if (Key[KEY_INPUT_UP] == 1)
{
gameSelectStatus = UME;
}
if (Key[KEY_INPUT_RIGHT] == 1)
{
gameSelectStatus = TAKE;
}
if (Key[KEY_INPUT_SPACE] == 1)
{
gameSelectStatus = MATU;
}
break;
case UME:
ume();
break;
case TAKE:
take();
break;
case MATU:
matu();
break;
}
return 0;
}
int ume(void)
{
switch (gameStatus) {
case 0:
clsDx();
printfDx("梅コースのメニューは以下となります。\n");
printfDx("カルビ、ハラミ、ロース。\n");
////一品目
////画像表示
ClearDrawScreen();
printfDx("\n");
printfDx("カルビはどちらですか?(右ならAを入力、左ならBを入力)\n");
gameStatus = 1;
break;
case 1:
DrawGraph(50, 100, Harami, TRUE);
DrawGraph(450, 100, Karubi, TRUE);
if (Key[KEY_INPUT_A] == 1)
{
printfDx("正解です!\n");
////二品目
printfDx("\n");
printfDx("ハラミはどちらですか?(右ならAを入力、左ならSを入力)\n");
gameStatus = 2;
}
if (Key[KEY_INPUT_S] == 1)
{
gameStatus = 7;
}
break;
case 2:
DrawGraph(50, 100, Harami, TRUE);
DrawGraph(450, 100, rose, TRUE);
if (Key[KEY_INPUT_A] == 1)
{
printfDx("正解です!\n");
////三品目
printfDx("\n");
printfDx("ロースはどちらですか?(右ならAを入力、左ならSを入力)\n");
gameStatus = 3;
}
if (Key[KEY_INPUT_S] == 1)
{
gameStatus = 6;
}
break;
case 3:
DrawGraph(50, 100, Karubi, TRUE);
DrawGraph(450, 100, rose, TRUE);
if (Key[KEY_INPUT_A] == 1)
{
DrawFormatString(100, 100, Red, "正解です!\n");
DrawFormatString(100, 200, Red, "梅コースクリアです!\n");
DrawFormatString(100, 400, Red, "Enterキーでゲーム終了です");
gameStatus = 4;
}
if (Key[KEY_INPUT_S] == 1)
{
gameStatus = 5;
}
break;
case 4:
if (Key[KEY_INPUT_RETURN] == 1)
{
clsDx();
currentStatus = MENU;
}
break;
case 5:
///三品目で間違えた状態
DrawFormatString(100, 100, Red, "間違えました。また次回です。\n");
break;
case 6:
///二品目で間違えた状態
DrawFormatString(100, 100, Red, "間違えました。また次回です。\n");
break;
case 7:
///一品目で間違えた状態
DrawFormatString(100, 100, Red, "間違えました。また次回です。\n");
break;
}
return 0;
}
int matu(void)
{
return 0;
}
int take(void)
{
return 0;
}
- 添付ファイル
-
- yakiniku.zip
- 仮素材
- (39.12 KiB) ダウンロード数: 149 回
基本的な考え方としては、
「今のフレームの状態と入力に基づき、描画を行い、次のフレームの状態を作る」です。
どの画面を表示するのかというのも「状態」ですし、
どのメニューが選択されているかというも「状態」です。
以下のコードのように、変数で状態を管理し、状態に応じて処理を分けると良いでしょう。
他のポイントとしては、(通常行われるように)毎フレーム画面を初期化する場合、
printfDxの出力はフレームをまたいで残るので状態が変わる直前に行い、
DrawFormatStringやDrawGraphの出力はそのフレームだけに反映されるので毎フレーム行います。
なお、画像は毎フレーム読み込むのではなく、ゲーム開始時に1回だけ読み込んでおくのがいいでしょう。
さらに、メニューを描画するループの回数が多すぎて範囲外へのアクセスが起こっていたので、修正しました。
また、自分の判断で適当にclsDx(printfDxの出力を消す)の呼び出しを入れました。
「ESCキーでそのままゲームはじめます」と出ているのにEnterキーを押さないと始まらない仕様、
および普通に考えると左が正しいのにA(右)を押さないと正解扱いにならない仕様は、一応残しました。
[code]
#include "DxLib.h"
////自作関数
int Menu();
int StartGame();
int Game();
int StartArasuzi();
int Arasuzi();
int ume();
int take();
int matu();
int Key[256];
int gpUpdateKey()
{
char key[256];
GetHitKeyStateAll(key);
for(int i = 0; i < 256; i++)
{
if ( key[i] != 0)
{
Key[i]++;
}
else
{
Key[i] = 0;
}
}
return 0;
}
///色
int White = GetColor(255, 255, 255);
int Red = GetColor(255, 0, 0);
///menu構造体
typedef struct {
int x, y;
char name[ 128 ];
}MenuElement_t;
///現在の画面
enum CurrentStatusEnum {
MENU,
ARASUZI,
GAME,
END
} currentStatus = MENU;
/////メニュー画面
////操作説明
int x = 100, y = 300;
MenuElement_t MenuElement[3]={
{80,100,"ゲーム説明"},{100,150,"ゲーム開始"},{100,200,"ゲーム終了"}
};
int SelectNum = 0;
///ゲーム画面の状態
enum GameSelectStatusEnum {
SELECT,
UME,
TAKE,
MATU
} gameSelectStatus = SELECT;
int gameStatus;
///画像
int Harami;
int Karubi;
int rose;
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int)
{
/// ウインドウモード
ChangeWindowMode(TRUE);
SetGraphMode(1024, 768,32);
// DXライブラリの初期化
DxLib_Init();
///裏画面設定
SetDrawScreen(DX_SCREEN_BACK);
///画像の読み込み
Harami = LoadGraph("ハラミ.png");
Karubi = LoadGraph("カルビ.png");
rose = LoadGraph("ロース.png");
PlayMusic("BGM.mp3", DX_PLAYTYPE_LOOP);
////裏画面を表画面反映&メッセージ処理&画面クリア&キーの状態更新
while (ScreenFlip() == 0 && ProcessMessage() == 0 && ClearDrawScreen() == 0 && gpUpdateKey() == 0)
{
switch (currentStatus) {
case MENU:
Menu();
break;
case ARASUZI:
Arasuzi();
break;
case GAME:
Game();
break;
case END:
break;
}
if (currentStatus == END) break;
}
// DXライブラリの後始末
DxLib_End();
// ソフトの終了
return 0;
}
int Menu(void)
{
DrawFormatString(50, 50, White, "【焼肉の神】");
////選択処理
if (Key[KEY_INPUT_DOWN] == 1)
{
SelectNum = (SelectNum + 1) % 3;
}
if ( Key[KEY_INPUT_UP] == 1)
{
SelectNum = (SelectNum + 2) % 3;
}
if (Key[KEY_INPUT_DOWN] == 1 || Key[KEY_INPUT_UP] == 1)
{
for (int i = 0; i < 3; i++)
{
if (i == SelectNum)
{
MenuElement[i].x = 80;
}
else
{
MenuElement[i].x = 100;
}
}
}
////ゲーム説明
if (Key[KEY_INPUT_RETURN] == 1 && MenuElement[SelectNum].y == 100)
{
StartArasuzi();
}
////ゲーム終了処理
if (Key[KEY_INPUT_RETURN] == 1 && MenuElement[SelectNum].y == 200)
{
currentStatus = END;
}
/////ゲーム画面処理
if (Key[KEY_INPUT_RETURN] == 1 && MenuElement[SelectNum].y == 150)
{
StartGame();
}
///描画処理
for (int i = 0; i < 3; i++)
{
DrawFormatString(MenuElement[i].x, MenuElement[i].y, GetColor(255, 255, 255), MenuElement[i].name);
}
DrawFormatString(x, y, White, "【下キーを押して選択、Enterキー押して動作】");
return 0;
}
int StartArasuzi(void)
{
clsDx();
printfDx("\n");
printfDx("◎焼肉の神は俺がなる!\n");
printfDx("あらすじ:\n");
printfDx("ある日、<焼肉の王>という焼肉屋さんは焼肉の神を求めていた―\n");
printfDx("「当店三つのコースのメニューそして出されている肉の種類を覚えるお客様は当店の永世VIPになります。\n");
printfDx(" つまり、いつでも当店のご利用は無料になります。」\n");
printfDx("\n");
printfDx("貧乏学生のあなたの戦いは、いざ開幕ーー\n");
printfDx("\n");
printfDx("◎操作説明:");
printfDx("各コースのメニューを覚えてください。\n");
printfDx("梅コース3品、竹コース4品、松コース5品っていう形で難易度が違います。\n");
printfDx("一品には二つ種類の肉が出されます。メニューに書いてある方を見分けて答えを入力しましょう。\n");
printfDx("\n");
printfDx("頑張って全コース制覇しましょう!\n");
printfDx("\n");
printfDx("【ESCキーでそのままゲームはじめます。】\n");
currentStatus = ARASUZI;
return 0;
}
int Arasuzi(void)
{
if (Key[KEY_INPUT_RETURN] == 1)
{
StartGame();
}
return 0;
}
int StartGame(void)
{
clsDx();
printfDx("コースを選んでください(対応するキーを押してください)\n");
printfDx("上キー.梅コース 右キー.竹コース SPACEキー.松コース\n");
currentStatus = GAME;
gameSelectStatus = SELECT;
gameStatus = 0;
return 0;
}
int Game(void)
{
switch (gameSelectStatus) {
case SELECT:
if (Key[KEY_INPUT_UP] == 1)
{
gameSelectStatus = UME;
}
if (Key[KEY_INPUT_RIGHT] == 1)
{
gameSelectStatus = TAKE;
}
if (Key[KEY_INPUT_SPACE] == 1)
{
gameSelectStatus = MATU;
}
break;
case UME:
ume();
break;
case TAKE:
take();
break;
case MATU:
matu();
break;
}
return 0;
}
int ume(void)
{
switch (gameStatus) {
case 0:
clsDx();
printfDx("梅コースのメニューは以下となります。\n");
printfDx("カルビ、ハラミ、ロース。\n");
////一品目
////画像表示
ClearDrawScreen();
printfDx("\n");
printfDx("カルビはどちらですか?(右ならAを入力、左ならBを入力)\n");
gameStatus = 1;
break;
case 1:
DrawGraph(50, 100, Harami, TRUE);
DrawGraph(450, 100, Karubi, TRUE);
if (Key[KEY_INPUT_A] == 1)
{
printfDx("正解です!\n");
////二品目
printfDx("\n");
printfDx("ハラミはどちらですか?(右ならAを入力、左ならSを入力)\n");
gameStatus = 2;
}
if (Key[KEY_INPUT_S] == 1)
{
gameStatus = 7;
}
break;
case 2:
DrawGraph(50, 100, Harami, TRUE);
DrawGraph(450, 100, rose, TRUE);
if (Key[KEY_INPUT_A] == 1)
{
printfDx("正解です!\n");
////三品目
printfDx("\n");
printfDx("ロースはどちらですか?(右ならAを入力、左ならSを入力)\n");
gameStatus = 3;
}
if (Key[KEY_INPUT_S] == 1)
{
gameStatus = 6;
}
break;
case 3:
DrawGraph(50, 100, Karubi, TRUE);
DrawGraph(450, 100, rose, TRUE);
if (Key[KEY_INPUT_A] == 1)
{
DrawFormatString(100, 100, Red, "正解です!\n");
DrawFormatString(100, 200, Red, "梅コースクリアです!\n");
DrawFormatString(100, 400, Red, "Enterキーでゲーム終了です");
gameStatus = 4;
}
if (Key[KEY_INPUT_S] == 1)
{
gameStatus = 5;
}
break;
case 4:
if (Key[KEY_INPUT_RETURN] == 1)
{
clsDx();
currentStatus = MENU;
}
break;
case 5:
///三品目で間違えた状態
DrawFormatString(100, 100, Red, "間違えました。また次回です。\n");
break;
case 6:
///二品目で間違えた状態
DrawFormatString(100, 100, Red, "間違えました。また次回です。\n");
break;
case 7:
///一品目で間違えた状態
DrawFormatString(100, 100, Red, "間違えました。また次回です。\n");
break;
}
return 0;
}
int matu(void)
{
return 0;
}
int take(void)
{
return 0;
}
[/code]