Amazon Fire and GameCircle


Introduction

Welcome to the Amazon Fire tutorial. In this tutorial we will take you through creating an App Listing on the Amazon App Store, uploading a file to the app store for testing, and the integration of Amazon Game Circle services as well as the addition of In App Purchases to the test project. We won't be creating a full game for this tutorial, but instead a simple test app with some buttons and things to show the basic functionality and make it easier for you to follow along and then integrate what you've learned with full game projects.

Before going any further, there are a few things that you will need to get and set up:

  • A physical Amazon Fire device for testing your projects and connecting with GameMaker Studio 2. Make sure that the device is set to Developer Mode and discoverable using ADB.

  • An Amazon Developer account. You can find out how to get one and set it up here.

  • An Amazon Fire licence for GameMaker Studio 2. You can get a licence for Amazon Fire through several different mechanisms which are outlined here (note that there is a 60 day FREE trial available).

  • Have set up the Amazon Fire Preferences in GameMaker Studio 2 (you can find detailed setup details here) and be able to test run projects on your device from GMS2.

If you have not done any of the above then you should take some time to do so, otherwise the steps outlined this tutorial will not permit you to create the test project correctly.

If you have completed all of the above requisites then you are almost ready to set up an Amazon Developer app listing for testing, but before we do we need to make sure that the test project can run on your Fire device, so go to the Target Manager and select the Amazon Fire target, VM, and your test device then press Play :

Test App Preview Image

You should see on your Fire device an image similar to the one shown above. If you are getting any issues, then you should make sure that you have everything set up correctly in the Preferences and Game Options, as well make sure that the device is in developer mode and has ADB debugging enabled.

IMPORTANT! The test project has the Game Options set up already, but these values may not reflect the build tools that you have installed so you should check and make sure that the values for min/max SDK and Build tools are correct for what you have installed in Android Studio.

Before continuing you should take a moment to check each of the objects provided in the test project. Most of what we'll be adding will go into the object "obj_Control" and we'll be using a number of global variables to make it easier to access the different pieces of information we'll require. The variables are initialised in the Game Start event of the controller object, so make sure to look that over and get an idea of what we'll be doing later.


Set Up Store

Before we continue using GameMaker Studio 2 we need to first leave it to one side and go to the Amazon Developer page and create a listing on it for our test app. If you haven't already got a developer account, signing up with Amazon couldn't be easier. Just go to their App Distribution page and sign in if you have an Amazon account already, or sign up if you do not. There is no registration fee and all you have to do is fill in the necessary personal details as well as those of your company(should you have one).

NOTE: If you wish to monetise your apps and games, you should add in your bank details too, so make sure that you have your IBAN and Swift code available as these will be needed.

Once that has been done, you will be taken to the main "Home" screen where you can see the latest announcements and the "Dashboard" for your uploaded apps. Click on the Add a New App button to get started.

Add New App Button

You will now be asked to select a target platform for the app being submitted, and in this case you should select Android (Amazon Fire devices use Fire OS which is based on the Android platform). You will then be presented with the following page for filling out the initial submission details:

New Submission

The form requires the following information:

  • App Title: The display name for your app, in this case call it Game Circle Test App or something similar.

  • App SKU: The SKU is a unique identifier that you need to add for your app and should follow the reverse URL format, ie: com.COMPANYNAME.gctestapp.

  • Category: For our test app this is not important so just select any of the category and sub-category items that you wish.

  • Customer Support Contact: Checking the box marked "Use my default support information" will tell Amazon to use the default support email and URL that you supplied when registering, but if you wish to have something independent for each app then you can un-check this which will then make the following options available too:

    • Customer Support Email Address: The email address where people can contact you for product support.
    • Customer Support Phone: A telephone number for support issues.
    • Customer Support Website: The website for support issues

Once you are happy with the details provided, click the Save button to continue. Should there be any problem with the information provided, the problem fields will be marked and you will be given an opportunity to resolve the issues and continue again (this is the same on all the subsequent tabs).

We don't need to fill in any further details right now, but instead set up a Security Profile which is required by Amazon to use certain GameCircle and IAP features. This is done from the Game Circle section of the main Dashboard (there should be a link at the top of the developer page):

Game Circle Link

Now you need to click the Add a configuration button:

New GameCircle Configuration

Next you'll have to select from either an app that is already on the app store, or an app that is not on the App Store, and in this case we choose the first section, since we've already started the submission process and given initial details of the application. You'll then have to select the game from the drop down list and create a security profile for the application. You give the profile a name and a short description - you should name it something relevant like "Game Circle Test App", and ensure that the description is adequate for you to remember what you used it for in the future:

Security Profile Title Data

Click "Save and Continue" to move on to the next page where we will generate the API key that is required to use any Amazon services. We need to add some details here that will be specific to our app and the keystore file that is used for signing it in GameMaker Studio 2 so first give the API Key Name (which should be the same as the test app, ie: "Game Circle Test App" or whatever you used), and then the Package Name (which is the SKU for the app, ie: com.COMPANYNAME.gctestapp). You will now need to go back to GameMaker Studio 2 to get the required key hashes.

In GameMaker Studio 2 open the Preferences and go to the Platform Settings > Amazon Fire section. You should have added or created a keystore file when you set everything up originally, so now all you need to do is click on the Show Key Hash button:

Show Key Hash

This should generate an MD5 key hash and a SHA keyhash, but note that these are not the ones shown in the Preferences window! Instead you need to find them from the Output Window at the bottom of the GameMaker Studio 2 IDE:

