Alexa Web API for Gamesで開発を始める

Joe Muoio Oct 05, 2020
Share:
Build Advanced Game Skills Multimodal Tutorial
Blog_Header_Post_Img

※このブログはGetting Started with the Alexa Web API for Gamesを翻訳したものです。

Alexa Web API for Gamesを利用すると、任意のウェブテクノロジーやツールを使って、リッチで没入感のある音声対応ゲームを作成できます。新しいAlexa Skills KitディレクティブとクライアントサイドのJavaScriptライブラリを組み合わせてゲームを作成し、Echo ShowやFire TV Cubeなどの対応デバイスでロードすることができます。このブログでは、こうした新しい強力なツールを使用して独自のAlexa対応ゲームを開発するための下準備として、このAPIの基本概念について説明します。以下のトピックを取り上げます。

  • Alexa Web API for Gamesの有効化
  • Startディレクティブの使用
  • JavaScriptの初期化
  • ホスティングソリューション
  • 双方向通信
  • デバイス機能のインターフェース
  • パフォーマンスのインターフェース
  • 読み上げイベント
  • 音声イベント

Alexa Web API for Gamesのしくみ

Alexa Web API for Gamesの動作は、その他のマルチモーダルなカスタムAlexaスキルと非常によく似ています。

A picture of the flow between the customer, Alexa services, the skill backend, asset server, and multimodal device. This is described in detail below.
Alexaスキルのフロー図(上図)

  1. ユーザーがデバイス(Alexa Web API for Gamesに対応した端末)に話しかけます。
  2. ユーザーの発話に対して、自動音声認識(ASR)と自然言語理解(NLU)が適用されます。Alexaサービスは、スキルの対話モデルを入力として使用し、スキルのバックエンドコードにリクエストをルーティングします。
  3. スキルは、Alexa Web API for Gamesのサポートを示すコンテキストが含まれたリクエストを受け取り、ウェブアプリケーションがホストされている場所のHTTPS URLを含む応答を返します。
  4. 読み上げ機能(TTS)の出力がユーザーに送信されます。デバイスでWebViewが起動します。
  5. ステップ3で定義されたHTTPS URLに、リクエストが送信されます。従来のウェブサイトと同様に、ページに含まれる各アセットに対して複数のリクエストが送信されます。
  6. ウェブアプリケーションがロードされ、ユーザーはタッチと音声で操作できます。

インターフェースを有効にする

これらのリクエストと応答の詳細を見る前に、スキルでAlexa Web API for Gamesを有効にして使用できるようにする必要があります。この作業は開発者コンソールで行います。スキルを開き、インターフェースをクリックして、Alexa Web API for Gamesをオンにします。その後、モデルをビルドを選択します。

A gif showing how to turn on the Alexa Web API for Games using the Alexa Developer Console UI.
開発者コンソールでAlexa Web API for Gamesを有効にする方法(上図)

CLIユーザーは、スキルマニフェストを変更して有効にすることができます。その場合は、manifest.apis.custom.interfacesブロックにALEXA_PRESENTATION_HTMLインターフェースを追加するだけです。

Copied to clipboard
{
    "type": "ALEXA_PRESENTATION_HTML"
}

このコードにより、Alexa Web API対応デバイスがスキルのバックエンドに適切なコンテキスト情報を送信できるようになります。

リクエストのコンテキスト

ウェブアプリケーションの起動リクエストを送信する前に、必ずAlexa.Presentation.HTMLインターフェースが設定されていることを確認してください。このインターフェースは、Alexa Presentation Language(APL)に対応したスキルで確認を行う場合と同じく、supportedInterfacesブロックに設定されています。ステップ3で、デバイスがAlexa Web API for Gamesをサポートしているかどうかをスキルが確認する際に、スキルへのリクエストエンベロープに以下のコンテキストが含まれます。

Copied to clipboard
...
    "device": {
      "deviceId": "amzn1.ask.device.XXXX",
      "supportedInterfaces": {
        "Alexa.Presentation.HTML": {
          "runtime": {
            "maxVersion": "1.0"
          }
        }
      }
    },
...

この確認は、Node.jsで次のように実装できます。

Copied to clipboard
const supportedInterfaces = Alexa.getSupportedInterfaces(handlerInput.requestEnvelope);
const htmlInterface = supportedInterfaces['Alexa.Presentation.HTML'];
if(htmlInterface !== null && htmlInterface !== undefined) {
    // Startディレクティブを追加する。
}

