資料介紹
描述
概括
Speed Tap 是一款適用于 Amazon Alexa 的游戲,它使用 Echo Buttons 來(lái)測(cè)試玩家的反應(yīng)時(shí)間和注意力。該按鈕會(huì)在隨機(jī)顏色之間循環(huán),玩家必須在按鈕為綠色時(shí)點(diǎn)擊該按鈕。隨著游戲的進(jìn)行,燈光速度加快,難度也越來(lái)越大。
該游戲結(jié)合了許多Alexa API和AWS 服務(wù)來(lái)創(chuàng)造引人入勝的體驗(yàn),同時(shí)保持簡(jiǎn)單的游戲玩法。這個(gè)故事將重點(diǎn)解釋為什么需要這些以及它們的大致工作原理,以便其他開(kāi)發(fā)人員了解什么是可能的以及如何進(jìn)行。
這項(xiàng)技能是使用 Node、AWS 服務(wù)和alexa-app框架從頭開(kāi)始構(gòu)建的。
關(guān)鍵概念和獨(dú)特功能
從較高的層面來(lái)看,這些是使這款游戲無(wú)論是作為 Alexa Skill 還是從開(kāi)發(fā)/編程的角度來(lái)看都獨(dú)一無(wú)二的東西:
- 玩家與所有 Alexa 用戶競(jìng)爭(zhēng)以獲得世界紀(jì)錄,這使得它比僅僅與自己對(duì)戰(zhàn)更有趣和互動(dòng)。
- AlexaSpeedTap.com的集成網(wǎng)站具有一個(gè)自動(dòng)更新的排行榜,顯示每個(gè)玩家的高分。或者,玩家可以通過(guò)使用 Alexa API 的游戲本身在排行榜上無(wú)縫連接他們的真實(shí)姓名。
- In-Skill-Purchases允許玩家購(gòu)買(mǎi)額外的生命以在他們錯(cuò)過(guò)后繼續(xù)游戲。
- Alexa 聲音庫(kù)和隨機(jī)短語(yǔ)Speechcons(感嘆詞)用于使體驗(yàn)更加引人入勝和生動(dòng),并賦予其一些個(gè)性。孩子們特別喜歡它。
架構(gòu)和 AWS 服務(wù)

