この記事は 面白法人グループ Advent Calendar 2022 の11日目の記事です
はじめに
おはこんハロチャオ!
カヤックボンド初代ポケモンチャンピオンの栗野です。
皆さん、ポケットモンスター スカーレット・バイオレット楽しんでますか?
ついに12月からランクマッチが解禁され、私もレイドバトルやオンライン対戦など
パルデア地方の冒険を楽しんでいます。
ところで皆さん、ポケモンで対戦しているときに困ったことはないですか?
あれ…自分と相手のポケモン、どっちのすばやさが高いんだ…?
あるある。とてもわかります。
私もポケモン剣盾からオンライン対戦を始めて約3年になりますが、未だに対戦中に何度も検索してます。
ポケモンのわざを選ぶまでの持ち時間は60秒。
その短い時間で様々な読み合いをするポケモンバトルでは、
すばやさをインターネッツで検索している時間が敗北に繋がると言っても過言ではないのです。
そこで今回は、私のようなポケモン対戦初心者~中級者向けに、
「Alexaに話しかけるとポケモンのすばやさを返してくれるアプリ」
を作ってみました。
スマートスピーカー自体が下火な感じですが、
去年のブラックフライデーセールにて3000円ほどで購入し、
我が家で日々健気に明日の天気を教えてくれる
Amazon Echo Dot に、
Alexa Skillを自作して新たな力を授けます。
前段1:Alexaとは
Alexa とは
Amazonが出している音声アシスタントサービスのこと。
Alexaスキルとは
Alexaの機能を拡張するアプリのようなもの。
開発用のSDKも出ていて、ユーザーが自作することもできます。
Alexa Developer Console という開発者向けページも用意されていて、
開発、ビルド、テスト、ストアへの公開まで全てブラウザ上で出来ちゃいます。
今回はこちらを使って開発していきます。
実はノーコードでもAlexaスキルが作れちゃう
簡単なクイズや朗読、ちょっとしたミニゲームであれば、
テンプレートに沿ってポチポチしていくだけでSkillが作れちゃう
Alexaブループリントというサービスも用意されてます
blueprints.amazon.co.jp
前段2:ポケモンのすばやさとは
今回求めるポケモンのすばやさについて軽く触れます。
ポケモンのすばやさとは、どちらのポケモンが先に攻撃するかを決めるステータスです。
値が1違うだけで先行と後攻が変わるため、ポケモン対戦では非常に重要なステータスとなります。
ポケモンのステータス(実数値)を算出する計算式は
能力値=(種族値×2+個体値+努力値÷4)×レベル÷100+レベル+10
式から分かるように、ステータスは
・ポケモン毎の特徴(種族値、個体値)
・トレーナーの育て方(努力値)
で大きく変わります。人間と一緒ですね。
今回はポケモンの種族値に加え、対戦環境でよく使われる
・最速(個体値V+努力値MAX振り+性格補正)
・準速(個体値V+努力値MAX振り)
・無振り(個体値Vのみ)
の値を返してくれる機能を作っていきます。
本記事では、計算に使われる各値についての詳しい説明は割愛します。本当は話したい。
ポケモン徹底攻略さんの説明がわかりやすいです。興味ある方はぜひ。
本題:実装
ようやく本題。
公式のスキル作成のトレーニングを見つつ、HelloWorldのサンプルプロジェクトをあれこれ弄っていきます。
※今回はNode.jsで作ります(Pythonでも作れます)
スキルの呼び出し名(Invocation name)
Developer Consoleからスキルの呼び出し名を決めます。
「Alexa、〇〇を開いて」でスキルを呼び出す事が出来るようになります。
インテント(Intent)の作成
インテントを作成します。(左メニュー > 対話モデル > インテント)
インテントとは、そのスキルに対するユーザーの意図、もしくは実行できるアクションの定義のようなものです
サンプル発話に「ユーザーがこうやって話しかける」というデータを登録します。
{PokemonName}にはポケモンの名前が入ります。(Alexaではスロットと言います。)
このスロットを変数として、メッセージの判定処理に利用します。
カスタムスロットタイプの設定
スロットの定義を行います。(左メニュー > アセット > スロットタイプ)
今回はポケモンの名前を{PokemonName}というスロットで取り扱うため、
どういった値が{PokemonName}に入ってくるか、スロットの定義を行います。
パルデア地方のポケモン400匹(フォルム違いなども含め合計406)の名前を登録しました。
コーディング
コードエディタタブから、コーディングしていきます。
今回はNode.jsで書きますが、書く量は少なく、Node.js詳しくないよって人でも大丈夫です。
LaunchRequestHandler
LaunchRequestHandler はスキル呼び出しにキックされるイベントです。
const LaunchRequestHandler = { canHandle(handlerInput) { return Alexa.getRequestType(handlerInput.requestEnvelope) === 'LaunchRequest'; }, async handle(handlerInput) { const speakOutput = '私はすばやさ博士です。どのポケモンのすばやさについて知りたいですか?'; return handlerInput.responseBuilder .speak(speakOutput) .reprompt(speakOutput) .getResponse(); } };
PokemonNameIntentHandler(メイン処理)
次にメインの処理である、PokemonNameIntentがキックされた時の処理を書きます。
今回は時間の都合上、DynamoDBやS3に保存したポケモンのデータを引っ張ってくる、といった
ところまで作れなかったので、speedDataという連想配列を内部で定義してます。
パラメータとして受け取ったポケモンの名前から種族値を検索→各値を計算して返す、という流れです。
今回は、オンラインの対戦環境を考慮して以下のルールで計算してます。
・個体値は最大値の31
・準速以上の努力値は実数値として効果のある最大値252で計算
・ランクマッチ対戦ではポケモンのレベルは50固定のため、50で計算
※努力値は252÷4=63で計算してます。性格補正は1.1倍です。
const PokemonNameIntentHandler = { canHandle(handlerInput) { return Alexa.getRequestType(handlerInput.requestEnvelope) === 'IntentRequest' && Alexa.getIntentName(handlerInput.requestEnvelope) === 'PokemonNameIntent'; }, handle(handlerInput) { let speakOutput = `パルデア地方にはいないポケモンのようです`; const pokemonName = handlerInput.requestEnvelope.request.intent.slots.PokemonName.value; if(pokemonName in speedData){ const raceValue = speedData[pokemonName]; // 種族値 const defaultSpeed = parseInt((raceValue * 2 + 31) * 50 / 100 + 5); // 無振り const secondSpeed = parseInt((raceValue * 2 + 31 + 63) * 50 / 100 + 5); // 準速 const topSpeed = parseInt(secondSpeed * 1.1); // 最速 speakOutput = `${pokemonName} のすばやさ種族値は ${raceValue} 。実数値は、最速 ${topSpeed}。準速 ${secondSpeed} 。無振り ${defaultSpeed} です。`; } return handlerInput.responseBuilder .speak(speakOutput) .reprompt(speakOutput) .getResponse(); } };
IntentHandlerのインポート
index.jsの下部にIntentHandlerのインポート箇所があるため、忘れずに追加しておきましょう。
exports.handler = Alexa.SkillBuilders.custom() .addRequestHandlers( LaunchRequestHandler, //HelloWorldIntentHandler, PokemonNameIntentHandler, // 追加 HelpIntentHandler, CancelAndStopIntentHandler, FallbackIntentHandler, SessionEndedRequestHandler, IntentReflectorHandler) .addErrorHandlers( ErrorHandler) .withCustomUserAgent('sample/hello-world/v1.2') .lambda();
テスト!
ここまで出来たら「コードエディタ」タブ内のデプロイボタンを押しましょう!
AWS Lambdaの関数として、いい感じに色々やってくれます。
AWS Lambdaは月100万リクエストまで無料なので、お財布にも優しいですね。
実装が出来たので、いよいよテストです。
Developer Consoleでのテスト
なんとブラウザ上でテストまで出来ちゃいます。
PCにマイクを繋げば音声入力でのチェックも出来るので、実機が無い場合でも安心です。
いざ実機
チュートリアルの案内に沿って、Web版Alexaアプリから、開発したSkillが登録されているかチェックします。
このウェブサイトは、すべてのAmazonデバイスとAlexaの機能に対応しているわけではありません。機能はこれからも引き続き削減されます。すべての機能にアクセスするには、iOSアプリストアまたはGoogle Playストアから最新バージョンのAlexaアプリをダウンロードしてください。
スキルの項目が無くなっている…。やっぱりスマートスピーカー関連サービスは縮小傾向なんでしょうか。
仕方がないのでiOS版のAlexaアプリをダウンロードし、確認します。
いよいよ実機確認です。
イヤッタアアアアアアアアア!!!
これでオンライン対戦が捗りますね。
スマホでダメージ計算しながらすばやさ確認ができる(´ε` )
感想
今回思い切ってAdvent Calendarに参加しましたが、
最近は業務でコーディングをする機会がめっぽう減っており、
ガチャガチャと何かを作るのは楽しい!という気持ちを再確認する良い機会となりました。
それでは皆さま、良きポケモンライフを!
余談
余談その1
今回の開発中にたまたま知ったんですが、
実はAlexaにはデフォルトでポケモンの種族値を返す機能が実装されてるみたいです。
剣盾までのポケモンしか登録されてないようですが、
「Alexa、マルマインのすばやさを教えて」
といった感じで話しかけると種族値を教えてくれます。便利すぎるぜAlexa。。。
余談その2
以前、弊社CTOの駒田さんに貰ったGoogle Nest Miniを使って何かアプリを作りたかったけど、
Action on Google(GoogleHomeのアプリ作るサービス)が2023年6月に終了するとのことで断念しました。