リクエスト元のデバイスでインターフェースがサポートされていることが確認できたら、Startディレクティブを送信できます。

Startディレクティブ

Alexa.Presentation.HTML.Startディレクティブは、ウェブアプリケーションを起動しようとしていることをAlexaサービスに通知します。このメッセージは、ウェブエクスペリエンスを起動するために、スキルのバックエンドから送信されます。簡略化して表すと、次のようになります。

Copied to clipboard
{
    type:"Alexa.Presentation.HTML.Start",
    data: {
        "arbitraryDataKey1": "任意のデータ"
    },
    request: {
        uri: "https://mywebsite.com/alexa.html"
        method: "GET"
    },
    configuration: {
        timeoutInSeconds: 300
    }
});

このStartディレクティブには、重要な部分がいくつかあります。第一に、タイプは常にAlexa.Presentation.HTML.Startです。また、HTTPSリンクを持つリクエストブロックも必要です。ここで指定されたウェブページがデバイスでロードされます。Alexa搭載デバイスでこのページを開くには、SSL証明書が有効でなければなりません。configuration.timeoutInSecondsには、ユーザーとの対話なしでウェブサイトを画面に表示しておく時間を指定します。プレイヤーからの入力がないまま持続できる時間は、最長で30分間です。dataフィールドは、ウェブアプリケーションの初回起動情報を送信するために必要です。JSONペイロードで定義されるフィールドの内容は特に定められていませんが、ウェブアプリケーションによるビジュアルエクスペリエンスの開始時に正確な状態がプレイヤーに表示されるよう、必要な情報をすべて含めてください。このディレクティブでは、認可ヘッダーを追加する機能など、ほかのオプションを設定することもできます。このディレクティブの詳細については、技術ドキュメントを参照してください。

JavaScriptの初期化

Alexa JavaScriptライブラリをロードするには、次のようにHTMLページのscriptタグにライブラリを含める必要があります。

Copied to clipboard
<head>
      <script src="https://cdn.html.games.alexa.a2z.com/alexa-html/latest/alexa-html.js"> 
      </script >
</head>

デバイスはリクエストを受け取り、ライブラリをWebViewに組み込んで、JavaScriptコードでAlexaオブジェクトを参照できるようにします。Alexaを初期化するには、以下のコードを使用します。

Copied to clipboard
var alexaClient;
Alexa.create({version: '1.0'})
    .then((args) => {
        const {
            alexa,
            message
        } = args;
       alexaClient = alexa;
        document.getElementById('debugElement').innerHTML = 'Alexaの準備ができました';
    })
    .catch(error => {
        document.getElementById('debugElement').innerHTML = 'Alexaの準備ができていません';
    });

.thenブロックが実行された場合は、Alexaクライアントサイドライブラリの初期化が完了しており、skillspeechvoicedevice capabilityの各APIが使用可能になっています。このブロックは、ローカルのゲーム状態を設定し、送信した起動データ(message変数に格納)を使用するのにも適しています。一方、catchブロックが実行された場合は、ライブラリはインスタンス化に失敗しています。この問題は、コンピューターのブラウザを使用してローカルでテストするときによく発生します。Alexaライブラリはデバイスでのみ使用できるので、ローカルでサーバーにアクセスして使用することはできません。

ホスティングソリューション

前述のとおり、デバイスでウェブアプリケーションを動作させるには、有効なSSL証明書が必要になります。これを実現する方法はいくつかありますが、以下では開発の場合と本番の場合に分けて説明します。ただし、Alexaスキルの開発とデプロイに適用できる方法をすべて網羅しているわけではありません。ご自身の開発ワークフローに適した方法がある場合は、そちらをご使用ください。

開発

開発時には、次のことができる必要があります。 1)目的のウェブアプリケーション用にアセットをキャッシュしないローカルサーバーを起動すること、2)ローカルHTTPサーバーに到達するためのHTTPSトンネルを作成することです。単純にページを保存してリロードする(この場合は、繰り返しスキルを起動する)必要があるため、これらの準備を整えておくと、デバイスで変更をすばやくテストすることができ、効率的です。

注: 最新のコードとアートアセットを使用して開発を進めるために、開発中はno-cacheヘッダーを送信することが重要です。キャッシュされたアセットが誤ってAlexa搭載デバイスに提供された場合は、ファイル名を変更(または仮のクエリパラメーターを追加)して、デバイスキャッシュを破棄する方法があります。この方法を開発時に使用する場合は、本稼働の際にこのコードを無効にしてください。

