Rate this tutorial
When it comes to game development, you're almost always going to need to store information about your player. This information could be around how many health points you currently have in the game or it can extend beyond the game-play experience and into details such as the billing information for the person playing the game. When we talk about this type of data, we're talking about a user profile store.
The user profile has everything about the user or player and doesn't end at health points or billing information.
To get a better idea of what we're going to accomplish, take a look at the following animated image:
Alright, so we're not making a very exciting game. However, many modern games let you design your character either before the game starts or at some point as you progress through the game. These customizations that you add to your character are stored within the user profile. In our example, we're going to customize our player and then send the information to our back end for storing in MongoDB.
We'll be using Node.js to create a back end API that will communicate with our MongoDB cluster. The game, which will be our front end, will communicate with our Node.js back end. We won't go into the details on how to configure and deploy a MongoDB instance, but you'll need one with a database and collection available for use.
Before we get into the code, it is probably a good idea to explore what our user profile should contain. These aren't blanket rules for all user profiles because often, games are not the same.
Based on the animated image toward the beginning of this tutorial, we know we have the following JSON:
The depth of the above profile is hardly anything, but we know for this particular player that they've decided to give their character a certain appearance and that character has a particular set of attributes such as health points.
You might consider adding a hashed password, player position within the game, stored items, or any other relevant field to your user profile.
What makes MongoDB particularly powerful when it comes to gaming and user profiles is how flexible the data model can be. If we release an update to our game that includes customizations to the color of the diamond, we could easily add that field to our NoSQL documents. Adding and removing fields requires no special migrations or coding, which translates to faster development and better code.
We have a rough idea of what our user profile should look like for this particular game. Now we need to be able to add it to MongoDB and query for it. To do this, we're going to create a back end with two simple API endpoints: an endpoint for creating a document with the profile and an endpoint for retrieving it.
For this particular tutorial, we are not going to worry about authentication. In other words, we are only going to be creating user profiles and not caring who they truly belong to.
Within a new directory on your computer, execute the following commands:
The above commands will create a new package.json file and download the dependencies that we plan to use. We're using the
package because we plan to run our game locally within a web browser. If we don't manage cross-origin resource sharing (CORS), we're going to get errors. The
packages will be for our development framework and the
package will be for interaction with MongoDB.
Within the same project, create a main.js file with the following code:
The core of what is happening in the above code is we are laying the foundation to our Express Framework application by importing the dependencies and initializing the framework. When we start listening for connections, then we connect to MongoDB and define which database and collection we want to use. In this example, we are using a
gamedevdatabase and a
profilescollection. You can use whatever makes sense to you.
The actual connection information for MongoDB is defined with an
ATLAS_URIenvironment variable. You could easily use a configuration file or hard-code this value, but environment variables tend to work out the best for security and deployment reasons.
The connection string to be used would look something like this:
You can gather the information from your MongoDB Atlas dashboard after creating the appropriate user credentials for your database and collection.
When I add an environment variable, I generally do something like the following:
There are numerous ways to do this and it may vary depending on your operating system.
So let's focus on those endpoints.
When it comes to creating a user profile within MongoDB, we might have an endpoint that looks like the following:
The above endpoint is probably the most basic endpoint you can create. We are accepting a payload from the front end and we are inserting it into the database. Then we are returning the response that the database gives us.
With data going into MongoDB, now we can create our endpoint for retrieving it:
After supplying this endpoint a username, which in our example is the
_idfield, we use it to find one particular document. That one document is returned to the game or whatever tried to retrieve it.
Again, we are not using any data validation or authentication for this tutorial as it is outside the scope of what we want to accomplish.
We now have an API to work with.
With the API in place, we can focus on the game itself. Remember, the depth of our game is quite shallow, but it offers a feature that a lot of games offer. That feature is customizing the character.
I'm not going to share my graphic assets because I want you to use some imagination for creating your own. Will you create something better? Probably, but it's on you!
Create an index.html file on your computer with the following HTML markup:
The real magic is going to happen in the
scenelifecycle functions. The
initScenefunction is where we'll initialize our variables, the
preloadScenefunction is where we'll load our media assets, and the
createScenefunction is where we'll do our first render.
Starting with the
initScenefunction, add the following:
For our example, the
playerobject will represent our locally stored user profile object. It will look exactly like what you'd find stored in the database. This object could be as simple or as complex as you want to make it depending on your game needs. The
_idfield must be unique, so if you'd prefer to let MongoDB generate it, remove the field and create a
usernameor similar field instead. It's totally up to you.
moutharrays will represent all possible options for character customizations. The chosen customization will be stored in the user profile.
The next step is to look at the
In the above function, we are loading our image files and giving them a reference name to be used throughout the game. In the above example, the reference name is a pretty close name to the filename itself, but it doesn't have to be.
I didn't provide the image assets, so feel free to be adventurous.
The magic happens in the
Let me start by saying a few things first.
- The game in my example is 1280x720 and the image assets reflect this.
- The base image for the character and any cosmetic attachment image has the same resolution.
Rather than creating perfectly sized cosmetic attachment images, in my example, I just removed the base image but left the attachment images the same size but with transparency. Trying to figure out how to position small images could turn into more work than I wanted. Layering the images worked better.
With that said, we have the following:
The initialize images are placed on the screen with varying positions. The
mouthActiverepresent the initial attachments for the character. We are only setting those to a variable because we plan to change them later.
Based on the naming convention of the images and the sizes we chose to use, we can display the attachment options for
eyeson the screen:
In this example, we have two different possibilities for eyes. When pushing them into our array, we are specifying the position, but we are also changing a few things in regards to the image. For example, we know the images for attachments are mostly transparent. We can crop them and scale them to show only what we want. This is for selection purposes only, not displaying on the actual character.
We also want to respond to click events. When a click event happens, we want the appropriate image to be used as the active image. However, because we scaled and cropped our image, we need to redefine the
hitArea, which is the area that should be clickable on the image.
Doing most of these steps is only a requirement if you are using images of the same size and planning to layer them like I am.
We can reproduce our steps for the mouth options:
The steps are the same, but we are using different positions.
Remember, these two steps are for defining the thumbnails of our images and specifying a click region for them. While the active image is what we show on the character, the lead up is related to the thumbnails.
With the thumbnails out of the way, we can add some placeholder text of what profile we want to send:
The goal with this text is to only show what our
playerobject looks like on the screen. This will give us a good idea of what was sent to our MongoDB database.
Finally, we have the button that will submit the data to our API:
When the button is clicked, the image reference is added to the
playerobject. Using a
fetchoperation, we can send the
playerobject to our back end which will store it in MongoDB. Since we aren't doing any data validation, this will succeed as long as the
_idfield is unique.
If you're using MongoDB Atlas and you look at the data explorer, the documents should look something like the above image.
You just saw how to store user profiles for a Phaser game as NoSQL documents in MongoDB. Depending on the game you're creating, the user profile documents could be significantly more complex in comparison to what was seen in this example. At the end of the day, you're storing information about your game-play experience and that information could include information regarding the user such as password, billing information, or similar, and you're also storing information about the game, which might include outfit customizations, item inventory, player location, or something else game-related.