Inspiration

It's winter, so I wanted to create a game that would simulate the season. This game attempts to create the excitement of having a snowball fight, but without the cold!

What it does

The game uses scenes to test a players judgement for when they should be throwing a snowball. There are different characters, and they read off scenarios then the user needs to determine if they want to throw a snowball. For example, if you see someone in a snow fort, then it's good to throw one. If it's your mother calling you with hot chocolate, the right answer is not to throw a snowball at her. The game goes through multiple rounds until the user makes a bad decision, then the game ends. It also tracks score along the way, and has both a single and two player mode.

How I built it

The basic architecture is fairly standard for Alexa skills. The logic for the game is hosted on a lambda function written in NodeJS. Game state is persisted to a DynamoDB table, and multi-media content for the game is managed via a publicly available S3 bucket.

The skill makes use of a number of features on the Alexa platform.

APL to Render Scenes on Devices with a Display

If the user has a display, the skill uses multiple custom APL documents that complement the voice experience, including providing the ability to track the current score in either a one or two player game.

Here's an example of one of the documents that shows a one player game in-progress.

This APL document contains multiple items - the game name at the top with the word art, the snow at the bottom, the house, and then two for game score - a label and the score value.

How to assemble the items is described by the items in the APL document, including the sequence by which they are assembled. Here's the APL document that matches the image above.


{
    "type": "APL",
    "version": "1.0",
    "theme": "dark",
    "import": [{
        "name": "alexa-viewport-profiles",
        "version": "1.0.0"
    }],
    "resources": [],
    "styles": {},
    "layouts": {},
    "mainTemplate": {
            "parameters": [
                "payload"
            ],
        "item": [
        {
            "type": "Frame",
            "backgroundColor": "#58ACFA",
            "items": [
                {
                "when": "${@viewportProfile == @hubRoundSmall}",
                "type": "Container",
                "height": "100vh",
                "width": "100vw",
                "items": [
                    {
                        "type": "Image",
                        "source": "https://s3.amazonaws.com/snowballgame/images/gameLogo.png",
                        "height": "20vh",
                        "width": "90vw",
                        "position": "absolute",
                        "left": "5vw",
                        "right": "5vw",
                        "top": "30vh"
                    },
                    {
                        "type": "Text",
                        "text": "Score",
                        "position": "absolute",
                        "left": "12vw",
                    "right": "12vw",
                        "bottom": "30vh",
                        "fontWeight": "900",
                        "textAlign": "center",
                        "fontSize": "10vw"
                    },
                                    {
                                        "type": "Text",
                                        "text": "${payload.scoreMetadata.data.title}",
                                        "position": "absolute",
                                        "left": "40vw",
                                        "right": "40vw",
                                        "bottom": "15vh",
                                        "textAlign": "center",
                                        "fontSize": "10vw"
                                    }
                ]
                },
                {
                "when": "${@viewportProfile == @hubLandscapeMedium || @viewportProfile == @hubLandscapeLarge || @viewportProfile == @tvLandscapeXLarge}",
                    "type": "Container",
                    "height": "100vh",
                    "width": "100vw",
                    "items": [
                    {
                        "type": "Image",
                        "source": "https://s3.amazonaws.com/snowballgame/images/gameLogo.png",
                        "height": "25vh",
                        "width": "90vw",
                        "position": "absolute",
                        "left": "10vh",
                        "top": "12vh"
                    },
                                        {
                                            "type": "Image",
                                            "source": "https://s3.amazonaws.com/snowballgame/images/snow_background.png",
                                            "height": "80vh",
                                            "width": "120vw",
                                            "position": "absolute",
                                            "left": "0vh",
                                            "top": "30vh"
                                        },
                                        {
                                            "type": "Image",
                                            "source": "https://s3.amazonaws.com/snowballgame/images/winter_house.png",
                                            "height": "50vh",
                                            "width": "50vw",
                                            "position": "absolute",
                                            "right": "0vh",
                                            "bottom": "10vh"
                                        },
                                        {
                                            "type": "Text",
                                            "text": "Score",
                                            "position": "absolute",
                                            "left": "15vh",
                                            "top": "40vh",
                                            "fontWeight": "900",
                                            "fontSize": "6vw"
                                        },
                    {
                        "type": "Text",
                        "text": "${payload.scoreMetadata.data.title}",
                        "position": "absolute",
                        "left": "45vh",
                        "top": "40vh",
                        "fontSize": "6vw"
                    }
                    ]
                }
            ]
        }]
    }
}