ローカルサーバーを起動するには、node.jsのhttp-serverモジュールなどのツールを使用します。
このモジュールは、サーバーをセットアップする機能と、-c-1フラグでno-cacheヘッダーを送信するオプションを備えています。たとえば、http-serverをグローバルにインストールした後、次のコマンドを実行します。

Copied to clipboard
npm install http-server -g

その後、ポート8080でサーバーを起動し、応答でno-cacheヘッダーを送信するように設定します。それには、ウェブアプリケーションのディレクトリから、次のコマンドを実行します。

Copied to clipboard
http-server . -p 8080 -c-1 

こうすると、マシンでローカルサーバーが立ち上がります。これで、127.0.0.1/alexa-game.htmlを開いてお好みのブラウザ開発ツールでテストを実行できますが、スキルのバックエンドとの通信はできません。通信ができるようにするには、ツールを使用して、Alexaデバイスから到達できるエンドポイントを作成する必要があります。これにはいくつかの方法があります。たとえばngrokというツールを使用します。そうすると、localhostにルーティングするHTTPSエンドポイントにパブリックにアクセスできるようになります。必ず、ローカルサーバー起動コマンドで指定したポートと同じポートを使用してください。たとえば、ngrokをインストールした後、次のコマンドを実行します。

Copied to clipboard
ngrok http 8080

ポートがローカルサーバーの起動時に使用したものと一致していれば、公開されたngrok URLから、ローカルで稼働しているウェブページにアクセスできます。この例については、この設定を使用してウェブアプリケーション開発を行うMy Cactus(サボテン栽培)スキルのサンプルを確認してください。

本番

ライブトラフィックで運用する場合は、起動に伴うレイテンシーの発生を回避し、アプリケーションの安定性を高めるために、開発用マシンではウェブアプリケーションや関連するアセットをホストしないでください。ストレージソリューションやコンテンツ配信ネットワークを利用すると、世界中のサーバーのうち、各プレイヤーの所在地に近いエッジロケーションで、アセットを格納およびキャッシュすることができます。このような体制を構築することで、プレイヤーのエクスペリエンスに影響を与えるレイテンシーを大幅に低減できます。その目的に適うストレージソリューションとして、私がお勧めするのはAmazon S3です。また、コンテンツ配信ネットワークとしては、世界中にエッジロケーションを持つAmazon Cloudfrontなどのご利用をお勧めします。Amazon Cloudfrontを使用する場合は、ランダムに決まる*.cloudfront.netドメインよりも、判別しやすいドメイン名を付けるとよいでしょう。ウェブアプリケーションがホストされているURLは、ユーザーのデバイスでウェブサイトを起動したときに表示されるため、必ずブランドにふさわしいドメイン名を使用してください。この点について詳しくは、これらのツールを使用した静的ウェブサイトの設定方法をAWSガイドでご確認いただくか、ASK CLI V2で実装されたCloudFormationテンプレートを介してこの設定をモデリングしているリファレンスアプリケーションをご覧ください。ここで挙げた資料は一例に過ぎませんが、ウェブサイト構築の一般的なベストプラクティスは、Alexa Web API for Gamesスキルで使用されるウェブアプリケーションにも有効です。

注: 開発中のゲームでもこうしたセットアップは使用できますが、テストのたびに変更点のあったファイルをすべてアップロードする必要があるため、開発サイクルが遅くなります。ただ、複数の開発者で取り組むプロジェクトであれば、統合的な(実稼働前の)環境を実現できるというメリットはあります。

双方向通信

デバイスで実行されているウェブアプリケーションとスキルのバックエンドとの間でメッセージを送信する機能は、Alexaスキル側でコアゲームロジックを保持できるので、非常に有用です。この機能は、スキルのバックエンドから送信されるディレクティブAlexa.Presentation.HTML.HandleMessageと2つのJavaScript API(alexaClient.skill.sendMessagealexaClient.skill.onMessage)を使用して実装されます。

Alexaスキルからローカルウェブアプリケーションへの送信

メッセージ処理ディレクティブを使用すると、スキル側のコードから任意のデータをウェブアプリケーションに送信できます。このディレクティブ自体は非常にシンプルです。

Copied to clipboard
{
    "type":"Alexa.Presentation.HTML.HandleMessage",
    "message": {
        "arbitraryDataKey1":"ArbitraryDataValue1"
    }
}