Find Key Hash In GMS2

You should copy these into the appropriate sections of the Amazon Developer security profile page and then click the "Generate key now" button:

Security Profile Data

On the page that follows you need to make a copy of the API key to add into GameMaker Studio 2:

Get The API Key

Now you've copied that to your clipboard, we can go back GameMaker Studio 2 where we can set up the test application to use the Amazon GameCircle and IAP features... Don't worry about the rest of the Game Circle setup for now, as we'll come back to it later to add in further features as we need them.


Set Up GameMaker Studio 2

You should have the API key for your test app copied to the clipboard, so we will now add that into a text file and then import it into GameMaker Studio 2 for use. So, create a text file now somewhere safe and call it api_key.txt, then paste in the key from the Amazon Developer page and save the file. In GameMaker Studio 2, you need to add this as an Included File, so go to the Included Files entry in the Resource Tree and right click then select Insert Included File:

Insert Included File

You will need to double-click on the new included file now to open the file properties window and make sure that the only ticked export option is Amazon Fire (if you don't have any other target platforms then this step can be omitted):

File Properties

With that done, you will also need to go to the Amazon Fire Game Options and make sure that the general information is correct, specifically the app title and the SKU/Package ID that is to be used. The tutorial comes with these details filled in for YoYo Games, but they are not valid for use, and so you must change them. You should set the Display Name to the name that you gave in the Amazon Developer console, and then set the package Domain, Company and Product details using the values given for the SKU/Package ID. So, for example, if you have given the SKU/Package ID "com.mcsweenygames.GCTestApp" then the Game Options should look like this:

Game Options

IMPORTANT! You should make sure that the package details are all lower case, otherwise you may get build errors due to the way the Android build tools work.

We now need to add the Game Circle extension from the Marketplace to the project. To do that, go to the AddOns section of the Main Game Options (double click on the "Main" game options in the resource tree, then select "Addons") and you will see an option to get the Amazon Fire API extension:

Get The GameCircle Extension

Click the download button to get the extension, and then when the Game Options window shows "Installed" you can expand the Extensions section of the resource tree and see the different extensions there:

The GameCircle Extensions

It's important to note that both general Game Circle and In App Purchases are added as separate extensions, but if your project doesn't require IAP, then you can safely remove that extension (leaving it will mean that your project will require extra, and unnecessary, permissions). However, in this tutorial we are going to cover both Game Circle and IAP, so you don't need to do anything else with them.


Initialising GameCircle

Before we continue on to actually add in the Amazon features that the extensions provide, we need to first initialise the Amazon API and tell it which features we are going to use. This is done by using bitwise "or" to create a bitmask of the features we require, using the following constants:

ConstantDescription
AmazonGameCircle_AchievementsInitialise the achievements API
AmazonGameCircle_LeaderboardsInitialise the leaderboard API
AmazonGameCircle_WhispersyncInitialise Whispersync to synchronise game data over multiple devices
AmazonGameCircle_ProgressInitialise the achievements progress API - this permits achievements to be progress based instead of a simple yes/no
AmazonGameCircle_IAPInitialise the in app purchase API


We now need to add some code to the Game Start event of the object obj_Control:

AmazonGameCircle_InitFeatures(AmazonGameCircle_Achievements |
		        AmazonGameCircle_Leaderboards |
		        AmazonGameCircle_Whispersync |
		        AmazonGameCircle_Progress |
		        AmazonGameCircle_IAP);

It is very important that this function gets called before any other extension function otherwise you may get errors in your games, and note that when you are using this in your own projects you can omit those constants that refer to features you do not wish to use (but for this tutorial we want to initialise all of them).

You should now be able to run the test project (press Play in the GameMaker Studio 2 IDE) and when it starts on the Fire Device, you will be logged in to your test account and a notification will be shown:

Sign In Testing

If you do not get the log in message then you should check the console output for an error message that looks like this:

I/yoyo    (15424): Attempting to initialize Amazon Game Circle via extension
		I/yoyo    (15424): Unable to initialise AmazonGamesClient - service not ready. Status=CANNOT_INITIALIZE

When this happens you should go back and double check the following:

  • that the package details given in the Game Options match exactly what you gave as the Package Name for the security profile (Remember! The package name should be all lowercase)

  • that you have the correct API key stored in the api_key.txt included file

  • that the api_key.txt included file is set to export to the Amazon Fire target

Once you have it all working and the login window pops up, you are almost ready to start adding features! However, we need to add another bit of code first to permit the player to open the GameCircle interface and log in/out at any time. We already have a button for this prepared in the test project so open "obj_Button_ShowGameCircle" now, and then add a Step Event to it.

In this event we will add the following code:

/// @description Show Game Circle UI
		
		if pressed
		    {
		    AmazonGameCircle_Show();
		    }

You can test the project again now, and clicking the onscreen button labelled "Show GameCircle UI" will open up the GameCircle interface and permit you to change your username and other details.

Show GameCircle UI


Get Player Data

With our test project now signing in to the GameCircle API, we can use it to do some other fun things like get the logged in player name or their avatar. This will be done through the Social Asynchronous Event which is an object event triggered when an external application sends information to GameMaker Studio 2. In this case, it will be triggered when the GameCircle API returns information about the game and player on player sign in.

This Social Event will always contain a DS Map, stored in the variable "async_load". In this case, the ds_map will always have an "id" key, and we will check this against a number of built-in GameMaker Studio 2 constants and then do different things depending on the returned value. In this case we want to check and see if the "id" is equal to the constant "achievement_our_info".

Open the object "obj_Control" now and add an Asynchronous Social Event:

Add Async Social Event

The asynchronous events are events that are triggered at any time in the game loop by an external source. This can be an image loading, or a network event, or, as is the case with the Amazon extension, a social event. Regardless of the reason for an asynchronous event being triggered, it will always contain the async_load variable with a DS map, and the first thing we need to do is parse this map and get the "id" of the event so we know how to handle it, so add this code to the Async Social Event:

var event_id = ds_map_find_value(async_load, "id");

The DS map in the Social Event will always have an "id" key and we use this to tell us which type of information has been received. The easiest way to do this is using a switch which will have different "cases" for each of the possible incoming event id's. We'll add that now and have it populate some of the general global variables that we have initialised previously:

NOTE: If the code doesn't fit the window properly, you can click and drag the edge to expand it further and see it fully.

switch (event_id)
		    {
		    case achievement_our_info:
		        global.player_signedin = AmazonGameCircle_IsSignedIn();
		        global.player_name = ds_map_find_value(async_load, "name");
		        global.player_id = ds_map_find_value(async_load, "playerid" );
		        global.player_avatar_url = ds_map_find_value(async_load, "avatar_url" );
		        global.player_avatar = sprite_add(global.player_avatar_url,0,0,0,0,0);
		    break;
		    }

The event we are checking for is to see if the player has signed in to Amazon Game Circle and get their basic player data, which means we get their name, unique ID value, and a URL for an avatar image which we then turn into a sprite so we can display it. We also use another extension function AmazonGameCircle_IsSignedIn to set the global variable that checks if the user is signed in or not.

If you run the test app on your Fire Device now, you should see that when the user is signed in the information in the top left of the screen is updated:

On-screen User Data

You can also use the "Show GameCircle UI" top open up the GameCircle interface and change the user name or avatar, and when you close it again, the Social event will be triggered again and the data shown on the screen updated to match.


Adding Achievements And Leaderboards

We are going to add an achievement to our test app (in your own games you'll probably want to add more than one achievement, but all you'll have to do is simply repeat the following for each one you want to add). We need to go back to our browser and the Amazon Developer dashboard where we started setting up GameCircle (see the Set Up Store page if you need to refresh your memory), and here add in our achievement by clicking the appropriate button:

Add An Achievement

This will open the "Create Achievement" window which you should fill in with the following data (you can find the test icons used in this tutorial here):

  • Achievement ID: TestAchievement

  • Title: Test Achievement

  • Hide Until Earned: No

  • Achievement XP: 50

  • Locked Achievement: You'll need a 512x512 image and set the description to "Gain 500 score to unlock"

  • Unlocked Achievement: You'll need a 512x512 image and set the description to "You gained 500 score!"

Achievement Data

You should now click the "Save" button and the data will be saved and added to the achievements list. We will now go on to add a Leaderboard for the app, so click "Continue" and then on the next page click the "Add Leaderboard" button, then fill in the following information:

  • Leaderboard ID: TestLeaderboard

  • Title: Test Leaderboard

  • Description: This is a Test Leaderboard

  • Leaderboard Icon: You'll need a 512x512 image to represent the leaderboard

  • Score Units: Points

  • Score Threshold: You can leave this blank

  • Sort Order: Set this to "Highest Scores First"

Leaderboard Data

The final thing we need to do here before going back to GameMaker Studio 2 is to add at least one username to the Test Accounts section, so click "Continue" and add the username of the test account you want to use (if you aren't sure, then run the Test App again, and open the GameCircle interface to get the username that was given to you initially):

Test User Name

Now click on "Finish" to take you back to the GameCircle configuration screen where you should see your achievements and leaderboards listed as "draft" beside your project:

GameCircle Draft Achievements and Leaderboards


Coding Leaderboards

Now that we have added achievements and leaderboards, we want to show them in the test project. There are two ways that we can do this:

  • Show the Amazon GameCircle UI for the required data

  • Retrieve the data and show it ourselves using GameMaker Studio 2 functions


We'll show you now how to do both these things, starting with opening the GameCircle interface. For that you will need to go back to GameMaker Studio 2 and open the object "obj_Control" then go to the Room Start Event. We already have some code prepared for this:

Empty Room Start Event Code

You will need to edit this code to look like this:

NOTE: If the code doesn't fit the window properly, you can click and drag the edge to expand it further and see it fully.

switch (room)
		    {
		    case rm_Achievements: AmazonGameCircle_ShowAchievements(); break;
		    case rm_Leaderboards: AmazonGameCircle_ShowLeaderboards(); break;
		    }

Now when you run the game and go to the rooms for achievements and for leaderboards (click the "Show Leaderboard" or "Show Achievements" buttons) you will be presented with the Amazon Game Circle UI, and it will be open on the selected sections. You can test this right now if you like before we continue...

GameCircle Leaderboard UI

This is fine if you want to permit the user to open the leaderboard UI from within your game without having to show the initial GameCircle screens, but if you want more control over the information shown or you want to style how the information is displayed so that it suits the aesthetic of your game then you need to do a bit more. Specifically, we need to get the information on the achievements and the leaderboard and then use the GameMaker Studio 2 draw functions to display them.

To start with, we need to get the relevant data from GameCircle. For that we call the extension function AmazonGameCircle_LoadLeaderboard in the Create Event of the "obj_Control" object. Open that now and you'll see we already have some code that will set the different title text variable for each room so we'll simply add to that and set the case for "rm_Leaderboard" like this:

case rm_Leaderboards:
		    txt = "Leaderboards";
		    AmazonGameCircle_LoadLeaderboard("TestLeaderboard", 1, 5);
		    break;

Make sure that the name of the leaderboard that you enter here matches the leaderboard title as given on the Amazon dashboard. We now need to get the data that GameCircle returns to us and for that we need to open the Social Asynchronous Event. Again, we already have a switch set up so we can simply add to that to get the relevant data and store it all in some global variables. So, add this case to the switch:

case achievement_leaderboard_info:
		    global.lb_name = ds_map_find_value(async_load, "lb_name");
		    global.lb_text = ds_map_find_value(async_load, "lb_text");
		    global.lb_id = ds_map_find_value(async_load, "lb_id");
		    global.lb_numscores = ds_map_find_value(async_load, "lb_numscores");
		    global.lb_url = ds_map_find_value(async_load, "lb_url");    
		    global.lb_sprite = sprite_add(global.lb_url, 0, 0, 0, 0, 0);
		    for(var i = 0; i < global.lb_numscores; i++;)
		        {
		        global.lb_score[i] = ds_map_find_value(async_load, "lb_score" + string(i));
		        global.lb_rank[i] = ds_map_find_value(async_load, "lb_rank" + string(i));
		        global.lb_playeralias[i] = ds_map_find_value(async_load, "lb_player_alias" + string(i));
		        global.lb_playerid[i] = ds_map_find_value(async_load, "lb_player_id" + string(i));
		        global.lb_avatar_url[i] = ds_map_find_value(async_load, "lb_player_avatarurl" + string(i));      
		        global.lb_avatar_sprite[i] = sprite_add(global.lb_avatar_url[i] ,0,0,0,0,0);
		        }
		    break;

This code simply stores the leaderboard data in some global variables and then uses a for loop to create an array of entries for each score on the leaderboard.

The next thing to do is to display our leaderboard data, and that'll be done in the Draw Event of the object by adding the following case to the code already there:

case rm_Leaderboards:
		    draw_set_halign(fa_left);
		    draw_set_font(fnt_Arial);
		    var _x = (room_width / 2) - 100;
		    var _y = 150;
		    if sprite_exists(global.lb_sprite)
		        {
		        draw_sprite_stretched(global.lb_sprite, 0, _x + 50, 20, 100, 100);
		        }
		    for (var i = 0; i < global.lb_numscores; i++;)
		        {
		        if sprite_exists(global.lb_avatar_sprite[i])
		            {
		            draw_sprite_stretched(global.lb_avatar_sprite[i], 0, _x - 25, _y, 50, 50);
		            }
		        draw_text(_x + 25, _y + 20, global.lb_playeralias[i] + "(" +global.lb_playerid[i] + ")"); 
		        draw_text(_x + 25, _y + 40, string(global.lb_rank[i]) + " - " + string(global.lb_score[i]));
		        _y += 60;
		        }
		    _x += 170;
		    draw_text(_x, 30, global.lb_name);
		    draw_text(_x, 50, global.lb_id);
		    draw_text(_x, 70, global.lb_text);
		    draw_text(_x, 90, global.lb_numscores);
		    draw_text(_x, 110, global.lb_url);
		    break;

Our final task is to permit the user to submit their score to GameCircle, and for that we need to open up the button object "obj_Button_SubmitScore". Add a Step Event to the object and in it add the following code:

if pressed
		    {
		    AmazonGameCircle_PostLeaderboardScore("TestLeaderboard", global.game_score);
		    }

You should run the test project now on your Fire device. When it's running, you should click the "Play Game" button and then add some value to the score and click the "Submit Score" button to submit it to GameCircle. You can leave the game room now (click "Back"), and click the "Show Leaderboards" button. The GameCircle UI will show first, but if you close that you will now see the Leaderboard data displayed, with the score you achieved and the rank, avatar and player name and ID displayed too:

GameCircle Leaderboard In GameMaker Studio 2


Coding Achievements

We can now go on and code the Achievements display for our test project. This will be done in a similar way to the leaderboards, so first we need to request the achievement data from the GameCircle API. For that, open the object "obj_Control" and go to the Create Event, where you'll need to change the "rm_Achievements" case to look like this:

NOTE: If the code doesn't fit the window properly, you can click and drag the edge to expand it further and see it fully.

case rm_Achievements:
		    txt = "Achievements";
		    AmazonGameCircle_LoadAchievements();
		    break;

Still in the "obj_Control" object, open the Social Async Event and add this case to the switch, under the case for leaderboards:

case achievement_achievement_info:
	        global.ach_num = ds_map_find_value(async_load, "ach_num");
		    for(var i = 0; i < global.ach_num; i++;)
		        {
		        global.ach_dateunlocked[i] = ds_map_find_value(async_load, "ach_dateunlocked" + string(i));
		        global.ach_description[i] = ds_map_find_value(async_load, "ach_description" + string(i));
		        global.ach_id[i] = ds_map_find_value(async_load, "ach_id" + string(i) );
		        global.ach_pointvalue[i] = ds_map_find_value(async_load, "ach_pointvalue" + string(i));           
		        global.ach_position[i] = ds_map_find_value(async_load, "ach_position" + string(i));     
		        global.ach_progress[i] = ds_map_find_value(async_load, "ach_progress" + string(i));
		        global.ach_title[i] = ds_map_find_value(async_load, "ach_title" + string(i));
		        global.ach_is_hidden[i] = ds_map_find_value(async_load, "ach_is_hidden" + string(i));
		        global.ach_is_unlocked[i] = ds_map_find_value(async_load, "ach_is_unlocked" + string(i));
		        global.ach_imageurl[i] = ds_map_find_value(async_load, "ach_imageurl" + string(i));   
		        global.ach_sprite[i]  = sprite_add(global.ach_imageurl[i], 0, 0, 0, 0, 0);
		        }
		    break;

This will loop through all the defined achievements and add the data for them into a number of global variables. To draw these we'll use the following code in the switch in the Draw Event of the object (we won't draw all of them though, only the most important), so add this there:

case rm_Achievements:
		    draw_set_halign(fa_left);
		    draw_set_font(fnt_Arial);
		    var _x = room_width / 2;
		    var _y = 200;
		    for(var i = 0; i < global.ach_num; i++;)
		        {
		        if(sprite_exists(global.ach_sprite[i]))
		            {
		            draw_sprite_stretched(global.ach_sprite[i],0,_x - 100, _y - 20, 100, 100);
		            }
		        draw_text(_x + 30, _y + 10, global.ach_title[i]);
		        draw_text(_x + 30, _y + 30, global.ach_is_unlocked[i]);     
		        draw_text(_x + 30, _y + 50, global.ach_description[i]);
		        _y += 60;
		        }
		    break;

Finally, we need to tell GameCircle when the user has gained the achievement, so for that we need to open up the object "obj_Button_Add" and in the Step Event change the code already there to look like this:

if pressed
		{
		global.game_score += 10 + irandom(10);
		if global.game_score > 500
		    {
		    AmazonGameCircle_AchievementProgress("TestAchievement", 100);
		    }
		}

Here we simply check the value of the score and, since this achievement is either true or false, we set the value of the achievement to 100, since achievements are calculated as percentage values from 0 to 100 (so 100% means the achievement is granted).

You can test the test project on your fire device now and when you go to the "Play Game" room and increase the score, you should see a notification for the achievement when it goes above 500:

Earn The Achievement

If you then leave the game room and click the "Show Achievements" button, you'll be taken to the achievements room where you'll see the achievement data drawn for you (after you close theGameCircle UI):

The Achievement Drawn In GameMaker Studio 2


Tidying Up

Things are looking good for our test project and if all has gone correctly you should be able to view the achievement and leaderboard that we added previously. There is one final thing that we need to look at before moving on to the next part of this tutorial, and that's the part where we tidy up a few loose ends...

The first thing we need to do is remove the sprites that get added for leaderboard avatars and for achievements. At the moment, these are retrieved from the returned ds map in the Social Asynch Event, which is triggered every time we enter either "rm_Achievement" or "rm_leaderboard". This means that if we visit those rooms more than once, then we'll be loading new sprites every time, and these new sprites will overwrite the global array that stores the ID of the image to use... This is called a "memory leak" and is caused by us filling up the device memory with images that we can no longer access to free them (or use them).

To resolve this issue we're going to add a Room End Event into our controller object "obj_Control". Open it now (if it's not already) and add the event:

