Table of Contents
スキル名
アレモンスキル
Alexa Monstarを略して、アレモンにしました。
完成品
途中エラーが出る箇所がありますが、なんとかジムリーダーに挑戦することができました。
使用技術
- Alexa Developer Console
- Node.js
各会話でやってること
LaunchRequestHandler
最初にスキルを立ち上げた際に、発火するHandlerです。
const LaunchRequestHandler = {
canHandle(handlerInput) {
return Alexa.getRequestType(handlerInput.requestEnvelope) === 'LaunchRequest';
},
async handle(handlerInput) {
// DynamoDBから永続化データを取得
const data = await handlerInput.attributesManager.getPersistentAttributes()
let speakOutput
// ユーザー名が取得できた時の処理
if (data.username) {
let alemonText = ''
data.alemons.forEach((alemon) => {
alemonText += alemon.name + ','
})
speakOutput = `
${data.username}、おかえりなさい!
${data.username}が持っているアレモンは
${alemonText}だよ。
ゲームを再開する時は、再開すると言ってね。
ゲームを最初からやり直す時は、やり直すと言ってね。
`
} else {
speakOutput = `
アレモンスキルへようこそ。このスキルではアレクサモンスターを育てて、バトルすることができるよ。
新しい<prosody pitch="low">ア</prosody>レモンをゲットして、ジムリーダーに挑戦しよう!
アレモン達のHPがたくさん残っている人が勝ちだよ。
まずは君の名前を教えてね
`
repromptOutput = `君の名前を教えてね`
}
return handlerInput.responseBuilder
.speak(speakOutput)
.getResponse();
}
};
ここでは、最初にDynamoDBにあるデータを取得しています。 ユーザー名が保存されている場合は、「おかえりなさい」と返答してくれ、既に所持しているモンスターを列挙してくれます。
ユーザー名が保存されておらず、新規プレイの場合は、名前を聞きます。 名前を答えると、その名前が保存されるHandlerが発火するようになっています。
SearchAlemonIntentHandler
次に、アクションとして「アレモンを探す」を選択した場合の処理になります。
const SearchAlemonIntentHandler = {
canHandle(handlerInput) {
return Alexa.getRequestType(handlerInput.requestEnvelope) === 'IntentRequest'
&& Alexa.getIntentName(handlerInput.requestEnvelope) === 'SearchAlemonIntent';
},
async handle(handlerInput) {
const data = await handlerInput.attributesManager.getPersistentAttributes()
const nameArray = [
'チュウ', 'ダイル', 'キング', 'ックス', 'ゴン', 'ライク'
]
const level = Math.ceil(Math.random()*10)
const enemy = {
level: level,
name: 'アレ' + nameArray[Math.floor(Math.random() * nameArray.length)],
hp: 8 * level,
attack: Math.ceil(Math.random() * level * 2)
}
const speakOutput = `
野生の${enemy.name}が現れた!レベルは<say-as interpret-as="cardinal">${enemy.level}</say-as>だ!
どのアクションを実行する?
たたかう<break time="1s"/>
モンスターボールを使う<break time="1s"/>
逃げる
`;
const attributes = {
username: data.username,
alemons: data.alemons,
items: data.items,
enemy: enemy
}
handlerInput.attributesManager.setPersistentAttributes(attributes); // セット
await handlerInput.attributesManager.savePersistentAttributes(); // 保存
return handlerInput.responseBuilder
.speak(speakOutput)
.getResponse();
}
}
敵の名前は、'アレチュウ', 'アレダイル', 'アレキング', 'アレックス', 'アレゴン', 'アレライク'の中からランダムで決まります。
レベルも1~10までのレベルからランダムに決まり、レベルが高いほどHPと攻撃力が高くなるように、出現するモンスターを定義しています。
この際にDynamoDBに敵のモンスターを保存します。
敵が出現すると、
- たたかう
- モンスターボールを投げる
- 逃げる
の三択から、次のアクションを選ぶように、促します。
BattleIntentHandler
最後に、アレモンに遭遇した際の処理です。
const BattleIntentHandler = {
canHandle(handlerInput) {
return Alexa.getRequestType(handlerInput.requestEnvelope) === 'IntentRequest'
&& Alexa.getIntentName(handlerInput.requestEnvelope) === 'BattleIntent';
},
async handle(handlerInput) {
const action = handlerInput.requestEnvelope.request.intent.slots.Battle.resolutions.resolutionsPerAuthority[0].values[0].value.name
const data = await handlerInput.attributesManager.getPersistentAttributes()
let lastEnemy = null
// ジムリーダー戦の場合
if (data.enemy.name === 'アレリュー') {
lastEnemy = data.enemy
}
let speakOutput = ''
switch (action) {
case 'たたかう':
speakOutput = await tatakauAction(handlerInput, lastEnemy)
break;
case 'モンスターボールを使う':
if (lastEnemy) {
speakOutput = 'たたかうか逃げるを選んでね'
} else {
speakOutput = await ballAction(handlerInput)
}
break;
case '逃げる':
speakOutput = await runAction(handlerInput, lastEnemy)
break;
}
return handlerInput.responseBuilder
.speak(speakOutput)
.getResponse();
}
}
const action = handlerInput.requestEnvelope.request.intent.slots.Battle.resolutions.resolutionsPerAuthority[0].values[0].value.name
ここでは、ユーザーが発したアクションの中から、スロットに登録してあるアクションを取得します。 スロットには
- たたかう
- モンスターボールを使う
- 逃げる
の三種類が登録されています。
tatakauActionの中では、所持するアレモンのレベルに応じて、相手のモンスターにダメージを与え、 相手のモンスターが生きていれば、反撃される、という処理を書いています。
処理が長くなってしまうので、ここでは割愛します。
作ってみて、つまづいたとこ・うまくいってないところ
人の名前問題
これに一番悩んでて、まだ解決してません、、、笑
最初にユーザーの名前を聞く箇所があるのですが、そこのSlotにAmazon.FirstNameを使用しています。
ただ、このFirstNameの種類が少ないのか、ほとんどの人の名前をうまく認識してくれません。。
ここの解決策、もしわかる方いれば是非コメントください!
ゲーム性をもたせることの難しさ
初めてAlexaでゲームを作ったのですが、そもそもゲームを作る上で、ちょうどいい難易度に調整することが大変でした。
ただ、少しずつ調整して、ちょうどいい難易度にしていくのは楽しいですね。
音声UI設計の難しさ
Alexaという端末の問題から、パラメーターの記憶が難しいことも一つ音声UIの問題かなと思いました。 今回は対策として、「ステータスを確認」と言えば、いつでも持っているモンスターのリストがわかるように対応しました。
作る上で助かったもの
attributesManagerが便利
Alexa SDK ver.2を初めてしっかり使ったのですが、DynamoDBの値を管理する上でattributeManagerはとても便利でした。
以前はlambdaから直接DynamoのAPIを叩いていたのですが、それよりもコードがシンプルになって、書きやすかったです。
Alexa道場
実装の上でわからなかった部分は、Alexa道場の動画を参考にさせてもらいました。
畠中さんのわかりやすい説明、とても助かりました。ありがとうございました。
まとめ
以上、Alexaでポ○モン風ゲームをつくった話でした。
Alexaの開発環境、どんどん進化していってるなと感じました。
是非興味ある方、作ってみてください!