拉姆達(dá)
游戲的所有代碼都在 AWS Lambda 中。它是使用 alexa-app 框架在 Node.js 中編寫(xiě)的。Alexa 事件(玩游戲的用戶)和 CloudWatch Events 使用相同的 lambda 函數(shù)代碼,這會(huì)觸發(fā)后臺(tái)任務(wù)來(lái)更新排行榜。
動(dòng)態(tài)數(shù)據(jù)庫(kù)
個(gè)人球員和全球世界紀(jì)錄統(tǒng)計(jì)數(shù)據(jù)都存儲(chǔ)在 DynamoDB 中。對(duì) DDB 的訪問(wèn)是使用 Amazon 的 aws-sdk 包完成的,我在其頂部編寫(xiě)了一個(gè)自定義層以簡(jiǎn)化 API 并在現(xiàn)有的 Promise 接口上提供一個(gè)異步/等待接口。
每次交互時(shí),用戶數(shù)據(jù)和狀態(tài)都會(huì)保存到 DDB,從而使游戲能夠以隨著時(shí)間的推移和適當(dāng)?shù)姆绞脚c用戶進(jìn)行交互(請(qǐng)參閱下面的用戶體驗(yàn)部分)。
S3
Lambda 函數(shù)每 5 分鐘生成一個(gè)包含所有高分和當(dāng)前世界紀(jì)錄數(shù)據(jù)的 JSON 文件,并將其存儲(chǔ)在公共 S3 存儲(chǔ)桶中。然后通過(guò) AJAX 從網(wǎng)站加載此 JSON 文件以顯示排行榜并以大文本宣布當(dāng)前的記錄保持者。
后臺(tái)任務(wù) - CloudWatch Events
需要一個(gè)“后臺(tái)任務(wù)”來(lái)定期更新包含網(wǎng)站數(shù)據(jù)的 JSON 文件。由于無(wú)法按計(jì)劃觸發(fā) Lambda 函數(shù),因此使用 CloudWatch Events 觸發(fā)觸發(fā) Lambda 函數(shù)的事件。
然后,Lambda 函數(shù)代碼會(huì)檢測(cè)它是由 Alexa 還是 CloudWatch Events 觸發(fā)的,并做出相應(yīng)的行為。
ALEXA API 和功能
許多 Alexa API 和功能被用來(lái)創(chuàng)建這個(gè)游戲。以下是使用的內(nèi)容和原因的摘要。
小工具API
Gadgets API需要與 Echo Buttons 交互,實(shí)際上有兩種風(fēng)格,它們都必須聲明為技能所需的接口。
GAME_ENGINE是用于監(jiān)聽(tīng) Echo Buttons 事件的接口。
GADGET_CONTROLLER是用于實(shí)際設(shè)置按鈕本身的燈光模式的接口。
這是兩個(gè)不同的 API,要讓它們無(wú)縫地協(xié)同工作,一開(kāi)始理解起來(lái)有點(diǎn)棘手。仔細(xì)閱讀文檔、了解每個(gè)文檔的局限性并在設(shè)計(jì)您的體驗(yàn)時(shí)了解什么是可能的、什么是不可能的,這一點(diǎn)很重要。
客戶資料 API
Customer Profile API允許技能請(qǐng)求有關(guān)用戶的信息,例如姓名、電子郵件地址和電話號(hào)碼。使用時(shí),該技能會(huì)在用戶的 Alexa 應(yīng)用程序中向用戶出示一張卡片以授予權(quán)限,然后才能訪問(wèn)此信息。如果用戶請(qǐng)求,Speed Tap 使用此 API 獲取用戶名以顯示在網(wǎng)站的排行榜上。
技能購(gòu)買(mǎi)
In-Skill Purchases (ISP) API允許技能請(qǐng)求用戶使用真錢(qián)進(jìn)行實(shí)際購(gòu)買(mǎi),以解鎖技能中的內(nèi)容或功能。Speed Tap 使用“消耗品”ISP (CISP),這是可以購(gòu)買(mǎi)和用完的物品,而不是一次性購(gòu)買(mǎi)和訂閱,它們不會(huì)在使用時(shí)消失。
Speed Tap 使用戶能夠在點(diǎn)擊錯(cuò)誤的顏色后繼續(xù)。每個(gè)新用戶都會(huì)獲得 5 條額外的生命,當(dāng)他們離開(kāi)后,他們可以選擇使用 CISP 再購(gòu)買(mǎi) 10 條生命。
展示
Speed Tap 利用顯示接口 API與 Echo Show 和 Echo Spot 設(shè)備上的顯示功能進(jìn)行交互,方法是在游戲開(kāi)始時(shí)顯示彩色“啟動(dòng)畫(huà)面”,并在每次按下按鈕時(shí)顯示當(dāng)前回合。
隨著帶有顯示器的 Echo 設(shè)備變得越來(lái)越普遍,技能通過(guò)視覺(jué)提示和附加信息來(lái)補(bǔ)充其音頻輸出非常重要。
聲音庫(kù)
Alexa Skills Kit 聲音庫(kù)是可用于任何技能的音頻片段集合。這些音頻文件由亞馬遜存儲(chǔ),可以插入到任何響應(yīng)中,而無(wú)需技能作者創(chuàng)建、存儲(chǔ)或交付它們。例如:
src
='soundbank://soundlibrary/ui/gameshow/amzn_ui_sfx_gameshow_countdown_loop_32s_full_01'/>
隨著用于多種用途的聲音樣本的選擇越來(lái)越多,這個(gè)庫(kù)為技能作者提供了一種簡(jiǎn)單的方法來(lái)為他們的技能添加一點(diǎn)額外的個(gè)性和樂(lè)趣。Speed Tap 在等待用戶按下按鈕以增加戲劇性和張力時(shí)將這些聲音用于音樂(lè),以及用于積極和消極事件。
演講者
Alexa 語(yǔ)音輸出可以比默認(rèn)的文本轉(zhuǎn)語(yǔ)音更具表現(xiàn)力的方式說(shuō)出某些詞。這些“ Speechcons ”僅限于每種支持語(yǔ)言的一組特定單詞,并使用標(biāo)記插入到您的 SSML 響應(yīng)中:
<say-as interpret-as="interjection">abracadabra!say-as>
Speed Tap 使用 Speechcons 響應(yīng)每個(gè)按鈕按下,讓游戲?qū)τ脩魜?lái)說(shuō)更有趣,并增加一點(diǎn)多樣性。玩游戲的孩子特別喜歡有趣的輸出。
Alexa 技能活動(dòng)
技能事件API允許技能響應(yīng)更改或事件,而無(wú)需用戶直接與技能交互。例如,當(dāng)用戶啟用或禁用技能時(shí),可以通知該技能。
當(dāng)用戶使用手機(jī)上的 Alexa 應(yīng)用程序啟用或禁用對(duì)其客戶資料信息的訪問(wèn)時(shí),Speed Tap 使用 Skill Events API 立即做出響應(yīng)。收到此事件后,Speed Tap 會(huì)立即調(diào)用 API 獲取用戶姓名,因此無(wú)需用戶再次玩游戲即可更新排行榜。
用戶體驗(yàn)
語(yǔ)音應(yīng)用程序的用戶體驗(yàn)至關(guān)重要且微妙。與網(wǎng)絡(luò)和移動(dòng)等顯示媒體不同,音頻需要更長(zhǎng)的時(shí)間來(lái)呈現(xiàn)信息,并且用戶無(wú)法處理選項(xiàng)列表或掃描屏幕以了解他們下一步想做什么。
Speed Tap 實(shí)施了許多改進(jìn)用戶體驗(yàn)的方法,這些方法雖然不那么明顯,但卻產(chǎn)生了很大的不同。
共享的全球經(jīng)驗(yàn)
與移動(dòng)應(yīng)用程序不同,大多數(shù) Alexa 游戲都是單人游戲,不與其他玩家社區(qū)互動(dòng)。用戶是孤立的,通常玩游戲是為了個(gè)人高分或自己探索游戲。
但是……和別人一起玩游戲更有趣!
Speed Tap 朝著這個(gè)方向邁出了獨(dú)特的一步,讓玩家可以看到他們的分?jǐn)?shù)與其他人的比較情況。網(wǎng)站上的排行榜列出了每個(gè)玩家的個(gè)人最高分以及他們用了多少次連續(xù)得分來(lái)達(dá)到這一目標(biāo)。添加你的真實(shí)姓名的能力讓你有吹牛的權(quán)利,看看你是否在打敗你的朋友。
玩家知道什么與實(shí)際上是什么?
為用戶提供相關(guān)信息的一個(gè)重要部分是跟蹤他們所知道的,而不是實(shí)際情況。
在 Speed Tap 中,這與世界紀(jì)錄有關(guān)。當(dāng)用戶玩游戲時(shí),他們可能會(huì)被告知當(dāng)前的世界紀(jì)錄是 25 次點(diǎn)擊。但下次他們比賽時(shí),世界紀(jì)錄可能會(huì)增加到 30,他們很想知道。
懶人技能只會(huì)在用戶每次開(kāi)始游戲時(shí)告訴用戶世界紀(jì)錄。但為了獲得更好的體驗(yàn),該技能需要知道用戶上次被告知的世界紀(jì)錄是什么,并且只有在更改時(shí)才告訴他們。通過(guò)這種方式,他們可以獲得他們想要的信息,但如果相同,則不必每次都被迫忍受關(guān)于當(dāng)前世界紀(jì)錄的相同句子。
Speed Tap 通過(guò)保留兩個(gè)獨(dú)立的數(shù)據(jù)區(qū)域來(lái)跟蹤此類(lèi)事件。一個(gè)保存當(dāng)前的真實(shí)信息(例如當(dāng)前的世界紀(jì)錄和持有者),而另一個(gè)保存相同的信息,但處于用戶最后知道的狀態(tài)。然后將這些進(jìn)行比較以確定應(yīng)該告訴用戶什么。代碼如下所示:
sayif `Your high score is ${user.high_score} `;
// Check to see if there is a new world record to inform the user about
if (game.world_record) {
if (user.world_record && user.world_record < game.world_record) {
say `and there is a new world record. The highest score is now ${game.world_record}`;
}
else if (game.world_record_user && game.world_record_user===request.data.session.userId) {
say `and you still hold the world record`;
}
else {
say `and the world record is ${game.world_record}`;
}
user.world_record = game.world_record;
}
增量體驗(yàn)/會(huì)話跟蹤
由于用戶會(huì)多次玩游戲,因此不應(yīng)每次都向他們展示冗長(zhǎng)的音頻介紹。當(dāng)他們多次使用特性時(shí),不應(yīng)該要求他們?nèi)淌軐?duì)他們已經(jīng)理解的東西的解釋。
出于這個(gè)原因,跟蹤用戶與技能交互時(shí)的體驗(yàn)對(duì)于技能來(lái)說(shuō)很重要,這樣技能就知道何時(shí)可以縮短交互。這正是人類(lèi)所做的,通過(guò)這樣做,一項(xiàng)技能似乎更自然。
Speed Tap 為每個(gè)用戶保留一個(gè)“體驗(yàn)”對(duì)象,該對(duì)象隨依賴(lài)于它的每種交互類(lèi)型進(jìn)行更新。然后,根據(jù)用戶當(dāng)前的體驗(yàn)水平更改輸出。下面是一些示例代碼:
// Open with a more different intro depending on how often the user has played
if (request.experience("session_count",false) <= 1) {
say `Welcome to ${speedtap}. This is a game of quick reactions and concentration. Would you like to hear a quick explanation of how to play?`;
}
else {
if (request.experience("session_count",false) < 4) {
say `Welcome back to ${speedtap}.`;
}
else {
response.say(`Welcome back.`);
}
}
experience 對(duì)象還用于確定是否應(yīng)該給出有關(guān)排行榜如何工作、所需權(quán)限等的解釋。
隨機(jī)輸出
一遍又一遍重復(fù)相同輸出的技能變得非常重復(fù)并且感覺(jué)不自然。至少隨機(jī)化部分輸出很重要,這樣技能就不會(huì)感覺(jué)那么“僵硬”。
對(duì)于 Speed Tap,簡(jiǎn)單的隨機(jī)化是使用我在其他技能中實(shí)施的文本后處理技術(shù)完成的。
輸出文本時(shí),{braces} 可以放在作為隨機(jī)同義詞候選詞的任何單詞或短語(yǔ)周?chē)?/font>然后,創(chuàng)建一個(gè)可能的同義詞列表,并在輸出字符串中僅使用主鍵。單詞會(huì)自動(dòng)隨機(jī)化,使技能感覺(jué)更自然,不那么機(jī)械化。
// The synonym list
const outputSynonyms = {
"Okay, ": ["Okay, ","Alright, ",""]
};
// A method in the response object
"randomizeSynonyms": function(synonyms) {
try {
let ssml = this.response.response.outputSpeech.ssml;
ssml = ssml.replace(/\{([^\}]+)\}/g, function (m, m1) {
if (synonyms && synonyms[m1]) {
let s = synonyms[m1];
if (s.length) {
// simple array of synonyms
return s[Math.floor(Math.random() * s.length)];
}
}
return m1;
});
this.response.response.outputSpeech.ssml = ssml;
} catch(e) { }
}
// Example
response.say("{Okay, }let's play."); // Could be: "Alright, let's play" or "let's play"
// Randomize synonyms in the output during app.post()
response.randomizeSynonyms(outputSynonyms);
執(zhí)行
本節(jié)將深入探討該技能的實(shí)現(xiàn)細(xì)節(jié),并展示演示每種功能如何實(shí)現(xiàn)的代碼片段。
觸發(fā)切換
Lambda 函數(shù)需要根據(jù)它是從 CloudWatch Events(更新排行榜)還是從 Alexa 觸發(fā)而表現(xiàn)不同。Lambda 函數(shù)處理程序使用以下代碼進(jìn)行切換:
// connect to lambda
exports.handler = function(event, context, callback) {
if (event && "aws.events"===event.source) {
// Scheduled Event!
app.scheduled(event).then((response)=>{
callback(null,response);
}).catch((e)=>{
callback(e);
});
}
else {
// Alexa Request
log("Alexa Request");
app.handler(event, context, callback);
}
};
按鈕偵聽(tīng)器事件
技能使用 Gadgets API 監(jiān)聽(tīng)按鈕事件。該技能返回一個(gè)指令,定義觸發(fā)技能的確切條件。這是一個(gè)示例按鈕偵聽(tīng)器指令,它將導(dǎo)致在按下按鈕或超時(shí)時(shí)調(diào)用技能。
{
"type" : "GameEngine.StartInputHandler",
"timeout" : 25000,
"proxies" : ["button"],
"recognizers" : {
"button_down_recognizer" : {
"type" : "match",
"anchor" : "end",
"fuzzy" : false,
"pattern" : [{
"action" : "down"
}
]
}
},
"events" : {
"button_down_listener" : {
"meets" : ["button_down_recognizer"],
"reports" : "matches",
"shouldEndInputHandler" : true
},
"timeout" : {
"meets" : ["timed out"],
"reports" : "history",
"shouldEndInputHandler" : true
}
}
}
光指令
有幾種不同格式的 GadgetController.SetLight 指令可以根據(jù)按鈕的狀態(tài)更改按鈕的顏色。API 文檔詳細(xì)介紹了這些狀態(tài)是什么以及它們?nèi)绾喂ぷ鳌?/font>
這是一個(gè)循環(huán)顯示顏色并等待用戶在按鈕變?yōu)榫G色時(shí)按下按鈕的示例指令。當(dāng)代碼接收到按鈕按下事件時(shí),它還會(huì)接收按鈕被按下時(shí)的顏色。這允許代碼檢查用戶是否在它為綠色時(shí)按下它。
{
"type" : "GadgetController.SetLight",
"version" : 1,
"targetGadgets" : [],
"parameters" : {
"triggerEvent" : "none",
"triggerEventTimeMs" : 0,
"animations" : [
{
"repeat" : 255,
"targetLights" : ["1"],
"sequence" : [{
"durationMs" : 1000,
"blend" : false,
"color" : "0000FF"
}, {
"durationMs" : 1000,
"blend" : false,
"color" : "FFA500"
}, {
"durationMs" : 1000,
"blend" : false,
"color" : "FF0000"
}, {
"durationMs" : 1000,
"blend" : false,
"color" : "FF00FF"
}, {
"durationMs" : 1000,
"blend" : false,
"color" : "00FF00"
}
]
}
]
}
}
堅(jiān)持
所有持久化都是使用 DynamoDB (DDB) 完成的。
每次調(diào)用技能時(shí),它都會(huì)檢查會(huì)話是否有用戶對(duì)象。如果沒(méi)有,它會(huì)嘗試從請(qǐng)求中給定的 userId 的 DDB 加載它。如果用戶記錄存在,它會(huì)加載它并將其存儲(chǔ)在會(huì)話中,這樣就不需要在每次調(diào)用技能時(shí)都檢索它。
用戶數(shù)據(jù)在發(fā)生變化時(shí)會(huì)持久保存回 DDB。例如,新的高分、會(huì)話計(jì)數(shù)增量、使用額外的生命等。
在觸發(fā) In-Skill Purchase 流程的情況下,實(shí)際需要技能退出,會(huì)話結(jié)束。然后,當(dāng)玩家完成購(gòu)買(mǎi)時(shí),將創(chuàng)建一個(gè)新會(huì)話。出于這個(gè)原因,在某些情況下需要保留當(dāng)前游戲的狀態(tài)。但在大多數(shù)情況下,當(dāng)前游戲的回合和分?jǐn)?shù)需要從用戶記錄中清除,而不是持久化。
持久化用戶記錄的通用函數(shù)會(huì)清除不應(yīng)持久化的數(shù)據(jù),并調(diào)用 DDB 持久化層來(lái)存儲(chǔ)數(shù)據(jù)。
async function persist_user(persist_game_state) {
// Persist user session back to db if it has changed
if (user) {
let u = JSON.parse(JSON.stringify(user));
if (!persist_game_state) {
delete u.round;
delete u.lives_used;
delete u.state;
delete u.buttonConnected;
}
delete u.game;
delete u.listenerRequestId;
await ddb.put(app.user_persistence_table, u);
}
}
數(shù)據(jù)庫(kù)層
javascript 的 ask-sdk 模塊有很多很棒的功能,但它的 API 對(duì)我來(lái)說(shuō)仍然太低級(jí)了。我圍繞 ask-sdk 函數(shù)編寫(xiě)了一個(gè)包裝器,進(jìn)一步抽象了功能。
包裝器所做的其中一件事是提取從 ask-sdk 函數(shù)返回的 Promises,而是公開(kāi)異步函數(shù),以便我的代碼可以使用 await。
包裝器代碼包含在源代碼中,我打算將其清理并作為 NPM 模塊分發(fā)。下面是一個(gè)示例函數(shù),它簡(jiǎn)化了從 DDB 中檢索單個(gè)記錄的過(guò)程:
'get': async function(table, keyAttribute, keyValue) {
let params = {TableName:table, Key:{ [keyAttribute]:keyValue } };
return docClient.get(params).promise()
.then( (item)=> {
if (!item || !item.Item) { return null; }
return item.Item;
});
}
會(huì)話/體驗(yàn)維護(hù)
游戲源中的用戶對(duì)象包括一個(gè)“體驗(yàn)”對(duì)象,用于存儲(chǔ)用戶在玩游戲時(shí)的體驗(yàn)。作為此體驗(yàn)對(duì)象的一部分,每次啟動(dòng)游戲時(shí)都會(huì)增加 session_count 屬性。
體驗(yàn)對(duì)象還包含跟蹤用戶是否已經(jīng)聽(tīng)到特定響應(yīng)的鍵。如果他們有,那么下次觸發(fā)時(shí)他們會(huì)得到一個(gè)較短的版本。例如:
if (round===1 && request.experience('intro_1')) {
say `That was easy, but now the lights will get a little faster every round. How far can you go? Keep going.`;
}
else if (round===1) {
say `Nice, Keep going.`;
}
文本響應(yīng)后處理
構(gòu)建被翻譯成語(yǔ)音的文本輸出有一些煩惱,例如復(fù)數(shù)化和 is/are 等。Speed Tap 包含文本后處理功能,可大大簡(jiǎn)化常見(jiàn)用例。每次調(diào)用技能后,都會(huì)自動(dòng)對(duì)輸出 SSML 執(zhí)行此后處理。
以下是后處理功能可以執(zhí)行的操作的一些示例。
let coins=1;
say `There {are} ${coins} coin{s} left.`;
后處理器處理 {are} 和 {s} 并查找附近的數(shù)字以確定應(yīng)如何處理它們。在本例中,它看到“1”并且輸出為:
"There is 1 coin left."
如果 coins==2,那么同一個(gè) say 調(diào)用的輸出將是:
"There are 2 coins left."
狀態(tài)和上下文意圖
意圖缺乏狀態(tài)和上下文。意思是,有全局 AMAZON.YesIntent 和 AMAZON.NoIntent 意圖,當(dāng)用戶說(shuō)“是”或“否”時(shí)會(huì)被觸發(fā)。但是這些意圖不知道問(wèn)的是哪個(gè)問(wèn)題,所以一種常見(jiàn)的方法是在每個(gè)處理程序中構(gòu)建邏輯知道被問(wèn)到的問(wèn)題并采取適當(dāng)?shù)男袆?dòng)。
相反,我在 alexa-app 框架上構(gòu)建了一個(gè)層,允許我創(chuàng)建“上下文意圖”。當(dāng)用戶處于某種狀態(tài)時(shí),是或否將在用戶所在的上下文中觸發(fā)函數(shù),而不是全局意圖。
例如,如果詢(xún)問(wèn)用戶是否要繼續(xù),他們的會(huì)話會(huì)更新以反映他們處于“繼續(xù)”狀態(tài),并且適當(dāng)?shù)靥幚硎腔蚍瘢?/font>
app.intentMap({
"continue": {
[YES]: async()=>{
await continue_game();
}
,[NO]: async()=>{
await end_game();
}
}
});
技能購(gòu)買(mǎi)
In-Skill Purchases 允許玩家購(gòu)買(mǎi)額外的生命以在他們搞砸時(shí)繼續(xù)玩。這實(shí)際上是一個(gè)復(fù)雜的主題,有很多實(shí)施怪癖和細(xì)節(jié)。一些重要的注意事項(xiàng)值得一提:
- ISP 只能使用 CLI 工具進(jìn)行配置和部署。
- 它將您的技能限制在美國(guó)地區(qū)(這就是 Speed Tap 不在英國(guó)的原因)。
- 需要從技能代碼中進(jìn)行 API 調(diào)用,以向其 API 端點(diǎn)發(fā)出 https 請(qǐng)求。
- 當(dāng)ISP流程發(fā)起后,技能完全退出,會(huì)話結(jié)束。Alexa 服務(wù)控制并引導(dǎo)用戶完成購(gòu)買(mǎi)流程。當(dāng)它完成時(shí) - 無(wú)論成功還是失敗 - 技能將再次啟動(dòng),并帶有指示購(gòu)買(mǎi)狀態(tài)的“Connections.Response”事件類(lèi)型。
- 該技能必須處理購(gòu)買(mǎi)流程中的錯(cuò)誤情況以及用戶是否拒絕購(gòu)買(mǎi)。它還必須處理用戶在購(gòu)買(mǎi)后要求退款的情況。
- 對(duì)于消耗品 ISP,正如 Speed Tap 使用的那樣,該技能必須記住并存儲(chǔ)用戶購(gòu)買(mǎi)了哪些物品以及他們還剩多少。Alex 服務(wù)不維護(hù)庫(kù)存。
要觸發(fā) ISP 購(gòu)買(mǎi)流程,必須從技能返回指令并且必須關(guān)閉會(huì)話。該指令如下所示:
{
'type': 'Connections.SendRequest',
'name': 'Buy',
'payload': {
'InSkillProduct': {
'productId': "XYZ"
}
},
'token': "arbitrary-token"
}
Alexa 技能活動(dòng)
Alexa 技能事件允許技能在沒(méi)有用戶主動(dòng)與技能交互時(shí)響應(yīng)事件。對(duì)于 Speed Tap,它處理用戶授予或撤銷(xiāo)使用其真實(shí)姓名的權(quán)限的情況。
下面的代碼是 Speed Tap 如何響應(yīng)此事件,使用 API 檢索用戶的真實(shí)姓名,更新他們的用戶記錄,并持久化它。
app.on('AlexaSkillEvent.SkillPermissionAccepted', async()=>{
try {
let user_id = request.data.context.System.user.userId;
user = await ddb.get(app.user_persistence_table, "userid", user_id);
let name = await app.api("/v2/accounts/~current/settings/Profile.name");
user.name = name;
user.linked = true;
await persist_user();
} catch(e) {
console.log(e.message);
}
});
為了響應(yīng)這些事件,技能必須注冊(cè)它希望收到這些事件的通知。使用 CLI 時(shí),這些存儲(chǔ)在 manifest.events.subscriptions 下的 skill.json 中:
"subscriptions": [
{
"eventName": "SKILL_PERMISSION_ACCEPTED"
},
{
"eventName": "SKILL_PERMISSION_CHANGED"
}
]
聲音庫(kù)
聲音庫(kù)是一種非常好的方法,可以輕松地將聲音包含在技能中。這些聲音都按類(lèi)別列在“聲音庫(kù)”頁(yè)面上。
您無(wú)需執(zhí)行任何特殊操作即可使用這些聲音。只需找到您要使用的聲音,復(fù)制列出的 SSML 內(nèi)容,然后將其插入到您的響應(yīng)中。
src
='soundbank://soundlibrary/animals/amzn_sfx_bear_groan_roar_01'/>
我假設(shè)使用該soundbank:
協(xié)議可以最大限度地減少延遲,因?yàn)槁曇粑募鎯?chǔ)在靠近 Alexa 內(nèi)部服務(wù)器的某些邊緣服務(wù)器上。
啟動(dòng)畫(huà)面/顯示
當(dāng)技能啟動(dòng)時(shí),會(huì)顯示一個(gè)圖形啟動(dòng)畫(huà)面。這是通過(guò)使用 Display 接口在響應(yīng)中返回指令來(lái)實(shí)現(xiàn)的。可重用函數(shù)將功能包裝在一個(gè)地方:
function display_splash_screen(request,response) {
if (has_display(request)) {
response.directive({
"type" : "Display.RenderTemplate",
"template" : {
"type" : "BodyTemplate1",
"backButton" : "HIDDEN",
"backgroundImage" : {
"contentDescription" : "",
"sources" : [{
"url" : "https://alexaspeedtap.com/splash.jpg",
"size" : "MEDIUM"
},{
"url" : "https://alexaspeedtap.com/splash-square.jpg",
"widthPixels":640,
"heightPixels":640
}
]
}
}
});
}
}
這不一定代表展示的最佳做法,但它可以完成工作。
使用 Display 指令時(shí),技能必須檢測(cè)用戶的設(shè)備是否有屏幕,如果沒(méi)有則不發(fā)送指令,否則會(huì)拋出錯(cuò)誤。has_display() 函數(shù)封裝了該檢查。
const has_display = function(request) { try { return !!request.data.context.System.device.supportedInterfaces.Display; } catch(e) { return false; }};
還必須為該技能注冊(cè) RENDER_TEMPLATE 接口。如果跳過(guò)此步驟,任何返回 Display 指令的嘗試都將導(dǎo)致異常。
寫(xiě)入 S3
當(dāng)后臺(tái)任務(wù)運(yùn)行并創(chuàng)建排行榜 JSON 文件時(shí),必須從技能代碼中將其寫(xiě)入 S3 存儲(chǔ)桶。
我寫(xiě)了一個(gè)簡(jiǎn)單的可重用函數(shù)來(lái)將任意 javascript 對(duì)象寫(xiě)入我的存儲(chǔ)桶:
const putObjectToS3 = async function (o,filename) {
let s3 = new AWS.S3({'region': 'us-east-1'});
let params = {
Bucket: "alexa-speed-tap",
Key: filename,
Body: JSON.stringify(o),
ContentType: "application/json",
CacheControl: "no-cache",
ACL: "public-read"
};
return s3.putObject(params).promise();
};
CORS
由于排行榜 JSON 文件存儲(chǔ)在 AWS S3 上,在生成的 Amazon URL 上提供服務(wù),瀏覽器將阻止它從其他域加載,例如 AlexaSpeedTap.com。
為了允許瀏覽器訪問(wèn)不同服務(wù)器上的內(nèi)容,托管服務(wù)器 (S3) 必須明確允許此類(lèi)請(qǐng)求。
這可以使用如下策略在權(quán)限 --> CORS 配置下的 S3 存儲(chǔ)桶上進(jìn)行配置。
<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
<CORSRule>
<AllowedOrigin>*AllowedOrigin>
<AllowedMethod>GETAllowedMethod>
<MaxAgeSeconds>3000MaxAgeSeconds>
<AllowedHeader>*AllowedHeader>
CORSRule>
CORSConfiguration>
結(jié)論
構(gòu)建 Echo Buttons 是一個(gè)獨(dú)特的挑戰(zhàn),因?yàn)?API 和實(shí)際的小工具功能有點(diǎn)棘手。但是,一旦將預(yù)期調(diào)整為實(shí)際可能的情況,并構(gòu)建了基本功能,完善體驗(yàn)和讓游戲變得有趣就相對(duì)簡(jiǎn)單了。
盡管 Speed Tap 是一款易于玩和理解的游戲,但它使用了許多使其真正獨(dú)一無(wú)二的 Alexa 功能和概念,并為玩家提供了不同于其他游戲的體驗(yàn)。
我希望你喜歡它!
?
- 適用于AWS的M5Core2/Core2上的亞馬遜Alexa
- 適用于Windows 10 IoT Core的Adafruit入門(mén)包創(chuàng)建開(kāi)源項(xiàng)目
- 適用于PC的ARDUINO控制游戲手柄(有線)
- 兼容Google Assistant和Amazon Alexa的智能燈泡
- 適用于PC和Android的Arduino游戲控制器
- 兆易創(chuàng)新GD32 MCU選型手冊(cè),適用于GD32全系列MCU
- 適用于低壓大電流開(kāi)關(guān)電源的多組變壓器設(shè)計(jì)
- 適用于中國(guó)國(guó)家標(biāo)準(zhǔn)的機(jī)動(dòng)車(chē)測(cè)速儀 2次下載
- 適用于新能源高電壓并網(wǎng)的電力電子拓?fù)?/a> 0次下載
- 適用于惡劣環(huán)境的產(chǎn)品
- 適用于負(fù)載開(kāi)關(guān)應(yīng)用的芯片AO3415數(shù)據(jù)手冊(cè)下載 26次下載
- 適用于MPLAB工具設(shè)計(jì)的多條忠告
- 適用于MPLAB? X IDE的MPLAB REAL ICE?在線仿真器用戶指南
- 適用于PDA的PLC編程系統(tǒng)開(kāi)發(fā)
- 適用于3V系統(tǒng)的電池切換電路
- 適用于內(nèi)窺鏡鏡頭模組的環(huán)氧樹(shù)脂封裝膠 94次閱讀
- 一款適用于紅藍(lán)對(duì)抗的演練工具Goblin介紹 1733次閱讀
- 如何編寫(xiě)適用于Go項(xiàng)目的Makefile 2241次閱讀
- 微雪電子ROC-RK3308主板CC-Amazon Alexa簡(jiǎn)介 1429次閱讀
- 基于一種適用于SSL產(chǎn)品的LED控制電路設(shè)計(jì) 1194次閱讀
- 電壓放大器適用于什么場(chǎng)合 1w次閱讀
- 適用于測(cè)控領(lǐng)域的4種實(shí)時(shí)操作系統(tǒng)對(duì)比分析 3700次閱讀
- Super Sabre電源連接器系統(tǒng),適用于大電流應(yīng)用 926次閱讀
- 適用于大功率動(dòng)力馬達(dá)逆變器的IGBT模塊 7752次閱讀
- TC1948P是紅外線遙控發(fā)射電路,適用于錄像機(jī) 3037次閱讀
- FAIR和INRIA的合作提出人體姿勢(shì)估計(jì)新模型,適用于人體3D表面構(gòu)建 5836次閱讀
- tap和click事件區(qū)別 5086次閱讀
- 適用于868MHz至915MHz的射頻布局參考設(shè)計(jì)電路圖 4333次閱讀
- 適用于420-470MHz的射頻布局參考設(shè)計(jì)電路圖 2321次閱讀
- 適用于RS232串口的溫度檢測(cè)電路 2732次閱讀
下載排行
本周
- 1山景DSP芯片AP8248A2數(shù)據(jù)手冊(cè)
- 1.06 MB | 532次下載 | 免費(fèi)
- 2RK3399完整板原理圖(支持平板,盒子VR)
- 3.28 MB | 339次下載 | 免費(fèi)
- 3TC358743XBG評(píng)估板參考手冊(cè)
- 1.36 MB | 330次下載 | 免費(fèi)
- 4DFM軟件使用教程
- 0.84 MB | 295次下載 | 免費(fèi)
- 5元宇宙深度解析—未來(lái)的未來(lái)-風(fēng)口還是泡沫
- 6.40 MB | 227次下載 | 免費(fèi)
- 6迪文DGUS開(kāi)發(fā)指南
- 31.67 MB | 194次下載 | 免費(fèi)
- 7元宇宙底層硬件系列報(bào)告
- 13.42 MB | 182次下載 | 免費(fèi)
- 8FP5207XR-G1中文應(yīng)用手冊(cè)
- 1.09 MB | 178次下載 | 免費(fèi)
本月
- 1OrCAD10.5下載OrCAD10.5中文版軟件
- 0.00 MB | 234315次下載 | 免費(fèi)
- 2555集成電路應(yīng)用800例(新編版)
- 0.00 MB | 33566次下載 | 免費(fèi)
- 3接口電路圖大全
- 未知 | 30323次下載 | 免費(fèi)
- 4開(kāi)關(guān)電源設(shè)計(jì)實(shí)例指南
- 未知 | 21549次下載 | 免費(fèi)
- 5電氣工程師手冊(cè)免費(fèi)下載(新編第二版pdf電子書(shū))
- 0.00 MB | 15349次下載 | 免費(fèi)
- 6數(shù)字電路基礎(chǔ)pdf(下載)
- 未知 | 13750次下載 | 免費(fèi)
- 7電子制作實(shí)例集錦 下載
- 未知 | 8113次下載 | 免費(fèi)
- 8《LED驅(qū)動(dòng)電路設(shè)計(jì)》 溫德?tīng)栔?/a>
- 0.00 MB | 6656次下載 | 免費(fèi)
總榜
- 1matlab軟件下載入口
- 未知 | 935054次下載 | 免費(fèi)
- 2protel99se軟件下載(可英文版轉(zhuǎn)中文版)
- 78.1 MB | 537798次下載 | 免費(fèi)
- 3MATLAB 7.1 下載 (含軟件介紹)
- 未知 | 420027次下載 | 免費(fèi)
- 4OrCAD10.5下載OrCAD10.5中文版軟件
- 0.00 MB | 234315次下載 | 免費(fèi)
- 5Altium DXP2002下載入口
- 未知 | 233046次下載 | 免費(fèi)
- 6電路仿真軟件multisim 10.0免費(fèi)下載
- 340992 | 191187次下載 | 免費(fèi)
- 7十天學(xué)會(huì)AVR單片機(jī)與C語(yǔ)言視頻教程 下載
- 158M | 183279次下載 | 免費(fèi)
- 8proe5.0野火版下載(中文版免費(fèi)下載)
- 未知 | 138040次下載 | 免費(fèi)
評(píng)論