This same APL document is used to render on different devices, and alters what gets displayed. Here is the same state of the game on a small hub - aka an Echo Spot.

Given the smaller screen size, the images are not rendered, rather the display space is focused on the current score of the game.

For the two player game, there is a different APL document given that there are two different text items, one for each score. It's also responsive for the screen size.

By making the score entries different items, the style for them can be different - for example, the color changes.

Echo Buttons for Multi-Player Game

If the user has echo buttons, they can register them, and it replaces having to say yes or no to a given scenario. It also enables a two player game where the first person to answer correctly wins a point.

Buttons can be attached to an Alexa device, then they communicate back to the lambda function for the skill through events similar to that generated by the microphone or screen on the device.

A separate APL document is used for a two player game rendering the score of the head-to-head matchup.

The gameplay when using Echo Buttons creates some urgency as the user only has a certain amount of time to determine if a snowball should be thrown. If they wait too long when a snowball should be thrown, one gets thrown at them. This is done by the timeout parameter in the buttons that creates an event back to the Echo device after a certain amount of time has passed.

Polly for simulating different characters

The speech rendered by the game uses Polly voices to create characters within the game. This can be done with SSML markup, and using the "voice name" tag. Here are a few entries from the scenarios array that shows how to do the markup.

    {
        "throwNeeded":true,
        "timeout":25000,
        "description":"You see a blue hat above the wall of the snow fort.",
        "successMessage":"Nice shot!<break time=\"1s\"/><voice name=\"Justin\">Where did that come from?</voice><break time=\"1s\"/>You knocked his hat off with that throw.",
        "errorMessage":"Too late. You just got blasted with a snowball.<break time=\"1s\"/><voice name=\"Justin\">Ha Ha!</voice>"
    },
    {
        "throwNeeded":false,
        "timeout":25000,
        "description":"You hear your mom calling you.<break time=\"1s\"/><voice name=\"Joanna\">I made some hot chocolate. Get it before it turns cold!</voice>",
        "errorMessage":"Oh no.<break time=\"1s\"/><voice name=\"Joanna\">Okay, time to go inside. No throwing snowballs at your mother!</voice><break time=\"1s\"/>Next time don't press $
        "successMessage":"Smart move. No need to throw a snowball at your mother, and this hot chocolate has marshmallows!"
    },

This adds variety into the audio experience, but doesn't require recording different sound files - rather the SSML speech output can be used to drive the audio of the different characters.

Challenges I ran into

It takes some practice to learn APL, especially how to pass in dynamic data into an item. Once I got the syntax down, it worked great, and the simulation tool for APL was really helpful to use and saved me a bunch of time.

Accomplishments that I'm proud of

Any time you can use a new beta feature on the platform, it's a great way of learning how the platform is evolving. I've used the prior templates to build images for skills, and APL is much more flexible, especially for games where you're looking to provide scoring details.

What I learned

I hadn't ever been able to control the difference between what renders on different devices, and it was great to be able to do this given how diverse the variations of screens. For example, an Echo Spot screen is quite small, so just being able to show a numeric score takes up a large part of the interface. For something large, such as a flat screen TV, there is plenty of space, so it's great to be able to put some contextual background images, as well as be very descriptive on the number of players as well as the score.

What's next for Alexa Skill Snowball Fight with APL

Continue to add more scenarios into the game to make it more exciting, as well as refine how the APL documents are used. Like any modeling exercise, there seems to be a balance between how many unique documents to create vs. having the items as dynamic variables that are changed within the game logic.

Built With

Share this project:

Updates