メッセージには、フロントエンドのウェブアプリケーションで使用する任意のJSONデータを使用できます。この方式では、ウェブアプリケーションと通信するための独自のプロトコルを柔軟に定義することができます。スキルのバックエンドからフロントエンドに状態情報を送信し、適切なビジュアルをレンダリングするために使用されることがよくあります。たとえば、ゲームのインテントに応じて3Dアニメーションをトリガーするといった用途です。

JavaScriptで、HandleMessageディレクティブの送信ごとに呼び出されるリスナーを登録できます。次のようにして、JavaScriptオブジェクトとして送信した、ウェブアプリケーションの動作に関するデータペイロードを確認することができます。

Copied to clipboard
alexaClient.skill.onMessage((message) => {
    console.log(JSON.stringify(message)); // {"arbitraryDataKey1":"ArbitraryDataValue1"}
});
JavaScriptウェブアプリケーションからAlexaスキルへの送信

任意のメッセージを双方向で送信できます。JavaScriptコードからスキルのバックエンドに情報を送信するには、最初にJavaScriptで関数alexa.skill.sendMessageを使用します。

Copied to clipboard
alexaClient.skill.sendMessage({
    localDataKey:"LocalDataValue"
}, messageSentCallback);

const messageSentCallback = function(sendResponse) {
    const {
        statusCode,
        reason,
        rateLimit,
    } = sendResponse;
    //次の応答コード別に処理する: 500、429、401、200
};

この関数が取るパラメーターは、任意のデータペイロードと、応答を処理するオプションのコールバック関数の2つです。このAPIの使用頻度は、1秒あたり2回に制限されています。この制限を超えると、ステータスコード429(リクエスト過多)が返されます。この制限を上回るシナリオは、待機後にメッセージを再試行して処理するとよいでしょう(待機時間の決定に使用できるAPIについては、rateLimitオブジェクトのドキュメントを参照してください)。

このJavaScript APIは、スキルのバックエンドコードにAlexa.Presentation.HTML.Messageタイプのリクエストを送信します。このリクエストは、インテントリクエストを処理するのと同様に処理できます。次のサンプルコードは、sendMessage APIを使用したシンプルなクラウドサイドロガーの例です。

Copied to clipboard
const WebAppCloudLogger = {
    canHandle(handlerInput) {
        return Alexa.getRequestType(handlerInput.requestEnvelope) === "Alexa.Presentation.HTML.Message";
    },
    handle(handlerInput) {
        const messageToLog = handlerInput.requestEnvelope.request.message;
        console.log(messageToLog);
        return handlerInput.responseBuilder
            .getResponse();
    }
}

このハンドラーは、メッセージペイロードをクラウドサイドロガーに記録するので、バックエンドでログを表示できます。ほかの例で使用されているメッセージハンドラーについては、My Cactus(サボテン栽培ゲーム)スキルのハンドラーを参照してください。

デバイスの機能

ローカルデバイスの機能APIを使用すると、ウェブアプリケーションが稼働しているデバイスの情報を確認することができます。標準のHTML環境ではアクセスできない情報も確認できます。デバイスの機能は、alexa.capabilitiesを介してアクセスします。これに含まれているmicrophoneフィールドには、次のフィールドがあります。

Copied to clipboard
{
    supportsPushToTalk: true,
    supportsWakeWord: true
}

SupportsPushToTalkは、デバイスがFire TVリモコンなどのプッシュツートーク入力をサポートしているかどうかを通知します。このデバイスを使用するユーザーは、リモコンに話しかけてAlexaにアクセスできます。もう1つのフィールドであるsupportsWakeWordは、デバイスがウェイクワード起動をサポートしている場合にtrueになります。Fire TV Cubeなどのデバイスは両方をサポートしますが、Fire TV Stick 4Kはプッシュツートークのみ、Echo Showはウェイクワードのみをサポートします。これらの機能を使用して、音声応答をバックエンドに渡して調整したり、簡潔に指示を送ったりすることができます。 

パフォーマンス

performanceインターフェースには、performance.getMemoryInfo()という関数があります。この関数は、デバイスで使用可能なメモリの情報を取得します。

Copied to clipboard
alexaClient.performance.getMemoryInfo().then((memInfo) => {
    console.log(memInfo)
});

performance.getMemoryInfo()関数は、Promiseと使用可能なメモリ容量(MB)を含むペイロードを返します。上記のコードは、次のようなログを出力します。

