前の記事
全体の目次
Longan Nanoを使ってみる 1 ~ビルド環境の構築~
Longan Nanoを使ってみる 2 ~デバッガの環境設定~
Sipeed RISC-V Debugger
Longan Nanoを使ってみる 3 ~デバッガの使用方法~
Longan Nanoを使ってみる 4 ~printfを使ったデバッグ~
Longan Nanoを使ってみる 5 ~ゲームのプロジェクトを作成~
Longan Nanoを使ってみる 6 ~文字出力~
Longan Nanoを使ってみる ~FONTX2ファイルを作る~
Longan Nanoを使ってみる 7 ~外枠とブロックを書く~
Longan Nanoを使ってみる ~謎の画像表示関数~
Longan Nanoを使ってみる 8 ~ボールを動かす~
Longan Nanoを使ってみる 9 ~A/Dコンバータから入力~
Longan Nanoを使ってみる 10 ~パドルを動かす~
Longan Nanoを使ってみる 11 ~ボタンの入力
Longan Nanoを使ってみる 12 ~ボールのロス~
Longan Nanoを使ってみる 13 ~ステージの遷移とゲームオーバー~
Longan Nanoを使ってみる 14 ~PWMとサウンド~
Longan Nanoを使ってみる 15 ~音楽を鳴らす~
Longan Nanoを使ってみる 16 ~とりあえずのまとめ~
はじめに
ここでは、前回の記事の続きとして、ステージを遷移する処理とゲームオーバーの処理を実装する。
ステージの遷移は、ブロックがすべて消えた時に発生するイベントで、ブロックの配置を初期化してゲームを再開させる。ゲームオーバーは、ライフがゼロになったときに発生し、ゲームを終了させてアイドル状態に戻す。
注意
このページは、quiita.com で公開されています。URLがqiita.com以外のサイト、例えばjpdebug.comなどのページでご覧になっている場合、悪質な無許可転載サイトで記事を見ています。正しい記事は、https://qiita.com/BUBUBB/items/7ce85ada67a3f6d1944d からリンクしています。
無許可転載サイトでの権利表記(CC BY SA 2.5、CC BY SA 3.0とCC BY SA 4.0など)は、不当な表示です。
正確な内容は、qiitaのページで参照してください。
ゲームオーバーの実装
ゲームオーバー処理は、基本的にボールのロスとほとんど同じになる。違いは、表示する文字列(--MISS!--ではなく-GAMEOVER-)と、時間経過後の遷移先(STATE_STARTGAMEではなくSTATE_IDLE)だけ。
これを踏まえた実装は次のようになる。 参考用に、STATE_BALLLOSSの処理も含めてある。
game.c
:
:
case STATE_BALLLOSS:{ // ボールロス
static u16 waitCnt = 0;
// ライフがないならゲームオーバーに遷移
if (LifeCnt == 0) {
gameState = STATE_GAMEOVER;
break;
}
// 画面に"-- MISS! --"と表示させる
LCD_ShowString(5,80,(const unsigned char *)"-- MISS! --",WHITE);
// 一定時間が経過するか、ボタンが押されたらゲームの再開に遷移する
waitCnt++;
if (waitCnt == 0x100 || CheckP1Button()) {
LCD_Clear(BLACK);
waitCnt = 0;
gameState = STATE_STARTGAME;
}
break;
}
case STATE_GAMEOVER:{
/*ゲーム-オーバー処理*/
static u16 waitGameOverCnt = 0;
// 画面に"-- MISS! --"と表示させる
LCD_ShowString(5,80,(const unsigned char *)"-GAMEOVER-",WHITE);
// 一定時間が経過するか、ボタンが押されたらゲームの再開に遷移する
waitGameOverCnt++;
if (waitGameOverCnt == 0x100 || CheckP1Button()) {
LCD_Clear(BLACK);
waitGameOverCnt = 0;
gameState = STATE_IDLE;
}
break;
}
:
:
ステージクリア
ステージ変数の追加
同じように、ステージのクリアも実装することができる。現在、ステージについては全く考慮されていないのでステージの変数を用意して、初期化を行う必要がある。ステージはプログラム内にグローバル変数として用意し、初期化はゲームの開始時(STATE_IDLE→STATE_STARTGAMEへの遷移時)に行えばよいだろう。
現在、ブロックの初期化はblock.c内の InitBlock関数で行われているが、これはステージを意識しておらず、常に同じようにブロックを配置する。ゲーム性を高めるため、ステージごとにブロックの配置を変えられるようにしておく。
とりあえず、簡単のため、ステージに応じて、ブロック配置の最初の位置や段数を変えられるようにしておく。InitBlock関数に引数でステージを受け取り、ブロック配置の開始位置と終了位置をあらかじめテーブルで用意しておく。
game.c
#include "lcd/lcd.h"
#include "led.h"
#include "memory.h"
:
:
int LifeCnt = 0;
int Score = 0;
int Stage= 1; // ステージ(追加)
:
:
case STATE_IDLE:{
u16 cnt = heartBeat & 0xFF;
:
if (CheckP1Button()) {
LCD_Clear(BLACK);
Score = 0;
LifeCnt = 3;
Stage = 1; // ステージを初期化してゲーム開始(追加)
InitBlock(Stage); // 引数を追加
gameState = STATE_STARTGAME;
}
break;
}
block.h
:
void InitBlock(int Stage); // 引数を追加
void DrawBlock();
void blockCheck(struct BALLINFO* bi);
#endif
block.c
// ブロックのテーブルを初期化する
//
void InitBlock(int Stage)
{
// ステージごとのブロック位置をテーブルで保持
static int BlockStart[] = {3,4,5,2,2,1,0,0};
static int BlockEnd[] = {6,7,8,7,8,9,8,9};
int stageWork = (Stage-1) & 0x7; // ステージは0~7を繰り返す
// ブロックテーブルを初期化する。
memset((void *)blockmtx,0,sizeof(blockmtx));
blkCnt = 0;
for (int i = 0;i<BLOCK_CNT_H;i++) {
for (int j = BlockStart[stageWork];j<BlockEnd[stageWork];j++) { // 用意されたテーブルに従いブロック配置
blockmtx[i][j].item = 1;
blockmtx[i][j].x1 = i * BLOCK_SIZE_W + (GAMEAREA_X0 + 2);
blockmtx[i][j].y1 = j * BLOCK_SIZE_H + (GAMEAREA_Y0 + 2);
blockmtx[i][j].x2 = blockmtx[i][j].x1 + BLOCK_SIZE_W - 2 ;
blockmtx[i][j].y2 = blockmtx[i][j].y1 + BLOCK_SIZE_H - 3 ;
blkCnt++;
}
}
blkBrk = blkCnt;
}
ステージクリアの状態を作成
まず、ステージクリアの状態を定義する。Longan Nanoを使ってみる 12 ~ボールのロスで追加したように、ヘッダとゲームのメインに状態を追加する。
game.h
enum GAMESTATE {
STATE_INIT, // 初期化処理中
STATE_IDLE,
STATE_STARTGAME, // ゲーム開始(初期化中)
STATE_INGAME, // ゲーム中
STATE_NEXTSTAGE, // 次のステージに進む (追加)
STATE_BALLLOSS, // ボールのロス
STATE_GAMEOVER, // ゲームオーバー処理中
};
game.c
while (TRUE) {
timer_enable(TIMER5); // タイマーを有効にする
// タイマーのウェイト処理。wakeFlagが割り込みルーチン内で1になるまで無限ループする
:
switch (gameState) {
case STATE_INGAME:{
drawDeleteBall(FALSE); // ひとつ前のボールを消す
u8 ret= moveBall();
:
:
break;
}
case STATE_NEXTSTAGE:{
/*ここに処理を追加 */
break;
}
ステージクリア処理の実装
ステージクリア処理も、ボールロスやゲームオーバー処理とほとんど同じ。違いは、ほとんど表示する文字列(--MISS!--ではなくWELL DONE!と、時間経過後の遷移先(STATE_STARTGAME)。
ただし、このままSTATE_STARTGAME(ボールロス時の遷移先)に遷移してしまうと、ブロックがない状態でゲームが開始されてしまう。そのため、遷移前にブロックの初期化処理を行う必要がある。
game.c
:
case STATE_NEXTSTAGE:{
static u16 waitNextCnt = 0;
// 画面に"WELL DONE!"と表示させる
LCD_ShowString(5,80,(const unsigned char *)"WELL DONE!",WHITE);
// 一定時間が経過するか、ボタンが押されたらブロックを初期化してゲームの再開に遷移する
waitNextCnt++;
if (waitNextCnt == 0x100 || CheckP1Button()) {
LCD_Clear(BLACK);
waitNextCnt = 0;
Stage++;
InitBlock(Stage);
gameState = STATE_STARTGAME;
}
break;
}
:
プログラム中、
Stage++;
InitBlock(Stage);
の2行がステージを1つ進めて、ブロックを開始する処理になる。
プログラムのテスト
プログラムを実行すると、ボールを3回ミスすると最初の画面に戻るようになっている。
また、ブロックをすべて消すと、ステージクリアになる。
テストを容易にするため、main.cの中の、Game関数の引数を、TRUE(自動操作)、FALSE(手動操作)に適宜切り替える。
ここまででできたこと
ゲームオーバー時の処理を実装し、さらにステージクリアの際の状態を追加、実装を行った
次の記事に進む
Longan Nanoを使ってみる 14 ~PWMとサウンド1/2~
今回の追加を反映したソースコード
block.h
#ifndef __block_h__
#define __block_h__
// 表示されるブロックの規定値
#define BLOCK_CNT_H 7 // ブロックは横に7個
#define BLOCK_CNT_V 21 // ブロックは縦に最大21個
#define BLOCK_SIZE_W 11 // ブロックの横幅は11ドット
#define BLOCK_SIZE_H 6 // ブロックの縦は6ドット
// ブロックのテーブル
// 画面上はBLOCK_CNT_H x BLOCK_CNT_Vのマス目に分類され、そこにあるブロックの
// 種類が item に入っている。
// テーブルには、事前にこのブロックが存在する矩形の情報が入っており、ボールとの
// 衝突判定ではこの座標が使われる。
struct BLOCKINFO {
unsigned char item;
unsigned char x1;
unsigned char y1;
unsigned char x2;
unsigned char y2;
};
//全体のブロック数と、残りのブロック数。ブロックが少なくなったらボールの速度を上げる、などに使用する。
extern int blkCnt; // 総ブロック数
extern int blkBrk; // 残りブロック数
void InitBlock(int Stage);
void DrawBlock();
void blockCheck(struct BALLINFO* bi);
#endif
block.c
#include "lcd/lcd.h"
#include "led.h"
#include "memory.h"
#include "gd32vf103.h"
#include "game.h"
#include "ball.h"
#include "wall.h"
#include "block.h"
#include "paddle.h"
// ブロックのテーブル
// 画面上はBLOCK_CNT_H x BLOCK_CNT_Vのマス目に分類され、そこにあるブロックの
// 種類が item に入っている。
// テーブルには、事前にこのブロックが存在する矩形の情報が入っており、ボールとの
// 衝突判定ではこの座標が使われる。
struct BLOCKINFO blockmtx[BLOCK_CNT_H][BLOCK_CNT_V];
//全体のブロック数と、残りのブロック数。ブロックが少なくなったらボールの速度を上げる、などに使用する。
int blkCnt = 0; // 総ブロック数
int blkBrk = 0; // 残りブロック数
// ブロックのテーブルを初期化する
//
void InitBlock(int Stage)
{
// ステージごとのブロック位置
static int BlockStart[] = {3,4,5,2,2,1,0,0};
static int BlockEnd[] = {6,7,8,7,8,9,8,9};
int stageWork = (Stage-1) & 0x7; // ステージは0~7を繰り返す
// ブロックテーブルを初期化する。
memset((void *)blockmtx,0,sizeof(blockmtx));
blkCnt = 0;
for (int i = 0;i<BLOCK_CNT_H;i++) {
for (int j = BlockStart[stageWork];j<BlockEnd[stageWork];j++) {
blockmtx[i][j].item = 1;
blockmtx[i][j].x1 = i * BLOCK_SIZE_W + (GAMEAREA_X0 + 2);
blockmtx[i][j].y1 = j * BLOCK_SIZE_H + (GAMEAREA_Y0 + 2);
blockmtx[i][j].x2 = blockmtx[i][j].x1 + BLOCK_SIZE_W - 2 ;
blockmtx[i][j].y2 = blockmtx[i][j].y1 + BLOCK_SIZE_H - 3 ;
blkCnt++;
}
}
blkBrk = blkCnt;
}
// ブロックすべてを描画する
// ブロック崩しでは、ブロックが1つだけ表示される、ということはない(消えていくだけ)ので、
// 表示は無条件で全ブロックを表示させれば良いことになる。
void DrawBlock()
{
static u16 colTbl[] = {RED, BLUE, GREEN ,MAGENTA,CYAN, YELLOW};
for (u8 i = 0 ; i< BLOCK_CNT_H;i++) {
for (u8 j = 0; j<BLOCK_CNT_V;j++) {
u16 col = colTbl[j % 6];
if (blockmtx[i][j].item == 1) {
LCD_Fill(blockmtx[i][j].x1,blockmtx[i][j].y1,blockmtx[i][j].x2,blockmtx[i][j].y2,col);
}
}
}
}
//
// ブロック反射チェック
// この時点では、BALLINFOの座標は移動済みの座標になっている。
//
void blockCheck(struct BALLINFO* bi)
{
// 一つ前の座標
int chkx = CVT_AXIS(bi->oldx);
int chky = CVT_AXIS(bi->oldy);
// 座標がある位置のブロック番号を求める
int xNow = CVT_AXIS(bi->x);
int yNow = CVT_AXIS(bi->y);
int xidx = (xNow - (GAMEAREA_X0+2)) /BLOCK_SIZE_W;
int yidx = (yNow - (GAMEAREA_Y0+2)) / BLOCK_SIZE_H;
// 現在のボールの位置が、ブロックの中にある場合、衝突処理を行う
if (blockmtx[xidx][yidx].item != 0 && GetOrthant(xNow,yNow, blockmtx[xidx][yidx].x1 ,blockmtx[xidx][yidx].y1,blockmtx[xidx][yidx].x2,blockmtx[xidx][yidx].y2) == 5) {
blkBrk--; // 残ブロック数を1つ減らす
// ボールの新しい位置は、ブロックの内側なので、座標はひとつ前の位置に戻さないといけない。
BallBack(bi);
// 難易度調整
if (blkBrk <= (blkCnt / 2)) { // 残ブロックスが全ブロック数の半分以下になったら
bi->SpeedMask |= SPDMSK_BLOCKCNT_1; // スピードレベル1
}
if (blkBrk <= (blkCnt / 4)) { // 残ブロックスが全ブロック数の1/4分以下になったら
bi->SpeedMask |= SPDMSK_BLOCKCNT_2; // スピードレベル2
}
// ボールを跳ね返す。
u8 pos = GetOrthant(chkx,chky, blockmtx[xidx][yidx].x1 ,blockmtx[xidx][yidx].y1,blockmtx[xidx][yidx].x2,blockmtx[xidx][yidx].y2);
if (pos == 2 || pos== 8) {
BallSwapY(bi);
} else if (pos == 4 || pos == 6) {
BallSwapX(bi);
} else {
BallSwapX(bi);
BallSwapY(bi);
}
// ブロックは消す
blockmtx[xidx][yidx].item = 0;
LCD_Fill(blockmtx[xidx][yidx].x1,blockmtx[xidx][yidx].y1,blockmtx[xidx][yidx].x2,blockmtx[xidx][yidx].y2,BLACK);
// スコアの処理
if (bi->SpeedMask & SPDMSK_BACKWALL) { // 裏に入っていたらスコアは増量
Score = Score + 2;
} else {
Score = Score + 1;
}
}
}
game.h
#ifndef __GAME_H__
#define __GAME_H__
extern void Game(bool);
// ゲームの表示領域
#define GAMEAREA_X0 0
#define GAMEAREA_Y0 20
#define GAMEAREA_X1 79
#define GAMEAREA_Y1 148
enum GAMESTATE {
STATE_INIT, // 初期化処理中
STATE_IDLE,
STATE_STARTGAME, // ゲーム開始(初期化中)
STATE_INGAME, // ゲーム中
STATE_NEXTSTAGE, // 次のステージに進む
STATE_BALLLOSS, // ボールのロス
STATE_GAMEOVER, // ゲームオーバー処理中
};
extern int LifeCnt;
extern int Score;
unsigned char GetOrthant(int x , int y , int x1, int y1 , int x2 , int y2);
#endif
game.c
#include "lcd/lcd.h"
#include "led.h"
#include "memory.h"
#include "gd32vf103.h"
#include "game.h"
#include "ball.h"
#include "block.h"
#include "paddle.h"
#include "button.h"
enum GAMESTATE gameState; // ゲームの状態
volatile u8 WakeFlag = 0; // このフラグが1になると、処理が開始される
int LifeCnt = 0;
int Score = 0;
int Stage= 1; // ステージ
//
// 割り込みハンドラ。タイマーにより指定した周期で非同期に呼び出される
//
void TIMER5_IRQHandler(void)
{
if(SET == timer_interrupt_flag_get(TIMER5, TIMER_INT_FLAG_UP)){
timer_interrupt_flag_clear(TIMER5, TIMER_INT_FLAG_UP);
WakeFlag = 1;
static u8 oeFlag;
if (oeFlag == 0) {
gpio_bit_reset(GPIOB, GPIO_PIN_8); //OE#
oeFlag = 1;
} else {
gpio_bit_set(GPIOB, GPIO_PIN_8); //OE#
oeFlag = 0;
}
}
}
//
// タイマーの初期化
//
void timer5_config(int Cnt)
{
// タイマーのパラメータを設定する。
// タイマーの16ビットプリスケーラには54MHzが入力される・・・はずだが、108MHzが入力されるように振舞っている。
// それを、10000分周(timer_initpara.prescaler = 10000-1)すると、おおよそ108,000,000/10000= 10.8Khz
// それを、引数の cnt回数えて(timer_initpara.period = Cnt;)タイマーの周期を決める。
// 例えば、cntが30の時は、10,800 / 30 =360で、360Hzとなる。
timer_parameter_struct timer_initpara;
rcu_periph_clock_enable(RCU_TIMER5);
timer_deinit(TIMER5);
timer_struct_para_init(&timer_initpara);
timer_initpara.prescaler = 10000 - 1;
timer_initpara.alignedmode = TIMER_COUNTER_EDGE;
timer_initpara.counterdirection = TIMER_COUNTER_UP;
timer_initpara.period = Cnt;
timer_initpara.clockdivision = TIMER_CKDIV_DIV1;
timer_init(TIMER5, &timer_initpara);
timer_auto_reload_shadow_enable(TIMER5);
timer_interrupt_enable(TIMER5, TIMER_INT_UP);
// 割り込みを有効にして、タイマー5を設定する
eclic_global_interrupt_enable();
eclic_set_nlbits(ECLIC_GROUP_LEVEL3_PRIO1);
eclic_irq_enable(TIMER5_IRQn,1,0);
// タイマーを開始する
timer_enable(TIMER5);
}
// 外枠とスコア、ライフ残を表示する
void DrawBORDER()
{
LCD_DrawLine(GAMEAREA_X0,GAMEAREA_Y1,GAMEAREA_X0,GAMEAREA_Y0,WHITE);
LCD_DrawLine(GAMEAREA_X0,GAMEAREA_Y0,GAMEAREA_X1,GAMEAREA_Y0,WHITE);
LCD_DrawLine(GAMEAREA_X1,GAMEAREA_Y0,GAMEAREA_X1,GAMEAREA_Y1,WHITE);
LCD_ShowString(0,0,(const u8 *)"SCORE:",WHITE);
LCD_ShowString(10,160-12,(const u8 *)"LIFE:",WHITE);
char life[3];
sprintf(life,"%1d",LifeCnt);
LCD_ShowString(55,160-12,(u8 *)life,WHITE);
u8 scr[12];
sprintf((char *)scr,"%5d0",Score);
LCD_ShowString(38,0,scr,WHITE);
}
// 座標が、矩形の外側に対して、どの象限にいるのかを返す関数
// 1 2 3
// +---------+
// 4 | 5 | 6
// +---------+
// 7 8 9
unsigned char GetOrthant(int x , int y , int x1, int y1 , int x2 , int y2)
{
bool bLowerX1 = (x < x1);
bool bUpperX2 = (x > x2);
bool bLowerY1 = (y < y1);
bool bUpperY2 = (y > y2);
if (bLowerX1) {
return bLowerY1 ? 1: (bUpperY2 ? 7:4);
} else if (bUpperX2) {
return bLowerY1 ? 3: (bUpperY2 ? 9:6);
} else {
return bLowerY1 ? 2: (bUpperY2 ? 8:5);
}
}
//
// メイン処理
//
void Game(bool isDemo)
{
rcu_periph_clock_enable(RCU_GPIOB);
gpio_init(GPIOB, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_8); // B8をデバッグに使う
// 初期化処理
gameState = STATE_INIT; // ステータスを初期化にする
timer5_config(100); // タイマーの初期化を行う
LCD_Init();
LCD_Clear(BLACK);
Adc_init();
u16 tick = 0; // LEDを点滅させるためのカウンターを初期化
gameState = STATE_IDLE; //初期化処理が終了したのでゲーム開始処理を行う
static u16 heartBeat = 0; // 状態遷移ループのカウンタ
while (TRUE) {
timer_enable(TIMER5); // タイマーを有効にする
// タイマーのウェイト処理。wakeFlagが割り込みルーチン内で1になるまで無限ループする
while(WakeFlag == 0) {
delay_1ms(10);
break;
}
tick = (tick + 1) & 0x8FFF; // LEDの点滅用カウンタのインクリメント
WakeFlag = 0; // タイマーのウエイトフラグを初期化する
heartBeat = (heartBeat+1) & 0x7FFF;
switch (gameState) {
case STATE_IDLE:{
u16 cnt = heartBeat & 0xFF;
if (cnt == 0x00) {
LCD_ShowString(5,40,(const unsigned char *)"PUSH BUTTON",WHITE);
LCD_ShowString(5,60,(const unsigned char *)" TO START ",WHITE);
} else if (cnt == 0x80) {
LCD_ShowString(5,40,(const unsigned char *)"PUSH BUTTON",RED);
LCD_ShowString(5,60,(const unsigned char *)" TO START ",RED);
}
if (CheckP1Button()) {
LCD_Clear(BLACK);
Score = 0;
LifeCnt = 3;
Stage = 1;
InitBlock(Stage);
gameState = STATE_STARTGAME;
}
break;
}
case STATE_STARTGAME:{
/*ゲームの開始処理 */
DrawBORDER(); //外枠とライフ残、スコアを画面に表示させる
DrawBlock();
InitBallPos(0,NULL);
InitPaddle();
gameState = STATE_INGAME;
break;
}
case STATE_INGAME:{
drawDeleteBall(FALSE); // ひとつ前のボールを消す
u8 ret= moveBall();
if (ret == 0) { // すべてのボールがなくなったら
LifeCnt--;
gameState = STATE_BALLLOSS; // ボールロスの状態に遷移させる
break;
} else if (ret == 2) { // ブロックがすべてなくなったら
gameState = STATE_NEXTSTAGE;
break;
}
drawDeleteBall(TRUE); // ひとつ前のボールを消す
breakout_PaddleCtrl(isDemo); // パドルを動かす
break;
}
case STATE_NEXTSTAGE:{
static u16 waitNextCnt = 0;
// 画面に"WELL DONE!"と表示させる
LCD_ShowString(5,80,(const unsigned char *)"WELL DONE!",WHITE);
// 一定時間が経過するか、ボタンが押されたらブロックを初期化してゲームの再開に遷移する
waitNextCnt++;
if (waitNextCnt == 0x100 || CheckP1Button()) {
LCD_Clear(BLACK);
waitNextCnt = 0;
Stage++;
InitBlock(Stage);
gameState = STATE_STARTGAME;
}
break;
}
case STATE_BALLLOSS:{ // ボールロス
static u16 waitCnt = 0;
// ライフがないならゲームオーバーに遷移
if (LifeCnt == 0) {
gameState = STATE_GAMEOVER;
break;
}
// 画面に"-- MISS! --"と表示させる
LCD_ShowString(5,80,(const unsigned char *)"-- MISS! --",WHITE);
// 一定時間が経過するか、ボタンが押されたらゲームの再開に遷移する
waitCnt++;
if (waitCnt == 0x100 || CheckP1Button()) {
LCD_Clear(BLACK);
waitCnt = 0;
gameState = STATE_STARTGAME;
}
break;
}
case STATE_GAMEOVER:{
/*ゲーム-オーバー処理*/
static u16 waitGameOverCnt = 0;
// 画面に"-- MISS! --"と表示させる
LCD_ShowString(5,80,(const unsigned char *)"-GAMEOVER-",WHITE);
// 一定時間が経過するか、ボタンが押されたらゲームの再開に遷移する
waitGameOverCnt++;
if (waitGameOverCnt == 0x100 || CheckP1Button()) {
LCD_Clear(BLACK);
waitGameOverCnt = 0;
gameState = STATE_IDLE;
}
break;
}
}
}
}