EgretEngineで超簡単なゲームを作ってみよう(3) タップでの操作の実装

# 連載記事「EgretEngineで超簡単なゲームを作ってみよう」

[(1) プロジェクト作成＆ビルド] (https://qiita.com/motoyasu-yamada/items/584c46ea7e05601e96fa)
[(2) 物理エンジン エクステンションの導入](https://qiita.com/motoyasu-yamada/items/c5facea39b0102d7a365)
[(3) タップでの操作の実装](https://qiita.com/motoyasu-yamada/items/2ff3762015daccd4ebd3)
[(4) 障害物の実装と各種カプセル化](https://qiita.com/motoyasu-yamada/items/6963428cb3ba5ffc7a64)
[(5) 書き出し編](https://qiita.com/motoyasu-yamada/items/013909a29e65f1f4ec1d)

---

こちらの記事[EgretEngineで超簡単なゲームを作ってみよう(2)](https://qiita.com/motoyasu-yamada/items/c5facea39b0102d7a365)の続き

# 動作の説明
今回はこんな感じでボールを表示します。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/302078/3a939fc9-c42e-9ca0-4854-a075ac43885e.png)

1. このボールは自然落下する
2. タップするとボールに少し上向きのインパクト（力×時間）がかかる
3. (見えませんが)地面にあたるとバウンドして止まる

# 完成コード

ひとまずEgretおよびPhysicsエクステンション(p2)の練習ですので、Main.tsのcreateGameSceneメソッドにすべてのコードを書いてしまいます。

```ts:Main.ts
    private createGameScene() {
        const stageHeight = egret.MainContext.instance.stage.stageHeight;
        const stageWidth = egret.MainContext.instance.stage.stageWidth;
        const ballRadiusPixel = stageWidth / 10 / 2;
        const ballRadiusMeter = 0.6;
        const meterPerPixel =  ballRadiusMeter / ballRadiusPixel;
        const worldWidthMeter = meterPerPixel * stageWidth;
        const worldHeightMeter = meterPerPixel * stageHeight;

        const sprite: egret.Sprite = new egret.Sprite();
        this.addChild(sprite);

        const world = new p2.World();
        world.sleepMode = p2.World.BODY_SLEEPING;
        world.gravity = [0, 9.8];

        const groundShape: p2.Plane = new p2.Plane();
        const groundBody: p2.Body = new p2.Body();
        groundBody.position[1] = worldHeightMeter - 1;
        groundBody.angle = Math.PI;
        groundBody.addShape(groundShape);
        world.addBody(groundBody);

        const ball: p2.Body = new p2.Body({ mass: 1, position: [worldWidthMeter/2, ballRadiusMeter] });
        let ballShape = new p2.Circle({ radius: ballRadiusMeter });
        ball.addShape(ballShape);
        world.addBody(ball);

        egret.Ticker.getInstance().register(loop,this);
        this.stage.addEventListener(egret.TouchEvent.TOUCH_BEGIN,tapUpBall, this);

        function loop(dt:number) {
            world.step(1/60,dt / 1000,10);
            drawBall();
        }

        function tapUpBall(e: egret.TouchEvent) {
            ball.applyImpulseLocal([0,-9.8],[0,0]);
        }

        function drawBall() {
            sprite.graphics.clear();

            const g: egret.Graphics = sprite.graphics;
            const px = ball.position[0] / meterPerPixel;
            const py = ball.position[1] / meterPerPixel;
            g.beginFill(0xfff000, 0.5);
            g.drawCircle(px, py, ballRadiusPixel);
            g.endFill();
        }
    }
```

# 画面描画

Physics(p2)は物理演算は行いますが画面描画は行いません。
そのためにp2が演算してはじきだした位置情報をもとに自前で画面描画を行う必要があります

```ts
        function drawBall() {
            sprite.graphics.clear();
            const g: egret.Graphics = sprite.graphics;
            const px = ball.position[0] / meterPerPixel;
            const py = ball.position[1] / meterPerPixel;
            g.lineStyle(1, 0xfff000);
            g.beginFill(0xfff000, 0.5);
            g.drawCircle(px, py, ballRadiusPixel);
            g.endFill();
        }
```

# Physicsの単位

MKS単位系です

|系|単位|
|----|----|
|長さの単位|メートル(metre; m)|
|質量の単位|キログラム(kilogram; kg)|
|時間の単位|秒(second; s)|

そのために、画面の単位と物理演算の単位を変換するための比率`meterPerPixel`を計算する必要があります

ボールは画面の1/10のサイズで描画したい。ただしバスケットボールの論理物理サイズは60cmを想定します。

```ts
        const stageWidth = egret.MainContext.instance.stage.stageWidth;
        const ballRadiusPixel = stageWidth / 10 / 2;
        const ballRadiusMeter = 0.6;
        const meterPerPixel =  ballRadiusMeter / ballRadiusPixel;
```