In this tutorial, we’ll be covering building your very own Amazon AWS DynamoDB integrated Unity3D game. We’ll be using two public cloud based technologies provided by Amazon AWS, namely DynamoDB (a NoSQL database service), and Cognito (identity management and data synchronization).
We’ll be building a character creator with Amazon DynamoDB integration in Unity3D. This will be a modular character creator that allows you to configure the look of your character, as well as his/her stats. The screen will pull any saved characters down from your DynamoDB table, and allow you to save new characters back to the cloud.
Before we get started, here are two very quick primers for you if you are not already familiar with DynamoDB and/or NoSQL technologies.
NoSQL
NoSQL covers a group of database technology types that differ to traditional relational style database types. They were developed to help combat a rise in the volume of information stored in general about users, products and objects, as well as the performance requirements for accessing this information in cases where scale is very large. There are four main types of NoSQL ‘stores’ around
- Document databases
- Graph stores
- Key-value stores
- Wide-column stores
Without going into detail about the types, the main thing to keep in mind is that they differ to traditional relational databases, and make certain types of operations much faster in NoSQL than they are in relational style databases.
DynamoDB
DynamoDB is a fully managed NoSQL database service from the Amazon AWS cloud stack that provides quick, predictable performance with automatic scalability based on the provisioned throughput settings you apply to your tables. DynamoDB tables can store and retrieve any amount of data, and serve any level of request traffic you require for your applications, and run across multiple servers in the Amazon AWS cloud stack, backed by SSD storage.
Getting started
We’ll be using Unity3D version 5 for this tutorial, so the UI is the new Unity UI that became available in version 4.6 and above. Hopefully you are already familiar with creating a basic UI, as I’ll be skipping that in this tutorial, otherwise it would just take up too much space! Download the starter project here, which includes all the UI setup for you, along with some fancy scrolling background, and base GameObject items created, waiting for you to attach new scripts to.
CharacterCreatorAWSDynamoDB Starter Unity3D Project
I must point out that the awesome sprites you see in this tutorial are all courtesy of kenney.nl.
Download the Amazon AWS Unity3D SDK
Grab the latest version here. I am using version 2.0.0.0 for this tutorial. Extract the .zip file to a convenient location.
Importing the SDK
Open the starter project in Unity3D, and go to Assets -> Import Package -> Custom Package. Navigate to the extracted Amazon SDK folder, and located the “unitypackages” sub-folder. Choose the “aws-sdk-unity-dynamodb-2.0.0.0.unitypackage” file to import and import all items into the project.
Browse the Project Assets hierachy, and locate the AWSPrefab prefab under the “AWSSDK -> src -> GameObjects” folder. Drag and drop this prefab into your scene hierarchy. This GameObject is required in your scene to initialise everything we need to get started using the AWS SDK in our scene. The prefab should have a “UnityInitializer” script attached to it.
Creating and configuring the required Amazon services
First of all you’ll of course need an AWS account. Register for one if you don’t already have one. You can use the free tier for 12 months on a new account which includes everything we need. Once configured, sign into your AWS Console.
Now we need to setup Cognito for identity management. This is under “Mobile services” from the main console. This will only be used in our case in an “unauthenticated” user capacity, however it has some great features like user sync across devices and identity management for user persistence if you wanted to dig further than this tutorial’s scope.
Create an identity pool and give it a name like “CharacterCreatorAWS”. Ensure you select “Enable access to unauthenticated identities“.
The next screen asks whether you would like Cognito to create some default roles with limited permissions. Make sure you click “Allow” here for the default roles to be setup. While you are still in the Cognito dashboard, select “Edit identity pool” and copy your pool ID down into a text document. We’ll need this later.
Now we need to create a DynamoDB table. Go back to the main AWS console, and choose “DynamoDB“. Once the dashboard appears, click “Create Table“. Give the table a name of “CharacterCreator” and enter “CharacterID” as the Hash Attribute Name, making sure you select “String” as the type. Click “Continue”.
The next wizard screen is optional to add indexes. We won’t be adding any, so click “Continue” to skip this, and move to the Provisioned Throughput Capacity screen. Here we can choose how many read and write capacity units we require. For this tutorial you can choose 1 or 2 each, but would need to consider these sizes using the calculator if you were deploying this for a game that would see high amounts of characters being created/read to/from your database!
The next wizard screen offers to setup a basic alarm for table request rates that exceed 80% of your provisioned throughput in 60 minutes. This is a good idea if you wish to be notified of any potential utilisation issues. Enter your e-mail here if you wish to be notified in this case, then click “Continue”.
Finish the table creation wizard, and when done, select your new table and click the “Details” tab. Copy out the ARN (Amazon Resource Name) for your table and note it down. We’ll now create a custom role policy using this ARN and assign it to our Cognito identity pool, which will in effect give your users access to your newly created table.
So, our next step is to use Identity and Access Management (IAM) to apply a custom role to allow unauthenticated users that run your Unity3D game access to the DynamoDB table that will store character configurations.
Go back to the main AWS console and click “Identity and Access Management“. Click “Roles” on the side menu, and then locate and click on the “unauth” role that was automatically created by Cognito earlier. It should be named something like “Cognito_CharacterCreatorUnauth_Role”. Look for the Inline policy section and click “Create role policy”.
Select “Custom policy” when asked what type you would like to create, and then click “Select” to use the policy editor.
Give your new policy a name like “AllowDynamoDbTableAccess” and use the template provided below. Policies are formatted in JSON, and you’ll need to change the resource value in this template to the ARN you copied for your DynamoDB table you created earlier. Here is the policy template you can use:
Click “Validate” and once validated, click “Apply policy” to assign this policy to your Cognito unauth role. You now have all the groundwork for configuring your AWS services done. Well done! Let’s move back to Unity3D finally.
Unity3D and AWS code
Now that all the AWS setup is complete, lets begin adding our integration with AWS. The starter project you downloaded above has all of the UI ground work complete for you. If you run it now, you’ll get a character selection screen where you are able to change the look and configuration of your character, however you are not able to save it to the database or load any existing characters and change them either. This is what we will add now.
Start by adding some using statements at the top of the CharacterCreator.cs file. These will reference some of the Amazon SDK namespaces and allow us to use Amazon specific classes and services in our CharacterCreator script.
Now we’ll need to add a class for our characters to use with the DynamoDB data model concept. In DynamoDB, a database is a collection of tables, and each table is a collection of items with each item being a collection of attributes. This class that we create will represent the items in our CharacterCreator table.
Create a new class called CharacterEntity in the Unity3D editor under the scripts folder, and open it up in your editor. Remove the inheritance to Monobehaviour as we will not be needing this, as this is a plain data model class. Copy and paste the below into your CharacterEntity class.
As you can see, this class contains various properties to store each character’s configuration, from the stats like Age, Strength and Dexterity, to what the character parts are composed of (“ShirtSpriteName”, “BodySpriteName”, etc…).
Each property has an attribute applied to it, mostly all indicating the each property is a DynamoDBProperty. Note however the first property “CharacterID“. This is the same as the hash key we created earlier when we setup our table in DynamoDB. Note that it is a string value, as we dictated when we created our table. This has the DynamoDBHashKey attribute applied to it, to tell the table that this property is our primary key. To quote the AWS SDK documentation on this Hash Type Primary Key:
the primary key is made of one attribute, a hash attribute. DynamoDB builds an unordered hash index on this primary key attribute. Each item in the table is uniquely identified by its hash key value.
We also have a DynamoDBTable attribute applied to the class itself, this has a value indicating the name of your table, so make sure this is the name of your table too! If you created your table name as CharacterCreator then the above should be fine. Save your CharacterEntity.cs class and open the CharacterCreator.cs script next.
Now we will add some public and private fields to the top of this class. These will store our Cognito AWS credentials, a reference to our DynamoDB client, and a context for DynamoDB to use. They will also store a list of characters pulled from the table when the scene loads, and store the currently selected character index value. Add these just below the comment “// Add AWS specific variables here.” on line 57.
We’ll now add a Context property with a getter on it to return our DynamoDB context each time we need it by creating an instance and passing in our DynamoDB _client reference. Add this below the “allSprites” Sprite[] array field, just before the method call to Awake().
The context is used as an entry point to your DynamoDB database. It provides a connection to your database, and enables you to perform various operations against your tables, mostly of the CRUD type (create, read, update, delete).
Now that we have a context setup, we need some methods to load CharacterEntity objects that are pulled from our table, and to switch between loaded characters in our UI. Add the following three methods to your CharacterCreator class.
The LoadCharacter method will take a CharacterEntity passed to it, and update the UI values to display the properties stored in the entity, in our UI. The Cycle methods will be assigned as listeners to our Next/Previous character buttons, so that when you click these, the character selection in the UI updates and changes to each character that was loaded from the DynamoDB table.
Add the following listeners to the top of your Awake() method call.
You may notice that we do not yet have the CreateCharacterInTable and FetchAllCharactersFromAWS methods created, and these are referred to by our create and refresh Operation fields. These will map to the create and refresh buttons in our UI. Let’s get started on those next.
First we’ll create the Load method. This will load our DynamoDB table asynchronously, and once done, execute a callback method that will use the Context to do an asynchronous scan of our table for all CharacterEntity objects that meet the condition of “Age” is greater than 0. In other words, all characters should be returned.
This is fine for the tutorial, but if you were working with large sets of data, a scan operation is not the most efficient. You would rather use a query operation to zero in on more precise bits of data you require. The SDK documentation has lots to read about this, so feel free to explore that later!
Once the async scan operation completes, it assigns the results (which will be a collection of CharacterEntity objects) to our characterEntities field. This allows us to then iterate over them and load them / cycle through them in our UI. Drop this method into your CharacterCreator.cs script.
Now we need our CreateCharacterInTable() method. Drop the following method into the same script.
This will create a new instance of CharacterEntity type, and assign the properties with the values entered into the UI fields, like Age and Strength. It will also grab the values assigned to the fields that keep track of what character components are selected in the UI, like “selectedHair” and “selectedShirt”. Finally, it will use our context to DynamoDB to async save our CharacterEntity to the table. Once this operation completes, the table will hold an entry for the character that was configured in the UI!
Before we can run any of this code though, we need to add some initialisation logic to our Start() method. This will only run once when the scene starts up. The following code will create some Cognito AWS Credentials by taking your identity pool string, and a RegionEndpoint specification. It will then asynchronously fetch an ID and initiate another async call after creating a DynamoDB client. This client is assigned to the _client field which will be used during run of the scene to fetch characters and create characters. After the _client field is assigned, the FetchAllCharactersFromAWS() method is executed to load all the characters up into the scene from the table. Add the following code to your Start() method in the CharacterCreator script.
You’ll notice that CognitoAWSCredentials object is created by passing in a string called cognitoIdentityPoolString. This is a public string that you need to assign a value to in the Unity editor. Go back to your scene, and select the CharacterCreator GameObject in the hierachy. Locate your Cognito Identity Pool Id that you hopefully noted down earlier (don’t worry if you forgot, just go back to your AWS console, load the Cognito dashboard, and edit your identity pool to find this ID). Enter the Id into the field on your GameObject.
One other thing to check here – make special note of the region you are using for your Cognito Identity Pool in the AWS console. If it is not US East 1, then you’ll need to change the code in a couple of places in the Start() method to specify your RegionEndpoint accordingly. Auto-complete will show you the other regions you can use on the RegionEndpoint. The two places to change are on creation of the credentials object, and the ddbClient object. Lastly, make sure the TableName specified in the Start() logic matches the name you used for your table (it should be “CharacterCreator” if you followed the naming convention when setting it up).
Ensure all your scripts and your scene file is saved, then give it a run from the Unity3D editor. If all goes well and everything is setup correctly, after a few moments the async calls should complete and the table should be initialised. There is a bit of debug text you can you view in the scene that various DynamoDB calls log to (you may need to increase the opacity of the font colour to view it. It is positioned just under the Refresh button).
So no characters will be loaded at first, as we have not yet created any. Enter a name and some stats for a new character and adjust his/her clothing and body types using the UI controls. Click “Create new” when you are done, and the character should be saved to your DynamoDB table in the cloud!
Note that when a new CharacterEntity is created, we give the CharacterID a new GUID value as the Hash ID. This ensures that every character created has a unique Hash ID value. If you wish to perform super fast lookup queries on your table you can create queries and search for these ID values.
Go to your DynamoDB console, select your table and click “Explore”. You should see your new character entry and see the GUID value that was assigned as its Hash ID. You’ll also see the properties that you selected in the UI saved into each row for each character you create.
Moving on from here
That is the tutorial complete now. We can see that there was initially a fair amount of ground work required to setup our AWS services, including Cognito, a DynamoDB table, and a custom IAM role, however using the Amazon SDK in our Unity3D project after this was done was relatively straightforward. The SDK provides lots of async method calls for you to utilise the various actions available to use DynamoDB tables. Remember that all the async calls can have a callback handler assigned so that you can execute code, or update values when the calls complete.
We didn’t use any table queries, or look into deleting entries, however the SDK documentation has lots of examples for you to try out if you wish to explore these areas further.
Going forward, I’m sure you can think of tons of use cases for DynamoDB and Cognito. One simple change I could think of is that you could identify users by unique login and store all of their own personal characters in the DynamoDB table. When they login using Cognito (you could provide a login dialog box in the UI), you could use Cognito to generate them temporary credentials to pull down their characters, and save/modify them on a per-user basis.
If you didn’t know too much about NoSQL databases, then hopefully this article also helped you out there. There are lots more out there. I have personally also tried out MongoDB, running in the Microsoft Azure cloud, and found working with it just as easy as DynamoDB, although the way documents are stored is slightly different. It wouldn’t take much to change this project to use MongoDB if you were feeling adventurous and preferred to try that out instead.
Finally, here is a link to download the complete Unity3D project, or to get the source from Github if you wish to get to the end point. Don’t forget to fill in the Cognito Identity Pool String on the CharacterCreator GameObject though, as I have removed it for the download due to it pointing to my own personal identity pool!
Thanks Sean for this excellent example!
I think there’s a small bit missing: I’ve added “using Assets.CharacterCreatorAWSDynamoDB.Scripts;” to the top of the CharacterCreator script. I’ve built it today and it works exactly as explained. Now it’s time to dig into your code in a bit more detail and see if I can expand on it for my own concoctions.
Thanks for the feedback Goodgulf, and good luck with your own implementations!
I second this little fix.
ssets/CharacterCreatorAWSDynamoDB/Scripts/CharacterCreator.cs(67,18): error CS0246: The type or namespace name `CharacterEntity’ could not be found. Are you missing `Assets.CharacterCreatorAWSDynamoDB.Scripts’ using directive?
Error is fixed by adding “using Assets.CharacterCreatorAWSDynamoDB.Scripts” to the top of CharacterCreator.cs
Hey Sean, cool article.
I was wondering why you chose to use DynamoDB over CognitoSyncManager and Datasets. Datasets give a couple of possible advantages in that they are tied to a user account and can also be written to and read from when offline, and synced when online with auto merging options available. I guess the drawbacks are that it’s clunky key value-pair data and you’re limited to 20 datasets per user, with each dataset limited to 1Mb(total 20Mb though of save game data, which should be more than enough for most cases).
I need to have a look at the DynamoDB stuff though for some cross-user shared data so your article is going to be very handy. Cheers.
Hi Giles,
Truth be told, I haven’t actually looked into CognitoSyncManager and/or Datasets. Thanks for the brief explanation of them – they do sound like a good match for this sort of use case. If I look into persisting this kind of stuff to the cloud I’ll definitely check them out. Thanks!
Hope the article comes in handy.
Cheers,
Sean
Hey Sean.
This is SUCH a good tutorial. Really helped me get my head around dynamoDB.
I think one of the unknowns for me is how to structure the read/write to be as economic as possible. 🙂
Still getting my head around the price structure. (Not sure I’ll ever be in THAT position where it actually matters though)
Anyways, thanks for putting the time in.
/A
There should be a deleting item in this example.
Also how can we retrieve data with sorting it.?
Great tutorial. Really helped me get started!
One question, how would I go about iterating through all the properties under one hash key, and being able to modify them while iterating? I’ve been looking up and down trying to figure this out, but maybe you know a little something that could help me out?
Either way, thanks for the great tutorial!