Tonyu System 2であほげーを作ろう - その8 物理エンジンなど

# 記事一覧

[第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)

# はじめに

さて，気づいたら明日は[お題発表](https://www.youtube.com/watch?v=EuZKEKyUds8&feature=youtu.be)じゃないか！ということで，駆け込みでもう少しゲームの中身について改造をしてみたいと思います．

Tonyu2 で作っているゲームの内容について「こういう改造をしてみたいけどどうやったらいいの？」という疑問が出たら，ドキュメント内に

[用途別リファレンス](https://edit.tonyu.jp/doc/%E7%94%A8%E9%80%94%E5%88%A5%E3%83%AA%E3%83%95%E3%82%A1%E3%83%AC%E3%83%B3%E3%82%B9.html)

というのがありますので，参考にしてください．

今回は，その中の一例として，物理エンジンを使ったゲームを作ってみようと思います．

[今回のプログラムを開く](https://edit.tonyu.jp/index.html?fromPrjB=ahgtmpl0.11.zip)

今回のゲームは前回同様<<第26回のテーマ>>を，<<テーマに関連する何か>>をよけながら集めます．前回と違って，サイドビューで重力が働いています．上を押すとジャンプします.

# プログラム

## 床の追加

まず，重力が働くため，床がないと落ちてしまいます．そこで，Floor(床）クラスを新たに追加しています．

~~~JS:Floor
extends BodyActor;
width=200;
height=10;
fillStyle="white";
isStatic=true;
~~~

物理運動に関係する（物理運動するオブジェクトとぶつかる）オブジェクトは，[BodyActor](https://edit.tonyu.jp/doc/BodyActor.html)クラスを継承して作ります．床のテクスチャを用意するのが面倒なので，代わりに`width`，`height`，`fillStyle`フィールドに値を設定すると，指定した大きさ，色をもつキャラクタが表示されるようになります．また，`width`，`height`が当たり判定の大きさになります．
`isStatic=true;`は，落下などの物理運動をしない，他の物理オブジェクトとの衝突だけを行うオブジェクトに対して用います．

Mainの最初に床の出現を行っています．`rotation`を指定しているので，床が傾いています．

~~~JS:Main(一部)
new Floor{x=200,y=400,rotation=10};
new Floor{x=300,y=250,rotation=-10};
~~~

## 物理運動

既存のキャラクタ達も，物理運動させるため，[BodyActor](https://edit.tonyu.jp/doc/BodyActor.html) を継承したクラスに変更しています．
例えばEnemyクラスでは，1行目にextends節を追加し，物理的な当たり判定の形状を「円形」にするため，`radius`フィールドを定義しています．`radius`は当たり判定の大きさの設定も兼ねています．Targetクラスでも同様の変更をしています．

また，BodyActorでは`vx`や`vy`は予約されているフィールドで，これらに値を代入するだけでオブジェクトの速度を変更することができます．したがって，従来のオブジェクトにあった`x+=vx`などは不要になります．

~~~JS:Enemy
extends BodyActor;//★追加

radius=16;//★追加
p=$pat_ahoge26+2;
while(true) {
    //x+=vx;  ★削除
    //y+=vy;
    if (screenOut()) die();
    update();
}
~~~

Playerクラスはもっとあちこちいじっています．

~~~JS:Player
extends BodyActor;//★A 追加
pad=new APad;
p=$pat_ahoge26+0;
radius=16;//★A 追加
while(true) {
    vx+=pad.vx*0.3;//★B 変更 移動の処理
    //x+=pad.vx*5; ← これまでの処理
    //y+=pad.vy*5;
    t=contactTo(Target);//★C変更 crashTo → contactTo
    if (t) {
        $sound.playSE($se_coin) {rate:1+$score*0.1};
        $score+=1;
        t.die();
        if ($score>=10) {
            $state="clear";
            break;
        }
    }
    //---★Dジャンプの処理
    cp=contactPoint(Floor);
    if (cp && cp.y>y) {
        jumpable=1;
    }
    if (pad.vy<-0.1 && jumpable) {
        jumpable--;
        vy=-10;
    }
    //---★Dジャンプの処理ここまで
    e=contactTo(Enemy);//★C 変更 crashTo → contactTo
    if (e) {
        $state="gameover";
        die();
    }
    if (screenOut()) $state="gameover";
    update();
}
~~~

- ★AはEnemyクラスと同じ変更です．ちなみに当たり判定を「四角形」にしたい場合は，`radius`の代わりに`width`，`height`を設定します．
- ★Bでは，物理オブジェクトを移動させるときの方法を変更しています．物理オブジェクトに対して，従来のようにxやyを直接書き換えるのはおすすめしません（正しい物理シミュレーションができなくなる）．代わりに`vx`や`vy`を変更してください．
- ★Cでは crashToメソッドを[contactTo](https://edit.tonyu.jp/doc/contactTo.html)メソッドに変更しています．物理オブジェクト同士の衝突判定にはcontactToを使ってください．
- ★Dは，次に詳説します
  - ★D-1  [contactPoint](https://edit.tonyu.jp/doc/contactPoint.html)メソッドは，contactToメソッドの強化版です．当たっているオブジェクトだけでなく，どの点で当たったかを返します．`(cp.x,cp.y)`が当たった点，`cp.target`が当たったオブジェクトになります．引数に`Floor`クラスが指定されているので，床に当たっている場合，当たっている点を返しています．
  - ★D-2 当たってた点がある（nullやundefinedでない）場合で，かつ当たった点のy座標がプレイヤーのy座標より大きい，というのはつまり「床に当たっていて，当たりどころが自分より下，つまり頭とかじゃなくて足元」ということです．この場合，今プレイヤーは足場にいることになります．したがって`jumpable`(ジャンプできるよー）変数に1を入れています．
  - ★D-3 パッドを上に入れていて，ジャンプできるよきには`vy`に負の値を入れて，上方向に力を加えます．そのとき`jumpable`は0に戻します．

~~~JS
    cp=contactPoint(Floor);//★D-1
    if (cp && cp.y>y) {// ★D-2
        jumpable=1;
    }
    if (pad.vy<-0.1 && jumpable) {//★D-3
        jumpable--;
        vy=-10;
    }
~~~

最後に，Mainクラスにおける敵やターゲットの出方を変更しています．

~~~JS:Main(一部）
while($state=="play") {
    if (rnd(30)==0) {//出現確率UP
        new Target{x=rnd($screenWidth),y=0,vx=rndFloat(-5,5),vy=1};//vxをランダムに
    }
    if (rnd(10)==0) {//出現確率UP
        new Enemy{x=rnd($screenWidth),y=$screenHeight,vx=rndFloat(-5,5),vy=-10-rnd()*5};//vxをランダム，vyも上方向にランダム
    }
    $frames++;
    $time=floor($frames/$Boot.getFrameRate());
    update();
}
~~~

## おまけ － キーボードショートカット

今回の変更と直接関係ありませんが，最終バージョンになるので，もう1つ地味ながらプレイアビリティ？を上げる変更をしています．

~~~JS:Title（一部）
//スタートボタン
new Button{
    text="Start",
    top=300,
    onClick=start,
    key="space" //追加
};
~~~

タイトルのスタートボタンに`key="space"`という属性を加えています．PCでプレイしているときに，ボタンをマウスで押す代わりにスペースキーで代用できるようにしています．Mainクラスでゲームオーバーになったときの「Title」ボタンにも同様に追加してます．これで，キーボードから手を放さず何度でもプレイできるようになります．

# さいごに

というわけで，明日にはテーマ発表なので，連載はたぶんこれで完了です．読んでくださった方はありがとうございます．
連載冒頭でも申し上げましたが，ゲーム内容もグラフィックスも音楽も手抜きです．これはあくまで「タイトル」「クリア」「ゲームオーバー」などの枠組みを提供するためのものだと思ってください．皆様のセンスでたくさんの「あほね！[^ahone]」を稼いでくだされば幸いです．

[^ahone]: [あほげーのエントリされた各作品](http://ahoge.info/25/index.php)に「あほね！」というボタンがついているのです．