Tonyu System 2であほげーを作ろう - その4 スコア・時間など

## 記事一覧

[第1回 設定とタイトル](https://qiita.com/hoge1e3/items/388bdb47f3d56ed0789f)
[第2回 メイン画面](https://qiita.com/hoge1e3/items/1a471a00b38576430f7e)
[第3回 ターゲットと敵と](https://qiita.com/hoge1e3/items/c5d8379db1304a2897fd/)
[第4回 スコア・時間など](https://qiita.com/hoge1e3/items/e2ab8fbe03469e933022)
[第5回 ゲームを公開する](https://qiita.com/hoge1e3/items/f7bd844fd9743073190e)
[第6回 Twitterにスコアを投稿](https://qiita.com/hoge1e3/items/821e5dd1f163d3d9f3c3/)
[第7回 サウンド関係]
(https://qiita.com/hoge1e3/items/a5b1726f6c95bbb4e852)
[第8回 物理エンジンなど]
(https://qiita.com/hoge1e3/items/b2a0df1d5aaba773310f)


## はじめに

[@_ahoge_](https://twitter.com/_ahoge_)さんが，本連載のツイートをRTしてくださったので，気合をいれて第4回を書きました．

前回までで，なんとなくゲームの骨格ができた（ことにします）ので，スコアや時間など，ゲームに必要なパラメータを管理する部分を作っていきます．

[今回のプロジェクトを開く!](https://edit.tonyu.jp/index.html?fromPrjB=ahgtmpl0.05.zip)

## パラメータの設定と表示

まず，Mainの冒頭部分では，スコアと時間に関連する変数を作成する処理が追加されています．いずれも`$`で始まるグローバル変数を用いています．これは，ほかのオブジェクトからもこれらの値を参照する可能性があるからです[^whyglobal]

~~~JS:Main(メインループ手前)
$score=0;
$time=0;
$frames=0;
new Label{template="スコア $score  時間 $time", x=200,y=50, size=20}; //★A
$state="play";
new Player{x=100,y=100};
~~~~

経過時間については，`$time`と`$frames`という2つの変数を用意しました．経過時間をフレーム数`$frames`で計測した上で，秒に変換したものを`$time`に格納しています（変換処理は後述）．なぜそうしているかというと，★Aの`Label`オブジェクトのテンプレート機能を利用するためです．`template`フィールドに文字列を設定すると，文字列内の`$XXXX`の部分を該当するグローバル変数の値に置換して表示します．この値は常時更新されるので，繰り返しの途中でラベルのテキストの書き換え処理を書く必要がありません．

最後の`$state`は，ゲームの状態をあらわすものです．プレイ中は`"play"`，クリアしたら`"clear"`，ゲームオーバーになったら`"gameover"`にそれぞれ設定するようにします（設定する処理は`Player`クラスにて後述）．

## メインループ（経過時間の更新）

次に，メインループの前回からの変更点をしまします．

~~~JS:Main(メインループ)
while($state=="play") {//★B
    if (rnd(50)==0) {
        new Target{x=rnd($screenWidth),y=0,vx=0,vy=1};
    }
    if (rnd(50)==0) {
        new Enemy{x=rnd($screenWidth),y=$screenHeight,vx=0,vy=-1};
    }
    $frames++;//★C
    $time=floor($frames/$Boot.getFrameRate());//★C
    update();
}
~~~

- ★Bでは，メインループの繰り返し条件を`true`(無限ループ)から`$state`が`"play"`の間，つまりプレイが続いている間繰り返す，というように変更しています．
- ★Cでは，経過時間の計測を次のように行っています．
  - `$frames`をフレームごとに増やす
  - `$time`は`$frames`をフレームレート`$Boot.getFrameRate()` で割る[^fps]
  - `floor`関数で小数点以下を切り捨てる

## プレイヤー（スコアと状態の更新）

`Main`のプログラムはまだ続いているのですが，ここでいったん`Player`クラスのほうに目を向けてみましょう．★Dと★Eが追加した部分です．

~~~JS:Player
pad=new APad;
p=$pat_ahoge26+0;
while(true) {
    x+=pad.vx*5;
    y+=pad.vy*5;
    t=crashTo(Target);
    if (t) {
        //----- ★D
        $score+=1;
        if ($score>=10) {
            $state="clear";
            break;
        }
        //------
        t.die();
    }
    e=crashTo(Enemy);
    if (e) {
        $state="gameover";//★E
        die();
    }
    update();
}
~~~

- ★Dでは，ターゲットに触れたときにスコアを増やす処理を追加しています．また，スコアが10になったらクリアということにして，`$state`に`"clear"`を設定しています．また，`break;`によってメインループを抜け出し，プレイヤーが停止するようにします．[^cleargov]
- ★Eでは，敵にあたったときに`$state`に`"gameover"`を設定しています．

いずれも処理でも`$state`が`"play"`以外の値になった場合は，先ほどの`Main`の★Bの条件が不成立になり，メインループが終了します．

## ゲーム終了の処理

ここで再び`Main`に戻り，メインループ後の処理をみていきます．

~~~JS:Main(メインループ後)
switch($state) {
    case "gameover":
    new Label{text="ゲームオーバー",x=230,y=200,size=50};
    break;
    case "clear":
    new Label{text="クリア",x=230,y=200,size=50};
    break;
}
new Button{
    text="Title",
    top=300,
    onClick=goTitle,
};
\goTitle() {
    new FadeEffect{type="out",page=Title};
}
~~~

Tonyu2 でもswitch文が使えます[^tonyu1]．`$state`の値に応じてラベルを表示しています．
そして，タイトルに戻るボタンを表示しています．このへんの処理は[第2回](https://qiita.com/hoge1e3/items/1a471a00b38576430f7e#title)を参照してください．

## バリエーション

今回は「ターゲット規定個数取ったらクリア」というものでしたが，他にもクリア条件はあると思います．各自変更してみてください．

- 一定時間耐えたらクリア（時間内に取った個数を競う）
- 青天井（敵に当たるまでずっと．敵がだんだん速くなるとか．あとターゲットを逃したらだめ，とか）

今回はここまで．この後も[もう少し続きます](https://qiita.com/hoge1e3/items/f7bd844fd9743073190e)．

[第26回あほげー](http://ahoge.info)まであと約1か月です．次回は[7周年](https://twitter.com/_ahoge_/status/1003631974764851200)だったり，[スポンサーイベント](https://twitter.com/_ahoge_/status/1003616865199874048)だったりするらしいので楽しみにしています．


[^whyglobal]: スコアや経過時間に応じて敵が強くなったりする場合，Enemyクラスから`$score`や`$time`を参照することがあるでしょう．
[^fps]: 本プロジェクトではフレームレートは30になっていますが，Tonyu System2の開発環境から「新規作成」で作成したプロジェクトでは，デフォルトのフレームレートは60になります(Ver 2018-0427以降)．変更したい場合は最初に`$Boot.setFrameRate(30);`のように書いてください．
[^cleargov]: クリア後も動けたっていいんですが，敵にあたると消えてしまうので「クリアしたのにやられた！」みたいなことにならないようにするためです(それはそれでいい味という説も)．敵が動き続けるのが気になる方は，`Enemy`のメインループにおいて「`$state`が`"play"`の間だけ繰り返す」ようにしておくとよいでしょう．
[^tonyu1]: 前バージョンのTonyu System(Tonyu1)ではswitch使えなかったのです...