The Room End Event

Here we'll add the following code to parse the loaded sprites and delete them, freeing the memory associated with them at the same time:

NOTE: If the code doesn't fit the window properly, you can click and drag the edge to expand it further and see it fully.

/// @description Tidy Up
		
		switch (room)
		{
		case rm_Leaderboards:
		    if sprite_exists(global.lb_sprite)
		        {
		        sprite_delete(global.lb_sprite);
		        }
		    for (var i = 0; i < global.lb_numscores; i++;)
		        {
		        if sprite_exists(global.lb_avatar_sprite[i])
		            {
		            sprite_delete(global.lb_avatar_sprite[i]);
		            }
		        }
		    break;
		case rm_Achievements:
		    for (var i = 0; i < global.ach_num; i++;)
		        {
		        if sprite_exists(global.ach_sprite[i])
		            {
		            sprite_delete(global.ach_sprite[i]);
		            }
		        }
		    break;
		}

Once you've done that you will need to open a browser and go to your Amazon Developer page for the app. We are now going to tidy up the leaderboard and achievements here. We do this so that we can test them again and again as if the app was only just installed on the device, which is especially useful when dealing with achievements. Just to refresh your memory, you want to select the app from the main developer dashboard, then click the "GameCircle" link at the top, and then select the "Game Circle Test App" configuration:

