戦車ラジコン：大砲の照準を作る

もう少し、[戦車ラジコン](https://qiita.com/poruruba/items/0c814783f89f42e65933) をいじります。

M5Cameraで映した画像のどこかにQRコードがあればロックオンとしていましたが、今回は、GamePadの十字キーでちゃんと照準を合わせないと、ロックオンしないようにします。

こっちが照準が敵にあっていない状態。この状態ではまだ攻撃できません。
照準は青い丸です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/261826/50e24bc3-0021-f8bc-413b-7bb35f85e841.png)

こっちが照準があっている状態。青四角で囲われていますので、この状態で攻撃できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/261826/f6a44184-c5b7-6933-6a02-b0309bc2e0bd.png)

毎度の通り、GitHubに上げておきました。
　https://github.com/poruruba/obniz_motor

以下からもページを参照できます。
　https://poruruba.github.io/obniz_motor/

# 仕組み

照準合わせには、DUALSHOCK 4の十字キーを使いました。
画像の再描画タイミングごとに、十字キーの押下状態と経過時間をチェックし、照準を描画しています。
そして、検出したQRコードの青四角を描画するときに、照準の位置との関係を確認し、描画するかどうかを判断します。

# 実装

check_direction関数で、十字キーの押下状態と経過時間のチェックを実装しています。

前回のチェック時に引き続き押下されていれば、移動フラグを立てます。
一緒に前回チェック時からの経過時間を返します。

```js:start.js
        check_direction: function(gamepad){
            var direction = {};
            if( !this.direction.prev ){
                this.direction.prev = performance.now();
                return direction;
            }
            var now = performance.now();
            direction.diff = now - this.direction.prev;
            this.direction.prev = now;

            if( gamepad.buttons[12].pressed ){
                if( this.direction.up )
                    direction.up = true;
                else
                    this.direction.up = true;
            }else{
                this.direction.up = false;
            }
            if( gamepad.buttons[13].pressed ){
                if( this.direction.down )
                    direction.down = true;
                else
                    this.direction.down = true;
            }else{
                this.direction.down = false;
            }
            if( gamepad.buttons[14].pressed ){
                if( this.direction.left )
                    direction.left = true;
                else
                    this.direction.left = true;
            }else{
                this.direction.left = false;
            }
            if( gamepad.buttons[15].pressed ){
                if( this.direction.right )
                    direction.right = true;
                else
                    this.direction.right = true;
            }else{
                this.direction.right = false;
            }

            return direction;
        },
```

配列buttonと十字キーの割り当ては、DUALSHOCK 4の場合以下の通りでした。
・12：上
・13：下
・14：左
・15：右

チェック関数の呼び出し側は、押下があった方向に、現在照準位置を移動させます。
そして、照準位置を中心に円を描きます。

```js:start.js
    var direction = this.check_direction(gamepad);
//  console.log(direction);
    if( direction.up )
        this.aiming_y -= direction.diff * AIMING_DURATION;
    if( direction.down )
        this.aiming_y += direction.diff * AIMING_DURATION;
    if( direction.left )
        this.aiming_x -= direction.diff * AIMING_DURATION;
    if( direction.right )
        this.aiming_x += direction.diff * AIMING_DURATION;

    if( this.aiming_x > this.qrcode_canvas.width)
        this.aiming_x = this.qrcode_canvas.width;
    else if( this.aiming_x < 0 )
        this.aiming_x = 0;
    if( this.aiming_y > this.qrcode_canvas.height)
        this.aiming_y = this.qrcode_canvas.height;
    else if( this.aiming_y < 0 )
        this.aiming_y = 0;

    this.qrcode_context.beginPath();
    this.qrcode_context.arc(this.aiming_x, this.aiming_y, 10, 0 * Math.PI / 180, 360 * Math.PI / 180);
    this.qrcode_context.closePath();
    this.qrcode_context.stroke();
```

AIMING_DURATIONは、経過時間に対する照準位置の移動量です。大きくすると早く移動します。

次は、QRコードを検出したときの青四角の描画処理です。
これまでは、QRコードを検出したときには無条件に青四角を描画していましたが、今回は、照準位置がQRコードの範囲内にないと描画されないようにします。

```js:start.js
    var pos = code.location;
    this.qrcode_context.beginPath();
    this.qrcode_context.moveTo(pos.topLeftCorner.x, pos.topLeftCorner.y);
    this.qrcode_context.lineTo(pos.topRightCorner.x, pos.topRightCorner.y);
    this.qrcode_context.lineTo(pos.bottomRightCorner.x, pos.bottomRightCorner.y);
    this.qrcode_context.lineTo(pos.bottomLeftCorner.x, pos.bottomLeftCorner.y);
    this.qrcode_context.lineTo(pos.topLeftCorner.x, pos.topLeftCorner.y);
    this.qrcode_context.closePath();
    if( this.qrcode_context.isPointInPath(this.aiming_x, this.aiming_y) ){
        this.lockon = true;
        this.qrcode_context.stroke();
    }else{
        this.lockon = false;
    }
```

canvasのcontext.isPointInPath() を呼び出しているのがみそです。
指定されたX、Y座標が、直前で作ったパスの範囲内であるかどうかを確認します。

以上