HAMADAの語り草

興味のある技術のアウトプットをしたいと思います

Joy-conでLT用のスライドリモコンを作った話

 

はじめに

こんにちは!会津大学学部二年のHAMADAです。

今回は、Joy-conを使ったLT用のスライドリモコンを作った話を書いていきます。

 

動機・目的

部内のLTのネタを考えていて、元々はラズパイで遊ぼうとしていました。しかしパーツが何もないので、何かないかと探していたところ、Joy-conを見つけたので遊んでみました。

CEOとかがやっている、歩き回りながらスライド発表をするのに憧れがあったので、題材はスライドリモコンに決定しました。

 

開発準備

使用したもの
  • Chorme

普段はSafariを使っているのですが、APIが一部使用できなかったのでChromeを使いました

その名の通りGoogleのスライド。Macであれば、command + option + I で開発者モードにしてプログラムをはっ付けてEnter!!

  • GamepadAPI

コントローラなどを接続して、あれこれと状態を読み取って、イベントを追加してくれるAPI

みなさんご存知Nintendo Switchのコントローラの右の方

 

実装内容について

Joy-conについて

Joy-conは便利なことに、Bluetoothで簡単にパソコンと繋げることができます。

Joy-conBluetooth接続

Joystick Mapperという非常に便利なものがあるらしいのですが、今回の趣旨とは違うので使いません。

実際のプログラム

プログラムの全容は以下のようになっています。

((document, navigator, addEventListener) => {
    //デバイスの識別
    const VENDOR_ID = '57e';
    const DEVICE_ID = '2007';
    //ボタンの番号
    const Y_BUTTON = 3;
    const A_BUTTON = 0;
    const ZR_BUTTON = 7;
    //ボタンに割り当てるキーボード
    const LEFT_ARROW_KEY = 'ArrowLeft';
    const LEFT_ARROW_KEY_CODE = 37;
    const RIGHT_ARROW_KEY = 'ArrowRight';
    const RIGHT_ARROW_KEY_CODE = 39;
    const ESC_KEY = 'Esc';
    const ESC_KEY_CODE = 27;

    //ブラウザのドキュメントがIFRAMEかどうかを判断する
    const pressKey = (key, keyCode) => {
        const activeElement = document.activeElement;
        const targetDocument = activeElement.tagName === 'IFRAME' ? activeElement.contentDocument : document;
        ['keydown', 'keyup'].forEach(typeArg => {
            targetDocument.body.dispatchEvent(new KeyboardEvent(typeArg, { key, keyCode, bubbles: true }));
        });
    };

    let gamepadIndex, intervalID;

    addEventListener('gamepadconnected', ({ gamepad }) => {
        if (gamepadIndex != null || !gamepad.id.includes(VENDOR_ID) || !gamepad.id.includes(DEVICE_ID)) {
            return;
        }
        gamepadIndex = gamepad.index;

        let isPressing = false;
        intervalID = setInterval(() => {
            isPressing = (gamepad => {
                const buttons = gamepad.buttons;
                if (buttons[Y_BUTTON].pressed) {
                    console.log('Yが押されました');
                    !isPressing && pressKey(LEFT_ARROW_KEY, LEFT_ARROW_KEY_CODE);
                    return true;
                }
                else if (buttons[A_BUTTON].pressed) {
                    console.log('Aが押されました');
                    !isPressing && pressKey(RIGHT_ARROW_KEY, RIGHT_ARROW_KEY_CODE);
                    return true;
                }
                else if (buttons[ZR_BUTTON].pressed) {
                    console.log('ZRが押されました');
                    !isPressing && pressKey(ESC_KEY, ESC_KEY_CODE);
                    return true;
                }
                return false;
            })(navigator.getGamepads()[gamepadIndex]);
        }, 1000 / 60);
    });
    addEventListener('gamepaddisconnected', e => {
        if (gamepadIndex === e.gamepad.index) {
            clearInterval(intervalID);
            gamepadIndex = intervalID = null;
        }
    });

    if (navigator.wakeLock) {
        const requestWakeLock = isFirstRequest => {
            if (document.visibilityState !== 'visible') {
                return;
            }
            navigator.wakeLock.request('screen').then(() => {
                if (isFirstRequest) {
                    document.addEventListener('visibilitychange', requestWakeLock);
                    document.addEventListener('fullscreenchange', requestWakeLock);
                }
            }).catch(() => { });
        };
        requestWakeLock(true);
    }
})(document, navigator, addEventListener);

プログラムの説明

Joy-con(R)であることを識別させる

    //デバイスの識別
    const VENDOR_ID = '57e';
    const DEVICE_ID = '2007';

ベンダーIDは、企業の識別。デバイスIDは製品の識別をしています。

 

ボタンの割り当てをする

    //ボタンの番号
    const Y_BUTTON = 3;
    const A_BUTTON = 0;
    const ZR_BUTTON = 7;

各デバイスのボタンには数字が割り当てられています。

今回はA Y ZR の三つを使用した。 A は 0, Y は 3, ZR は 7でした。

 

ボタンに割り当てるキーボード操作

    //ボタンに割り当てるキーボード操作
    const LEFT_ARROW_KEY = 'ArrowLeft';
    const LEFT_ARROW_KEY_CODE = 37;
    const RIGHT_ARROW_KEY = 'ArrowRight';
    const RIGHT_ARROW_KEY_CODE = 39;
    const ESC_KEY = 'Esc';
    const ESC_KEY_CODE = 27;

パソコンのキーボードの操作をボタンと紐付けます。 今回欲しい操作は、「進む」「戻る」「全画面終了」の3つ。 「進む」は右矢印、「戻る」は左矢印、「全画面終了」はesc。

この3つを、キーコードを使ってJoy-conに割り当てます。

Aボタンに右矢印、 Bボタンに左矢印、ZRにescを割り当てます。

キーコードはそれぞれ、右矢印は39、左矢印は37、escは27です。

 

GamePadAPIについて

今回はGamePadAPIというAPIを使いましたが、その中のaddEventListenerについて、簡単に解説しようと思います。

 

addEventListenerについて

・構文

addEventListener(type, listener)

typeは対象となるイベントの種類を表す文字列

listenerは指定されたイベントの発生の通知を受け取るオブジェクト javascriptの関数

 

typeは今回 'gamepadconnected'

このイベントは、ゲームパッドが接続されたことをブラウザが検出したとき、またはゲームパッドのボタン/軸が初めて使用されたときに発生します。

listenerは今回、対応したボタンを押した際に起こる動作を関数として実装されています。

 

setInterval()について

・構文

setInterval(func, delay)

定の遅延間隔を置いて関数などを繰り返し呼び出す。


ClearInterval()について

・構文

ClearInterval(Interval)

以前にSetInterval()によって確立されたタイマーを利用した繰り返し動作を取り消す。

 

WakeLockについて

・アプリケーションが動作し続ける必要がある時に画面が暗くなったりロックされたりするのを防ぐもの

 

以上が簡素ではありますが、プログラムの説明です。

 

では実際に動かしてみましょう。

 

動作確認

こんな感じに動きます。

 

 

感想と展望

APIを触るのは、何気に初めてだったのでとても面白かったです。今後マウスと同期するようなこともできたら面白いなぁと思いました。何かの参考になれば幸いです。ここまでお付き合いありがとうございました!!!

 

参考リンク

developer.mozilla.org

qiita.com

所属サークル

A-PxL (@aizu_PxL) / Twitter

私のTwitter

HAMADA (@AHMOS_HMD) / Twitter