GameCircle Configuration Page

You then need to go to the Achievements section, click the arrow beside the "Edit" button and select "Reset":

Reset Achievement

This will reset the achievement for all test users of the project, enabling you to test more than once each achievement. For leaderboards you have to do the same essentially and go to the Leaderboards section then click on the arrow beside the "Edit" button and select "View Scores":

View Scores

Here you can either remove individual scores from the leaderboard, or you can choose to completely reset the leaderboard (removing all entries from it):

Reset Scores

With that done, you can continue to test your project again and again! Note that you can perform these actions while the test project is running on the Fire device, and when you enter either the Achievements or the Leaderboard rooms, then the changes will be reflected, so no need to restart the test project every time you reset an achievement etc...


Set Up In App Purchases

If you don't want In App Purchases in your projects then this tutorial ends here (don't forget to remove the IAP extension from your projects if it's not being used!), but if you do want some kind of IAP then over the next few sections we'll explain how to set them up and get them working on our test project.

Essentially, in app purchases come in two types:

  • Consumable: A consumable purchase is one that the user can buy again and again, for example "coin packs" or "gems" to be spent as in-game currency.

  • Entitlement: An entitlement purchase is for something that you only buy once and then never again, like a "remove ads" feature or extra levels.

In our test project we're going to set up one of each type.