Copied to clipboard
{
    memory: { 
        availableMemoryInMB: 715
    }
}

注: このAPIは、開発段階でのアセットの最適化や複数のデバイスタイプでのデバッグを行う場合に有用です。デバイスのパフォーマンスに悪影響を及ぼす可能性があるので、本番環境では使用しないでください。

読み上げ

speechインターフェースを使用して、読み上げイベントに対応できます。このインターフェースには、speech.onStartedspeech.onStoppedの2つのコールバックが含まれます。以下では、簡単な例を示します。

Copied to clipboard
alexaClient.speech.onStarted(() => {
    console.log('読み上げの再生中です');
});

alexaClient.speech.onStopped(() => {
    console.log('読み上げの再生を停止しました');
});

スキルによる読み上げの開始に合わせて、既に流れていたウェブオーディオを調節することができるほか、読み上げの再生中にキャラクターが話しているアニメーションを表示し、読み上げの終了とともにアニメーションを停止することもできます。

こうした機能に関連するのは、fetchAndDemuxMP3というメソッドを持つAlexa.utils.speechクラスです。このクラスを使用して、スキルのバックエンドから出力される読み上げに含まれるaudioBufferとspeechMarksを取得できます。こうすることで、ビジュアルとウェブオーディオをスピーチマークと同期できるので、speechコールバックだけで実装した場合よりも、ビジュアルの同期を細かく調整することができます。その例を示します。

Copied to clipboard
const transformerSpeechMp3 = 'https://tinytts.amazon.com/resource.mp3';
Alexa.utils.speech.fetchAndDemuxMP3(transformerSpeechMp3).then((audioResult) => {
    const {audioBuffer, speechMarks} = audioResult;
    playAudioAndAnimateCharacterSpeech(audioBuffer, speechMarks);
});
音声

voiceインターフェースは、onMicrophoneOpenedおよびonMicrophoneClosedコールバックでデバイス上の音声入力イベントをリッスンし、対応することができます。その例を示します。

Copied to clipboard
alexaClient.voice.onMicrophoneOpened(() => {
    console.log("マイクはオンです。ウェブオーディオの音量を下げます。");
});
alexaClient.voice.onMicrophoneClosed(() => {
    console.log("マイクはオフです。通常の状態に戻ります。");
});

このインターフェースでは、マイクのオン/オフに応じて、画面を暗くする、バックグラウンド(ウェブオーディオ)の音量を下げる、キャラクターのアニメーションを表示するといった操作が可能です。またvoiceインターフェースは、ウェイクワード起動に対応したデバイスでマイクをオンにするリクエスト機能も備えています。下記はその例です。

Copied to clipboard
alexaClient.voice.requestMicrophoneOpen({
    onError: (reason) => {
        if (reason === "request-open-unsupported") {
            requestMicrophoneOpenSupported = false;
            openMicrophoneUsingSkillFallback();
        } else {
            console.log("マイクのオンに失敗:" + reason);
        }
    }
});

プレイヤーがFireTV Stick 4Kなどのデバイスを使用している場合は、ウェイクワードが存在しないので、この方法でマイクをオンにしようとすると、request-open-unsupportedとして未対応である旨が表示されます。この機能とcapabilities.microphoneインターフェースを組み合わせて、ウェイクワードの対応状況を調べて、未対応であればFire TVリモコンのプッシュツートークを促すプロンプトを画面に表示するといったフォールバックエクスペリエンスを作成することができます。画面操作やアニメーション再生後に読み上げなしでマイクをオンにするリクエストを行うなど、会話から外れたエクスペリエンスも作成できるので、このAPIはさまざまな場面で役に立ちます。

まとめ

Alexa Web API for Gamesは、新しいAlexa Skills Kitディレクティブリクエストクライアントサイドライブラリを統合した強力なAPIで、開発者にはお馴染みのウェブテクノロジーを使用して、タッチ、オーディオ、ビジュアル、音声を駆使した新しいAlexaゲームを作成することができます。双方向通信音声読み上げの各種イベントやデバイス機能といったJavaScriptツールをデバイスで活用すれば、臨場感あふれるリッチなAlexaゲームの作成も可能です。これらのAPIをゲームに適用した例を確認するには、GitHubで公開しているAlexa Web API for Gamesを使用したMy Cactus - サボテン栽培シミュレーションゲームのコードを試してみてください。このAPIを利用してゲームを作られた方は、Twitterで@JoeMoCodeまでお知らせください。

参考リンク

Subscribe