What would be better than a voice driven game on your Alexa device? How about a game that combines the audio of Alexa with buttons similar to a handheld game? I've built a fun custom skill that uses buttons as well as the range of sounds available through Alexa. It's called "Alien Invasion" and is available on the Alexa Custom Skill store to enable. Read along to find out how it was created and how you can create your own fun skill just like this.
Architecture of Skills using ButtonsEcho buttons serve as an extension to the device, and are paired as an accessory. Unlike a screen which is fixed within an Echo Show, buttons can be paired and unpaired with a device. They connect back to the Amazon cloud via a bluetooth connection to the Alexa device.
Once a button pairs with a device they can be leveraged within a custom skill, and can enhance gameplay for users.
Designing a User Experience with ButtonsIf you're familiar with building Alexa skills, you're aware that the microphone on the device dictates the interaction between the Alexa Skills Kit that handles the speech recognition and natural language understanding models from Amazon. When the microphone is on, the Echo tower lights up in its iconic blue ring. This is the cue for the user to say something as Alexa is listening. When the speaker is playing audio, the ring is not on, and the microphones are in a somewhat passive mode (albeit active to listen for the wake word).
When using buttons, you now have a new device that can create events that are part of the skill. Each event is processed as a message, that ultimately will trigger the lambda function that you will author.
The implication is that you now need to coordinate the events going on between two different input devices.
For example, in the initial game registration process, the speaker may give instructions to the user, then play some background music while waiting for the buttons to be pressed. By playing the music, the microphone is not in an active mode, and won't inadvertently pick up background noise, and process a false event. This is important when building games that have many turns as we don't want the user to lose focus on the gameplay.
Let's get to building a Button Enabled Skill!
Step 1 - Create a Custom Skill using the Alexa Skills KitIf you don't already have one, register for an Amazon Developer account through this link.
This is a free account that enables you to develop applications on the Amazon platform, including custom Alexa Skills.
When specifying interfaces, make sure and enable the two sliders for Gadget Controller and Game Engine under the Alexa Gadget section. This is what allows you to Also, I'm adding some display elements for users that have an Echo Show which requires the second slider to be enabled (this gets explained in step 7).
There is also additional information that needs to be provided when publishing the skill to the skill store. For this game, I'm indicating that two buttons are required, and it's a single player game.
This detail will be verified during the skill certification process.
Step 2 - Create a Lambda Function using AWSWhen building the skill, you will need to specify an API that contains the processing logic for the game. The best way to do this is through using the AWS Lambda service. The Alien Invader game is using the 6.x version of NodeJS.
To begin, leverage the template from one of the button skills that Amazon provides.
https://github.com/alexa/skill-sample-nodejs-buttons-trivia
Step 3 - Establish Roll Call ParametersThere are additional parameters needed to establish with a button skill. Need to specify the timeout parameter for when the buttons will turn off, as well as what events to listen for. In Alien Invader, here is the object that I am using.
{
"type": "GameEngine.StartInputHandler",
"timeout": 80000,
"recognizers": {
"button_down_recognizer": {
"type": "match",
"fuzzy": false,
"anchor": "end",
"pattern": [{
"action": "down"
}]
}
},
"events": {
"button_down_event": {
"meets": ["button_down_recognizer"],
"reports": "matches",
"shouldEndInputHandler": false
},
"timeout": {
"meets": ["timed out"],
"reports": "history",
"shouldEndInputHandler": true
}
}
}
The timeout attribute is in milliseconds - so this value translates to 80 seconds. I'm also listening for just the "button down" event, and not the "button up" event.
These parameters get set at the start of the game when the LaunchRequest event is triggered. To set these parameters, just add this as a directive in the response of your lambda function.
Step 4 - Enable registering buttons with the Custom SkillAfter the skill is invoked, you will need to create interaction with the user where they press the button. In this game, I am doing that by having the skill respond to the initial launch of the skill by giving an intro to the game, then by saying "Please press a button to begin setting up the game."
Once a user presses the first button, the game engine creates an event which then triggers the Lambda function. The function processes the event, including persisting the GadgetId in a Dynamo Table, and prompting the user to press a second button. After the user presses the second button, the gameplay begins.
When beginning the game, the buttons are in a dormant state. This is to conserve the battery life, and here's a picture of a button in this state.
After a user presses a button in the enrollment phase, a GadgetId comes back in the event. Within the Lambda function we can take this GadgetId and pass it back to manipulate that exact button, including changing the color.
The sample code from the templates provided by Alexa does most of the work for us, and all that's needed is to use the included helper functions, and specify which color to use.
The actual request that the gadget engine needs is "SetLight", then an object that gets used by the gadget to know what color to set the light, and if there's a sequence of changing the color of the light, what that pattern is.
For more specifics on this part of the gadget engine, here's the documentation.
// the button was just woken up, so color red and send animations
this.response._addDirective(buildButtonIdleAnimationDirective([this.event.request.events[0].inputEvents[0].gadgetId],
breathAnimationRed));
// animation settings for buttons
const breathAnimationRed = buildBreathAnimation('552200', 'ff0000', 30, 1200);
const breathAnimationGreen = buildBreathAnimation('004411', '00ff00', 30, 1200);
const breathAnimationBlue = buildBreathAnimation('003366', '0000ff', 30, 1200);
const animations = [breathAnimationRed, breathAnimationGreen, breathAnimationBlue];
const buildBreathAnimation = function(fromRgbHex, toRgbHex, steps, totalDuration) {
const halfSteps = steps / 2;
const halfTotalDuration = totalDuration / 2;
return buildSeqentialAnimation(fromRgbHex, toRgbHex, halfSteps, halfTotalDuration)
.concat(buildSeqentialAnimation(toRgbHex, fromRgbHex, halfSteps, halfTotalDuration));
};
const buildSeqentialAnimation = function(fromRgbHex, toRgbHex, steps, totalDuration) {
const fromRgb = parseInt(fromRgbHex, 16);
let fromRed = fromRgb >> 16;
let fromGreen = (fromRgb & 0xff00) >> 8;
let fromBlue = fromRgb & 0xff;
const toRgb = parseInt(toRgbHex, 16);
const toRed = toRgb >> 16;
const toGreen = (toRgb & 0xff00) >> 8;
const toBlue = toRgb & 0xff;
const deltaRed = (toRed - fromRed) / steps;
const deltaGreen = (toGreen - fromGreen) / steps;
const deltaBlue = (toBlue - fromBlue) / steps;
const oneStepDuration = Math.floor(totalDuration / steps);
const result = [];
for (let i = 0; i < steps; i++) {
result.push({
"durationMs": oneStepDuration,
"color": rgb2h(fromRed, fromGreen, fromBlue),
"blend": true
});
fromRed += deltaRed;
fromGreen += deltaGreen;
fromBlue += deltaBlue;
}
return result;
};
const rgb2h = function(r, g, b) {
return '' + n2h(r) + n2h(g) + n2h(b);
};
// Number to hex with leading zeros.
const n2h = function(n) {
return ('00' + (Math.floor(n)).toString(16)).substr(-2);
};
One the directive is received, the LED's light up accordingly.
The gameplay dictates what the color of the buttons should be. For example in this skill, it requires two buttons. One is red and one is blue.
Step 6 - Integrate Gameplay with ButtonsOnce the buttons have been registered, it's time to integrate them within the events that a skill receives.
The basic gameplay is that either a cat sound or an alien sound are played, and the user needs to press the appropriate button. When the button is pressed, an event gets sent to the Lambda function, just like when a microphone picks up a voice on the device.
The sounds that are being used to go along with the native Alexa voice come from the Alexa Skills Kit Sound Library. For example, the sounds of the aliens as well as the cats. Here's more details on what is available.
https://developer.amazon.com/docs/custom-skills/ask-soundlibrary.html
To use these sounds, the links are imbedded in SSML tagging within the response object that Alexa uses to play through the speaker.
Step 7 - Add Images for Echo Show (Optional)For added value and user experience, I've added a background image within the game. Once again this requires using directives within the Lambda function. This should be done for only devices that have a display, and the code for this is the following.
// These are the backgrounds used to display on the device screen
const startupImage = 'https://s3.amazonaws.com/<bucketname>/startupImage.png';
const skillName = 'Alien Invaders';
const startupTitle = 'Please Save the Cats';
// these utility methods for creating Image and TextField objects
const makePlainText = Alexa.utils.TextUtils.makePlainText;
const makeImage = Alexa.utils.ImageUtils.makeImage;
// these are needed to construct the directives that may be used for a display
const builder = new Alexa.templateBuilders.BodyTemplate1Builder();
const template = builder.setTitle(startupTitle)
.setBackgroundImage(makeImage(startupImage))
.setTextContent(makePlainText(skillName))
.build();
//render the response based on if the device has a screen
if (this.event.context.System.device.supportedInterfaces.Display) {this.response.speak(welcomeMessage).listen(welcomeReprompt).renderTemplate(template);
console.log("this was requested by an Echo Show");
this.emit(':responseReady');
} else {
this.response.speak(welcomeMessage).listen(welcomeReprompt);
this.emit(':responseReady');
}
Tips & ClosingAs I've tested this game out, I've noticed a few things that relate to user experience.
- Be ready for the condition that a button is pressed multiple times. What my game does is skip out the duplicate events based on timestamp for when the event was created. If you don't do this, the user experience degrades as the skill will assume that the second button event is in a response to the in-flight audio response.
- Reward users that use the skill multiple times. This can be done by checking if the user exists on the DynamoDB table that saves state. If someone is playing the game again and again, they don't need the same level of instruction at the start of the game, and it just slows the intro.
- Find ways of varying responses during repetitive parts of the game. For example, after shooting aliens in the game, the language varies. Sometimes it says "You got it!" other times it states "Good shot. That ought to scare it away." This variety improves the user experience.
Comments