To start with we need to open a browser and go back to our Amazon Developer dashboard where we'll set up each purchase type. We can do this from the In App Items section, and we'll start by adding a consumable, so click the "Add A Consumable" button to continue:

Add A New Consumable IAP

On the page that follows you need to give a Title for the consumable, which in this case should be something like "Test Consumable" as well as an SKU string, which must follow the reverse URL format of DOMAIN.COMPANY.PRODUCT.CONSUMABLE where the initial three fields match those that are in the the Game Options package settings in GameMaker Studio 2, ie: com.mcsweeney.gctestapp.testconsumable. You don't need to do anything further on this page and so can click the "Save" button:

New Consumable Details

Once saved you need to go to the "Availability And Pricing" section and make sure that the consumable is set to "free" for now (for testing we'll keep it simple to get it working first then we can go back and put a price on it later).

Consumable Pricing

Next we need to give a description for the consumable IAP as well as some keywords to help optimise searching. Since this test project will never be made "live" on the app store, we won't bother adding keywords, but you should give a simple description of the consumable before clicking "save" to continue:

Consumable Description

Finally we are required to add in a couple of icon images that will be used by the test project and the Amazon Store to display our app and purchase options. These images should be 114x114px and 512x512px PNG format files (you can find the test icons used in this tutorial here):

Consumable Icons

Click "Save" now and if all has gone correctly you should see the titles of each section with a green tick beside them (if any do not have a tick, it means you didn't save the changes to the section so go back and revise each section and click "Save" again). You now need to submit the in app item to the store for it to become available to our test app, so click the "Submit In-App Item" button now:

IMPORTANT! Once an IAP has been submitted to the Amazon Store, it cannot be edited again, nor can you delete IAP items that have been submitted, so ensure that everything is correct before you hit the "Submit" button!

Submit Consumable In App Item

If you go back to the main In App Purchase page, you can now click the "Add An Entitlement" button:

Add A New Entitlement IAP

The process for setting up an entitlement purchase is the exact same as for the consumable setup:

  • Give a title ("Test Entitlement") and the SKU (com.[COMPANYNAME].gctestapp.testentitlement) for the entitlement

  • Click the "Availability and Pricing" section, set to "free and available immediately" then save

  • Click the "Description" section and add some text to describe the purchase then save

  • Click the "Images" section and add icons (a 114x114px image and a 512x512px image - you can find some premade ones here) then save

  • Click the "Submit In-App Item button

While you are in the IAP dashboard, you should download the JSON Data file for your newly created in-app purchases. This JSON will be required later to enable testing of the purchases on your Fire device:

Download JSON Data For Purchases

Once you have saved this file to a secure location on your computer, you will need to go to the Amazon Store and download the Amazon App Tester application to your fire device. You can get it here: Amazon App Tester. Once it is downloaded you need to open your device storage in a file window on your computer and copy the JSON to the root directory:

Copy JSON File To Fire Root Directory

Once this has been completed, you can then open the Amazon App Tester and go to the page for the "In-App Purchasing API" and select option 5 - "IAP Items In JSON File":

Add JSON To Tester App IAP

If everything has gone correctly then you should now see a listing of the IAPs that you created for our test project (if you get an error, then you should go back and make sure that your device is set to MTP for file transfer and make sure that the JSON has been copied to the correct place):

IAP Added To Tester App

With all that done, we are ready to start adding the necessary code to our test project in GameMaker Studio 2...


Set Up IAP In GameMaker Studio 2

It's time now to add the code required to use in-app purchases in GameMaker Studio 2. However, before we start, let's just outline the flow of things so you can get an idea of what we'll be doing and why...

  • To start with, we need to add our in app purchase SKUs to our test project, which will initialise them.

  • We then request product information which can be stored and displayed to the user if required (or used for debugging as we'll do in our test project)

  • After that we'll need buttons to purchase the products

  • The buttons will send a purchase request to the IAP extension, which in turn will communicate with the Amazon servers and return data about the purchase

  • The returned data is parsed and dealt with in different ways depending on whether it is a consumable or an entitlement:

    • A consumable will be used at the moment of receipt and a global variable will be set to show this. The user can then buy further consumables, but note that each one must be used before another can be bought.

    • An entitlement will set a global variable once and then the user will be unable to buy further entitlements.

  • Finally the sales data is perpetuated to the Amazon cloud so that the user does not have to pay twice for the same IAP.

That's a lot to get through so we'll take it a step at a time, starting with telling our test project that there are IAPs available. This means adding some code to the object "obj_Control", so open that now and go to the Game Start Event. Scroll down to the section labelled "IAPs" and add the SKUs for the in-app purchases we defined on the Amazon Developer site, like this:

global.iap_consumable = "com.mcsweeneygames.gctestapp.testconsumable";
		global.iap_entitlement = "com.mcsweeneygames.gctestapp.testentitlement";

We store the SKU strings in global variables to make it easier to identify them as we'll be accessing them at various points throughout the code. Next we need to add these purchases to our app and retrieve the relevant information about them using the following functions (add these at the end of the Game Start Event, but before the function call to room_goto_next()):

AmazonGameCircle_AddIAP_SKU(global.iap_consumable);
		AmazonGameCircle_AddIAP_SKU(global.iap_entitlement);
		AmazonGameCircle_GetProductData();

IAP Initialise On Game Start

This should initialise the IAPs for the test project and also send a request to get information on each product item. This information will returned in the Social Async Event which you should open now. Scroll to the end of the current code and add this case to the switch:

NOTE: If the code doesn't fit the window properly, you can click and drag the edge to expand it further and see it fully.

case AmazonGameCircle_ProductDataResponse:
	        var status = ds_map_find_value(async_load,"status");
	        if status == "SUCCESSFUL"
	            {
	            var num_unavailable_skus = ds_map_find_value(async_load, "num_unavailable_skus");
	            for(var i = 0; i < num_unavailable_skus; i++;)
	                {
	                var nosku = ds_map_find_value(async_load,"unavailable_sku_" + string(i));
	                show_debug_message("Sku: " + string(nosku) + " is not available");
	                }
	            var num_products = ds_map_find_value(async_load,"num_products");
	            for(var i = 0; i < num_products; i++;)
	                {
	                var product = ds_map_find_value(async_load,"product_" + string(i));
	                show_debug_message("Product: " + string(product) + " is available");
	                var product_desc = ds_map_find_value(async_load,"product_desc_" + string(i));
	                show_debug_message("Product desc: " + string(product_desc));
	                var product_price = ds_map_find_value(async_load,"product_price_" + string(i));
	                show_debug_message("Product price: " + string(product_price));
	                var product_type = ds_map_find_value(async_load,"product_type_" + string(i));
	                show_debug_message("Product type: " + string(product_type));
	                var product_sku = ds_map_find_value(async_load,"product_sku_" + string(i));
	                show_debug_message("Product sku: " + string(product_sku));    
	                }
	            }
	        else
	            {
	            show_debug_message("Error retrieving product data " + string(status));
	            }
	        break;

The constant AmazonGameCircle_ProductDataResponse is part of the Amazon GameCircle IAP extension and is used whenever we call the function AmazonGameCircle_GetProductData to identify the return data in the async_load DS map. In this case we are simply sending the returned data to the Output Console at the bottom of the GameMaker Studio 2 UI as a debug measure, but you can do the same as we did for the leaderboards and achievements and store the returned values in variables for displaying on-screen.


Using IAP In GameMaker Studio 2

With the purchase data set up and added to our test project we can now go ahead and add the purchase request code. The first we'll add something to our button object "obj_Button_BuyConsumable" to trigger the purchase, so open that now and add a Step Event with the following code:

/// @description Buy Consumable
		
		if pressed
		{
		AmazonGameCircle_BuyIAP_SKU(global.iap_consumable);
		pressed = false;
		}

We'll also set up the button for buying an entitlement. In this case we need to store a return value from the IAP function call which is the hash for the purchase. This will be used along with the player ID (which we got right at the start of this tutorial) to verify any entitlement purchases done in the test project.

Open the button "obj_Button_BuyEntitlement", and add a Step Event with the following code:

/// @description Buy Entitlement
		
		if pressed
		{
		global.iap_hash = AmazonGameCircle_BuyIAP_SKU(global.iap_entitlement);
		pressed = false;
		}

We need to switch back to our controller object, "obj_Control", so go to it now and open the Social Async Event. As we've done previously we'll add a new case to the switch, and in it we'll be looking for the AmazonGameCircle_IAP_Receipt event, so add this code:

NOTE: If the code doesn't fit the window properly, you can click and drag the edge to expand it further and see it fully.

case AmazonGameCircle_IAP_Receipt:
		    var cancelled = ds_map_find_value(async_load, "receipt_cancelled");
		    if !cancelled
		        {
		        var receipt = ds_map_find_value(async_load, "receipt_full");
		        show_debug_message("purchase receipt returned:" + string(receipt));
		        var receipt_sku = ds_map_find_value(async_load, "receipt_sku");
		        var receipt_id = ds_map_find_value(async_load, "receipt_id");
		        if receipt_sku == global.iap_consumable
		            {
		            show_debug_message("Purchase of CONSUMABLE confirmed!");
		            global.game_consumable += 10;
		            AmazonGameCircle_CloudNumberSet("CloudNumConsumables", global.game_consumable);
		            AmazonGameCircle_NotifyFulfillment(receipt_id, true);
		            }
		        else if receipt_sku == global.iap_entitlement
		            {
		            show_debug_message("Purchase of ENTITLEMENT confirmed!");
		            global.game_entitlement = true;
		            AmazonGameCircle_CloudStringSet("CloudEntitlementUnlocked","true");
		            AmazonGameCircle_NotifyFulfillment(receipt_id, true);
		            with (obj_Button_PlayExpansion)
		                {
		                sprite_index = spr_Button1;
		                image_index = 0;
		                pressed = false;
		                }
		            with (obj_Button_BuyEntitlement)
		                {
		                sprite_index = spr_Button2;
		                txt = "Entitlement Bought";
		                }
		            }
		        else
		            {
		            AmazonGameCircle_NotifyFulfillment(receipt_id, false);
		            }
		        }
		    else
		        {
		        show_debug_message("Purchase transaction cancelled by the user");
		        }
		    break;

Let's just go through this code and explain what exactly we're doing:

  • First we check to see if the DS map has a "receipt_cancelled" key, and if it does then we show a message in the debug console and take no further action.

  • If the purchase wasn't cancelled then we can move on and get the full receipt returned (and print it out to the console as a debug measure), and then we get the receipt SKU and ID.

  • We then check the ID of the SKU against the global variables we defined at the start, and deal with each one accordingly

  • If it is a consumable, we add 10 onto the appropriate global variable and then we store the new value in the GameCircle cloud service. We then create a GameCircle notification to say that the purchase has been completed correctly. If all has gone correctly you should see the "Consumables Bought" text update when you test the project.

  • If it is an entitlement, we set the appropriate global variable to true and store that in the GameCircle cloud too, then create a notification. We also set the button to enter the expansion level of the game to be active so that the player has access to their new content (in a real project you can do any number of things, this is just an example of how to use it), and make sure to disable the button that purchases the entitlement.

  • Finally, if the return SKU does not match any of those we have defined in the project, we send a failure notification to GameCircle.

It's worth noting that we'll add some code later to check the values stored to the GameCircle cloud service so that the bonus isn't lost between plays or even between devices. The cloud service permits us to store single strings or real numbers, but it also permits us to store whole files from our project so you can (for example) make an INI file with game configuration details and have this perpetuated to the cloud (we strongly suggest that you never store any purchase or login details in a file, as it will be resident on the device and not just the cloud making it unsafe - instead keep everything dynamic and calculated at run time as we do in this tutorial).

You can go ahead and test this now, although it's not quite finished. When testing you should see that both the "Buy Consumable" and "Buy Entitlement" buttons will open up a system window with the purchase details and you can buy them or cancel the purchase as required:

Purchase An IAP


Checking Purchases

Our in-app purchase code is almost complete, but we've left a few loose ends. The main one is dealing with purchases that have been made when the game starts, as at the moment you aren't getting access to the expansion level if you close and start the game again, nor are you getting the consumables you've bought added. This is a pretty major bug as you don't want to be charging users for things and then not letting them have them!

To resolve this we start by adding the following line of code into the "obj_Control" object Social Async Event. It will go into achievement_our_info case, after all the other code:

AmazonGameCircle_GetPurchasesData();

So the code block should now look like this:

Get IAP Purchase Data

This will trigger the Async Social Event with the event ID AmazonGameCircle_IAP_Receipt (which we coded previously) and so any incomplete purchases will be completed on game start. But what about those things that have already been purchased and and the purchase was completed? Well, thankfully we've been storing that data in the Amazon cloud and so retrieving it on game start is simply a case of adding the following in after we request the purchase data:

global.game_consumable = AmazonGameCircle_CloudNumberGet("CloudNumConsumables");
		    if AmazonGameCircle_CloudStringGet("CloudEntitlementUnlocked") == "true"
		        {
		        global.game_entitlement = true;
		        with (obj_Button_PlayExpansion)
		            {
		            sprite_index = spr_Button1;
		            image_index = 0;
		            }
		        with (obj_Button_BuyEntitlement)
		            {
		            sprite_index = spr_Button2;
		            txt = "Entitlement Bought";
		            }
		        }

The full code block for that case should now look like this:

Full Code For Info Event

Our final task is to permit the user to use the consumable that they've bought, so open the button object "obj_Button_UseConsumable", then add a Step Event, and in it add:

/// @description Use Consumable
		
		if pressed
		{
		if global.game_consumable > 0
		    {
		    --global.game_consumable;
		    AmazonGameCircle_CloudNumberSet("CloudNumConsumables", global.game_consumable);
		    }
		else
		    {
		    if !instance_exists(obj_Button_BuyConsumable)
		        {
		        instance_create_layer(room_width / 2, y, "Instances", obj_Button_BuyConsumable);
		        }
		    }
		}

Here we simply subtract one from the global consumable variable and then upload it to the cloud for future reference. If the user has no more consumables left then the button for them to buy some more will be spawned. Keep in mind that that as long as you have a controller object to deal with incoming purchase data then you can call an in-app purchase function anywhere in your game.


Summary

It's now time to run our test project and check to see that everything is working as it should, so make sure your Fire device is connected and test the project now:

The Finished Project

Running that you should test each purchase type to see that the purchase goes through, and when you choose an entitlement you should only be able to buy it once before the button becomes inactive (and the "Play Expansion" button becomes active). Buying consumables should add 10 each time onto the number bought, and in the main game room you should be able to use them up and also buy more if required. You should take a moment to close the game and then restart it and check that the number of consumables given to you and the entitlement are correct too.

With that done and working you are all ready to start adding GameCircle features and In-App Purchases into your own game. This tutorial provides you with a framework to start adding into any other game, and adding further achievements, purchases, etc... into your own projects should simply be a case of adding them to the Amazon Developer dashboard and then expanding the list of global variables to include their SKUs or names.

If you want to practice some more, then why not take this tutorial project and build a small game around it? Make a simple space shooter for example and then add in extra achievements for things like "enemies destroyed" or "Played more than 10 minutes"? You could also add a time based leaderboard to show the player that has played the longest before dying. You could use the consumable in-app purchases to provide an invulnerability bonus, or have the entitlement in-app purchase unlock weapons instead of levels...

That brings us to the end of the Amazon Game Circle tutorial for Amazon Fire. We hope you've learned a lot and that you can put it all into practice in your own games!



Back to Tutorials