Longan Nanoを使ってみる 13 ～ステージの遷移とゲームオーバー～

# 前の記事
[Longan Nanoを使ってみる 12 ～ボールのロス](https://qiita.com/BUBUBB/items/f8c6ff860de127c93cb9)

# 全体の目次
[Longan Nanoを使ってみる 1 ～ビルド環境の構築～](https://qiita.com/BUBUBB/items/7ce85ada67a3f6d1944d)
[Longan Nanoを使ってみる 2 ～デバッガの環境設定～](https://qiita.com/BUBUBB/items/864b2ef9404453810d5f)
　[Sipeed RISC-V Debugger](https://qiita.com/BUBUBB/items/61316a4df1d9f81e2864)
[Longan Nanoを使ってみる 3 ～デバッガの使用方法～](https://qiita.com/BUBUBB/items/8efa5989e7f16f67672e)
[Longan Nanoを使ってみる 4 ～printfを使ったデバッグ～](https://qiita.com/BUBUBB/items/b607a3eedc4b66e7a74f)
[Longan Nanoを使ってみる 5 ～ゲームのプロジェクトを作成～](https://qiita.com/BUBUBB/items/98d9c25120c0955d5200)
[Longan Nanoを使ってみる 6 ～文字出力～](https://qiita.com/BUBUBB/items/8dee2eabe78f38364674)
　[Longan Nanoを使ってみる ～FONTX2ファイルを作る～](https://qiita.com/BUBUBB/items/8760ac886423bf06be68)
[Longan Nanoを使ってみる 7 ～外枠とブロックを書く～](https://qiita.com/BUBUBB/items/96abb1158b413c9efd27)
　[Longan Nanoを使ってみる ～謎の画像表示関数～](https://qiita.com/BUBUBB/items/40b99468839e1daeebfb)
[Longan Nanoを使ってみる 8 ～ボールを動かす～](https://qiita.com/BUBUBB/items/92f4816af3c41ea13b9e)
[Longan Nanoを使ってみる 9 ～A/Dコンバータから入力～](https://qiita.com/BUBUBB/items/311c179f7d3a0e30495d)
[Longan Nanoを使ってみる 10 ～パドルを動かす～](https://qiita.com/BUBUBB/items/eb8149c68f6e5456e7ca)
[Longan Nanoを使ってみる 11 ～ボタンの入力](https://qiita.com/BUBUBB/items/2445940a6b0d2110ee04)
[Longan Nanoを使ってみる 12 ～ボールのロス～](https://qiita.com/BUBUBB/items/f8c6ff860de127c93cb9)
[Longan Nanoを使ってみる 13 ～ステージの遷移とゲームオーバー～](https://qiita.com/BUBUBB/items/f1ae81b835f55abe319e)
[Longan Nanoを使ってみる 14 ～PWMとサウンド～](https://qiita.com/BUBUBB/items/3b592078d30cfed0ec53)
[Longan Nanoを使ってみる 15 ～音楽を鳴らす～](https://qiita.com/BUBUBB/items/adb35e02209ad78dfa9b)
[Longan Nanoを使ってみる 16 ～とりあえずのまとめ～](https://qiita.com/BUBUBB/items/ba782ccc80f50b7ab8c3)


# はじめに
ここでは、前回の記事の続きとして、ステージを遷移する処理とゲームオーバーの処理を実装する。

ステージの遷移は、ブロックがすべて消えた時に発生するイベントで、ブロックの配置を初期化してゲームを再開させる。ゲームオーバーは、ライフがゼロになったときに発生し、ゲームを終了させてアイドル状態に戻す。


# 注意
このページは、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のページで参照してください。


# ゲームオーバーの実装
ゲームオーバー処理は、基本的に[ボールのロス](https://qiita.com/BUBUBB/items/f8c6ff860de127c93cb9)とほとんど同じになる。違いは、表示する文字列（--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 ～ボールのロス](https://qiita.com/BUBUBB/items/f8c6ff860de127c93cb9)で追加したように、ヘッダとゲームのメインに状態を追加する。

#### 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が割り込みルーチン内で１になるまで無限ループする
          :
        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);   
の２行がステージを１つ進めて、ブロックを開始する処理になる。

# プログラムのテスト
プログラムを実行すると、ボールを３回ミスすると最初の画面に戻るようになっている。
また、ブロックをすべて消すと、ステージクリアになる。

テストを容易にするため、main.cの中の、Game関数の引数を、TRUE（自動操作）、FALSE（手動操作）に適宜切り替える。


# ここまででできたこと
ゲームオーバー時の処理を実装し、さらにステージクリアの際の状態を追加、実装を行った


# 次の記事に進む
[Longan Nanoを使ってみる 14 ～PWMとサウンド1/2～](https://qiita.com/BUBUBB/items/3b592078d30cfed0ec53)

# 今回の追加を反映したソースコード

#### 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;
}

// ブロックすべてを描画する
// ブロック崩しでは、ブロックが１つだけ表示される、ということはない（消えていくだけ）ので、
// 表示は無条件で全ブロックを表示させれば良いことになる。
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--;							// 残ブロック数を１つ減らす
		// ボールの新しい位置は、ブロックの内側なので、座標はひとつ前の位置に戻さないといけない。
		BallBack(bi);

		// 難易度調整
		if (blkBrk <= (blkCnt / 2)) {				// 残ブロックスが全ブロック数の半分以下になったら
			bi->SpeedMask  |= SPDMSK_BLOCKCNT_1;	// スピードレベル１
		} 
		if (blkBrk <= (blkCnt / 4)) {				// 残ブロックスが全ブロック数の1/4分以下になったら
			bi->SpeedMask |= SPDMSK_BLOCKCNT_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;       // このフラグが１になると、処理が開始される

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);

    // 割り込みを有効にして、タイマー５を設定する
    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が割り込みルーチン内で１になるまで無限ループする
		
        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;
            }
        }

    }
}
```