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

拉姆達(dá)
游戲的所有代碼都在 AWS Lambda 中。它是使用 alexa-app 框架在 Node.js 中編寫的。Alexa 事件(玩游戲的用戶)和 CloudWatch Events 使用相同的 lambda 函數(shù)代碼,這會觸發(fā)后臺任務(wù)來更新排行榜。
動態(tài)數(shù)據(jù)庫
個人球員和全球世界紀(jì)錄統(tǒng)計(jì)數(shù)據(jù)都存儲在 DynamoDB 中。對 DDB 的訪問是使用 Amazon 的 aws-sdk 包完成的,我在其頂部編寫了一個自定義層以簡化 API 并在現(xiàn)有的 Promise 接口上提供一個異步/等待接口。
每次交互時,用戶數(shù)據(jù)和狀態(tài)都會保存到 DDB,從而使游戲能夠以隨著時間的推移和適當(dāng)?shù)姆绞脚c用戶進(jìn)行交互(請參閱下面的用戶體驗(yàn)部分)。
S3
Lambda 函數(shù)每 5 分鐘生成一個包含所有高分和當(dāng)前世界紀(jì)錄數(shù)據(jù)的 JSON 文件,并將其存儲在公共 S3 存儲桶中。然后通過 AJAX 從網(wǎng)站加載此 JSON 文件以顯示排行榜并以大文本宣布當(dāng)前的記錄保持者。
后臺任務(wù) - CloudWatch Events
需要一個“后臺任務(wù)”來定期更新包含網(wǎng)站數(shù)據(jù)的 JSON 文件。由于無法按計(jì)劃觸發(fā) Lambda 函數(shù),因此使用 CloudWatch Events 觸發(fā)觸發(fā) Lambda 函數(shù)的事件。
然后,Lambda 函數(shù)代碼會檢測它是由 Alexa 還是 CloudWatch Events 觸發(fā)的,并做出相應(yīng)的行為。
ALEXA API 和功能
許多 Alexa API 和功能被用來創(chuàng)建這個游戲。以下是使用的內(nèi)容和原因的摘要。
小工具API
Gadgets API需要與 Echo Buttons 交互,實(shí)際上有兩種風(fēng)格,它們都必須聲明為技能所需的接口。
GAME_ENGINE是用于監(jiān)聽 Echo Buttons 事件的接口。
GADGET_CONTROLLER是用于實(shí)際設(shè)置按鈕本身的燈光模式的接口。
這是兩個不同的 API,要讓它們無縫地協(xié)同工作,一開始理解起來有點(diǎn)棘手。仔細(xì)閱讀文檔、了解每個文檔的局限性并在設(shè)計(jì)您的體驗(yàn)時了解什么是可能的、什么是不可能的,這一點(diǎn)很重要。
客戶資料 API
Customer Profile API允許技能請求有關(guān)用戶的信息,例如姓名、電子郵件地址和電話號碼。使用時,該技能會在用戶的 Alexa 應(yīng)用程序中向用戶出示一張卡片以授予權(quán)限,然后才能訪問此信息。如果用戶請求,Speed Tap 使用此 API 獲取用戶名以顯示在網(wǎng)站的排行榜上。
技能購買
In-Skill Purchases (ISP) API允許技能請求用戶使用真錢進(jìn)行實(shí)際購買,以解鎖技能中的內(nèi)容或功能。Speed Tap 使用“消耗品”ISP (CISP),這是可以購買和用完的物品,而不是一次性購買和訂閱,它們不會在使用時消失。
Speed Tap 使用戶能夠在點(diǎn)擊錯誤的顏色后繼續(xù)。每個新用戶都會獲得 5 條額外的生命,當(dāng)他們離開后,他們可以選擇使用 CISP 再購買 10 條生命。
展示
Speed Tap 利用顯示接口 API與 Echo Show 和 Echo Spot 設(shè)備上的顯示功能進(jìn)行交互,方法是在游戲開始時顯示彩色“啟動畫面”,并在每次按下按鈕時顯示當(dāng)前回合。
隨著帶有顯示器的 Echo 設(shè)備變得越來越普遍,技能通過視覺提示和附加信息來補(bǔ)充其音頻輸出非常重要。
聲音庫
Alexa Skills Kit 聲音庫是可用于任何技能的音頻片段集合。這些音頻文件由亞馬遜存儲,可以插入到任何響應(yīng)中,而無需技能作者創(chuàng)建、存儲或交付它們。例如:
src
='soundbank://soundlibrary/ui/gameshow/amzn_ui_sfx_gameshow_countdown_loop_32s_full_01'/>
隨著用于多種用途的聲音樣本的選擇越來越多,這個庫為技能作者提供了一種簡單的方法來為他們的技能添加一點(diǎn)額外的個性和樂趣。Speed Tap 在等待用戶按下按鈕以增加戲劇性和張力時將這些聲音用于音樂,以及用于積極和消極事件。
演講者
Alexa 語音輸出可以比默認(rèn)的文本轉(zhuǎn)語音更具表現(xiàn)力的方式說出某些詞。這些“ Speechcons ”僅限于每種支持語言的一組特定單詞,并使用標(biāo)記插入到您的 SSML 響應(yīng)中:
<say-as interpret-as="interjection">abracadabra!say-as>
Speed Tap 使用 Speechcons 響應(yīng)每個按鈕按下,讓游戲?qū)τ脩魜碚f更有趣,并增加一點(diǎn)多樣性。玩游戲的孩子特別喜歡有趣的輸出。
Alexa 技能活動
技能事件API允許技能響應(yīng)更改或事件,而無需用戶直接與技能交互。例如,當(dāng)用戶啟用或禁用技能時,可以通知該技能。
當(dāng)用戶使用手機(jī)上的 Alexa 應(yīng)用程序啟用或禁用對其客戶資料信息的訪問時,Speed Tap 使用 Skill Events API 立即做出響應(yīng)。收到此事件后,Speed Tap 會立即調(diào)用 API 獲取用戶姓名,因此無需用戶再次玩游戲即可更新排行榜。
用戶體驗(yàn)
語音應(yīng)用程序的用戶體驗(yàn)至關(guān)重要且微妙。與網(wǎng)絡(luò)和移動等顯示媒體不同,音頻需要更長的時間來呈現(xiàn)信息,并且用戶無法處理選項(xiàng)列表或掃描屏幕以了解他們下一步想做什么。
Speed Tap 實(shí)施了許多改進(jìn)用戶體驗(yàn)的方法,這些方法雖然不那么明顯,但卻產(chǎn)生了很大的不同。
共享的全球經(jīng)驗(yàn)
與移動應(yīng)用程序不同,大多數(shù) Alexa 游戲都是單人游戲,不與其他玩家社區(qū)互動。用戶是孤立的,通常玩游戲是為了個人高分或自己探索游戲。
但是……和別人一起玩游戲更有趣!
Speed Tap 朝著這個方向邁出了獨(dú)特的一步,讓玩家可以看到他們的分?jǐn)?shù)與其他人的比較情況。網(wǎng)站上的排行榜列出了每個玩家的個人最高分以及他們用了多少次連續(xù)得分來達(dá)到這一目標(biāo)。添加你的真實(shí)姓名的能力讓你有吹牛的權(quán)利,看看你是否在打敗你的朋友。
玩家知道什么與實(shí)際上是什么?
為用戶提供相關(guān)信息的一個重要部分是跟蹤他們所知道的,而不是實(shí)際情況。
在 Speed Tap 中,這與世界紀(jì)錄有關(guān)。當(dāng)用戶玩游戲時,他們可能會被告知當(dāng)前的世界紀(jì)錄是 25 次點(diǎn)擊。但下次他們比賽時,世界紀(jì)錄可能會增加到 30,他們很想知道。
懶人技能只會在用戶每次開始游戲時告訴用戶世界紀(jì)錄。但為了獲得更好的體驗(yàn),該技能需要知道用戶上次被告知的世界紀(jì)錄是什么,并且只有在更改時才告訴他們。通過這種方式,他們可以獲得他們想要的信息,但如果相同,則不必每次都被迫忍受關(guān)于當(dāng)前世界紀(jì)錄的相同句子。
Speed Tap 通過保留兩個獨(dú)立的數(shù)據(jù)區(qū)域來跟蹤此類事件。一個保存當(dāng)前的真實(shí)信息(例如當(dāng)前的世界紀(jì)錄和持有者),而另一個保存相同的信息,但處于用戶最后知道的狀態(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)/會話跟蹤
由于用戶會多次玩游戲,因此不應(yīng)每次都向他們展示冗長的音頻介紹。當(dāng)他們多次使用特性時,不應(yīng)該要求他們?nèi)淌軐λ麄円呀?jīng)理解的東西的解釋。
出于這個原因,跟蹤用戶與技能交互時的體驗(yàn)對于技能來說很重要,這樣技能就知道何時可以縮短交互。這正是人類所做的,通過這樣做,一項(xiàng)技能似乎更自然。
Speed Tap 為每個用戶保留一個“體驗(yàn)”對象,該對象隨依賴于它的每種交互類型進(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 對象還用于確定是否應(yīng)該給出有關(guān)排行榜如何工作、所需權(quán)限等的解釋。
隨機(jī)輸出
一遍又一遍重復(fù)相同輸出的技能變得非常重復(fù)并且感覺不自然。至少隨機(jī)化部分輸出很重要,這樣技能就不會感覺那么“僵硬”。
對于 Speed Tap,簡單的隨機(jī)化是使用我在其他技能中實(shí)施的文本后處理技術(shù)完成的。
輸出文本時,{braces} 可以放在作為隨機(jī)同義詞候選詞的任何單詞或短語周圍。然后,創(chuàng)建一個可能的同義詞列表,并在輸出字符串中僅使用主鍵。單詞會自動隨機(jī)化,使技能感覺更自然,不那么機(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);
}
};
按鈕偵聽器事件
技能使用 Gadgets API 監(jiān)聽按鈕事件。該技能返回一個指令,定義觸發(fā)技能的確切條件。這是一個示例按鈕偵聽器指令,它將導(dǎo)致在按下按鈕或超時時調(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>
這是一個循環(huán)顯示顏色并等待用戶在按鈕變?yōu)榫G色時按下按鈕的示例指令。當(dāng)代碼接收到按鈕按下事件時,它還會接收按鈕被按下時的顏色。這允許代碼檢查用戶是否在它為綠色時按下它。
{
"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)用技能時,它都會檢查會話是否有用戶對象。如果沒有,它會嘗試從請求中給定的 userId 的 DDB 加載它。如果用戶記錄存在,它會加載它并將其存儲在會話中,這樣就不需要在每次調(diào)用技能時都檢索它。
用戶數(shù)據(jù)在發(fā)生變化時會持久保存回 DDB。例如,新的高分、會話計(jì)數(shù)增量、使用額外的生命等。
在觸發(fā) In-Skill Purchase 流程的情況下,實(shí)際需要技能退出,會話結(jié)束。然后,當(dāng)玩家完成購買時,將創(chuàng)建一個新會話。出于這個原因,在某些情況下需要保留當(dāng)前游戲的狀態(tài)。但在大多數(shù)情況下,當(dāng)前游戲的回合和分?jǐn)?shù)需要從用戶記錄中清除,而不是持久化。
持久化用戶記錄的通用函數(shù)會清除不應(yīng)持久化的數(shù)據(jù),并調(diào)用 DDB 持久化層來存儲數(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ù)庫層
javascript 的 ask-sdk 模塊有很多很棒的功能,但它的 API 對我來說仍然太低級了。我圍繞 ask-sdk 函數(shù)編寫了一個包裝器,進(jìn)一步抽象了功能。
包裝器所做的其中一件事是提取從 ask-sdk 函數(shù)返回的 Promises,而是公開異步函數(shù),以便我的代碼可以使用 await。
包裝器代碼包含在源代碼中,我打算將其清理并作為 NPM 模塊分發(fā)。下面是一個示例函數(shù),它簡化了從 DDB 中檢索單個記錄的過程:
'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;
});
}
會話/體驗(yàn)維護(hù)
游戲源中的用戶對象包括一個“體驗(yàn)”對象,用于存儲用戶在玩游戲時的體驗(yàn)。作為此體驗(yàn)對象的一部分,每次啟動游戲時都會增加 session_count 屬性。
體驗(yàn)對象還包含跟蹤用戶是否已經(jīng)聽到特定響應(yīng)的鍵。如果他們有,那么下次觸發(fā)時他們會得到一個較短的版本。例如:
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)建被翻譯成語音的文本輸出有一些煩惱,例如復(fù)數(shù)化和 is/are 等。Speed Tap 包含文本后處理功能,可大大簡化常見用例。每次調(diào)用技能后,都會自動對輸出 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,那么同一個 say 調(diào)用的輸出將是:
"There are 2 coins left."
狀態(tài)和上下文意圖
意圖缺乏狀態(tài)和上下文。意思是,有全局 AMAZON.YesIntent 和 AMAZON.NoIntent 意圖,當(dāng)用戶說“是”或“否”時會被觸發(fā)。但是這些意圖不知道問的是哪個問題,所以一種常見的方法是在每個處理程序中構(gòu)建邏輯知道被問到的問題并采取適當(dāng)?shù)男袆印?/font>
相反,我在 alexa-app 框架上構(gòu)建了一個層,允許我創(chuàng)建“上下文意圖”。當(dāng)用戶處于某種狀態(tài)時,是或否將在用戶所在的上下文中觸發(fā)函數(shù),而不是全局意圖。
例如,如果詢問用戶是否要繼續(xù),他們的會話會更新以反映他們處于“繼續(xù)”狀態(tài),并且適當(dāng)?shù)靥幚硎腔蚍瘢?/font>
app.intentMap({
"continue": {
[YES]: async()=>{
await continue_game();
}
,[NO]: async()=>{
await end_game();
}
}
});
技能購買
In-Skill Purchases 允許玩家購買額外的生命以在他們搞砸時繼續(xù)玩。這實(shí)際上是一個復(fù)雜的主題,有很多實(shí)施怪癖和細(xì)節(jié)。一些重要的注意事項(xiàng)值得一提:
- ISP 只能使用 CLI 工具進(jìn)行配置和部署。
- 它將您的技能限制在美國地區(qū)(這就是 Speed Tap 不在英國的原因)。
- 需要從技能代碼中進(jìn)行 API 調(diào)用,以向其 API 端點(diǎn)發(fā)出 https 請求。
- 當(dāng)ISP流程發(fā)起后,技能完全退出,會話結(jié)束。Alexa 服務(wù)控制并引導(dǎo)用戶完成購買流程。當(dāng)它完成時 - 無論成功還是失敗 - 技能將再次啟動,并帶有指示購買狀態(tài)的“Connections.Response”事件類型。
- 該技能必須處理購買流程中的錯誤情況以及用戶是否拒絕購買。它還必須處理用戶在購買后要求退款的情況。
- 對于消耗品 ISP,正如 Speed Tap 使用的那樣,該技能必須記住并存儲用戶購買了哪些物品以及他們還剩多少。Alex 服務(wù)不維護(hù)庫存。
要觸發(fā) ISP 購買流程,必須從技能返回指令并且必須關(guān)閉會話。該指令如下所示:
{
'type': 'Connections.SendRequest',
'name': 'Buy',
'payload': {
'InSkillProduct': {
'productId': "XYZ"
}
},
'token': "arbitrary-token"
}
Alexa 技能活動
Alexa 技能事件允許技能在沒有用戶主動與技能交互時響應(yīng)事件。對于 Speed Tap,它處理用戶授予或撤銷使用其真實(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)這些事件,技能必須注冊它希望收到這些事件的通知。使用 CLI 時,這些存儲在 manifest.events.subscriptions 下的 skill.json 中:
"subscriptions": [
{
"eventName": "SKILL_PERMISSION_ACCEPTED"
},
{
"eventName": "SKILL_PERMISSION_CHANGED"
}
]
聲音庫
聲音庫是一種非常好的方法,可以輕松地將聲音包含在技能中。這些聲音都按類別列在“聲音庫”頁面上。
您無需執(zhí)行任何特殊操作即可使用這些聲音。只需找到您要使用的聲音,復(fù)制列出的 SSML 內(nèi)容,然后將其插入到您的響應(yīng)中。
src
='soundbank://soundlibrary/animals/amzn_sfx_bear_groan_roar_01'/>
我假設(shè)使用該soundbank:
協(xié)議可以最大限度地減少延遲,因?yàn)槁曇粑募鎯υ诳拷?Alexa 內(nèi)部服務(wù)器的某些邊緣服務(wù)器上。
啟動畫面/顯示
當(dāng)技能啟動時,會顯示一個圖形啟動畫面。這是通過使用 Display 接口在響應(yīng)中返回指令來實(shí)現(xiàn)的。可重用函數(shù)將功能包裝在一個地方:
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è)備是否有屏幕,如果沒有則不發(fā)送指令,否則會拋出錯誤。has_display() 函數(shù)封裝了該檢查。
const has_display = function(request) { try { return !!request.data.context.System.device.supportedInterfaces.Display; } catch(e) { return false; }};
還必須為該技能注冊 RENDER_TEMPLATE 接口。如果跳過此步驟,任何返回 Display 指令的嘗試都將導(dǎo)致異常。
寫入 S3
當(dāng)后臺任務(wù)運(yùn)行并創(chuàng)建排行榜 JSON 文件時,必須從技能代碼中將其寫入 S3 存儲桶。
我寫了一個簡單的可重用函數(shù)來將任意 javascript 對象寫入我的存儲桶:
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 文件存儲在 AWS S3 上,在生成的 Amazon URL 上提供服務(wù),瀏覽器將阻止它從其他域加載,例如 AlexaSpeedTap.com。
為了允許瀏覽器訪問不同服務(wù)器上的內(nèi)容,托管服務(wù)器 (S3) 必須明確允許此類請求。
這可以使用如下策略在權(quán)限 --> CORS 配置下的 S3 存儲桶上進(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 是一個獨(dú)特的挑戰(zhàn),因?yàn)?API 和實(shí)際的小工具功能有點(diǎn)棘手。但是,一旦將預(yù)期調(diào)整為實(shí)際可能的情況,并構(gòu)建了基本功能,完善體驗(yàn)和讓游戲變得有趣就相對簡單了。
盡管 Speed Tap 是一款易于玩和理解的游戲,但它使用了許多使其真正獨(dú)一無二的 Alexa 功能和概念,并為玩家提供了不同于其他游戲的體驗(yàn)。
我希望你喜歡它!
?
- 適用于AWS的M5Core2/Core2上的亞馬遜Alexa
- 適用于Windows 10 IoT Core的Adafruit入門包創(chuàng)建開源項(xiàng)目
- 適用于PC的ARDUINO控制游戲手柄(有線)
- 兼容Google Assistant和Amazon Alexa的智能燈泡
- 適用于PC和Android的Arduino游戲控制器
- 兆易創(chuàng)新GD32 MCU選型手冊,適用于GD32全系列MCU
- 適用于低壓大電流開關(guān)電源的多組變壓器設(shè)計(jì)
- 適用于中國國家標(biāo)準(zhǔn)的機(jī)動車測速儀 2次下載
- 適用于新能源高電壓并網(wǎng)的電力電子拓?fù)?/a> 0次下載
- 適用于惡劣環(huán)境的產(chǎn)品
- 適用于負(fù)載開關(guān)應(yīng)用的芯片AO3415數(shù)據(jù)手冊下載 26次下載
- 適用于MPLAB工具設(shè)計(jì)的多條忠告
- 適用于MPLAB? X IDE的MPLAB REAL ICE?在線仿真器用戶指南
- 適用于PDA的PLC編程系統(tǒng)開發(fā)
- 適用于3V系統(tǒng)的電池切換電路
- 適用于內(nèi)窺鏡鏡頭模組的環(huán)氧樹脂封裝膠 94次閱讀
- 一款適用于紅藍(lán)對抗的演練工具Goblin介紹 1733次閱讀
- 如何編寫適用于Go項(xiàng)目的Makefile 2241次閱讀
- 微雪電子ROC-RK3308主板CC-Amazon Alexa簡介 1429次閱讀
- 基于一種適用于SSL產(chǎn)品的LED控制電路設(shè)計(jì) 1194次閱讀
- 電壓放大器適用于什么場合 1w次閱讀
- 適用于測控領(lǐng)域的4種實(shí)時操作系統(tǒng)對比分析 3700次閱讀
- Super Sabre電源連接器系統(tǒng),適用于大電流應(yīng)用 926次閱讀
- 適用于大功率動力馬達(dá)逆變器的IGBT模塊 7752次閱讀
- TC1948P是紅外線遙控發(fā)射電路,適用于錄像機(jī) 3037次閱讀
- FAIR和INRIA的合作提出人體姿勢估計(jì)新模型,適用于人體3D表面構(gòu)建 5836次閱讀
- tap和click事件區(qū)別 5086次閱讀
- 適用于868MHz至915MHz的射頻布局參考設(shè)計(jì)電路圖 4333次閱讀
- 適用于420-470MHz的射頻布局參考設(shè)計(jì)電路圖 2321次閱讀
- 適用于RS232串口的溫度檢測電路 2732次閱讀
下載排行
本周
- 1山景DSP芯片AP8248A2數(shù)據(jù)手冊
- 1.06 MB | 532次下載 | 免費(fèi)
- 2RK3399完整板原理圖(支持平板,盒子VR)
- 3.28 MB | 339次下載 | 免費(fèi)
- 3TC358743XBG評估板參考手冊
- 1.36 MB | 330次下載 | 免費(fèi)
- 4DFM軟件使用教程
- 0.84 MB | 295次下載 | 免費(fèi)
- 5元宇宙深度解析—未來的未來-風(fēng)口還是泡沫
- 6.40 MB | 227次下載 | 免費(fèi)
- 6迪文DGUS開發(fā)指南
- 31.67 MB | 194次下載 | 免費(fèi)
- 7元宇宙底層硬件系列報告
- 13.42 MB | 182次下載 | 免費(fèi)
- 8FP5207XR-G1中文應(yīng)用手冊
- 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開關(guān)電源設(shè)計(jì)實(shí)例指南
- 未知 | 21549次下載 | 免費(fèi)
- 5電氣工程師手冊免費(fèi)下載(新編第二版pdf電子書)
- 0.00 MB | 15349次下載 | 免費(fèi)
- 6數(shù)字電路基礎(chǔ)pdf(下載)
- 未知 | 13750次下載 | 免費(fèi)
- 7電子制作實(shí)例集錦 下載
- 未知 | 8113次下載 | 免費(fèi)
- 8《LED驅(qū)動電路設(shè)計(jì)》 溫德爾著
- 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é)會AVR單片機(jī)與C語言視頻教程 下載
- 158M | 183279次下載 | 免費(fèi)
- 8proe5.0野火版下載(中文版免費(fèi)下載)
- 未知 | 138040次下載 | 免費(fèi)
評論