Sending and Requesting Data from MongoDB in a Unity Game
Rate this tutorial
Are you working on a game in Unity and finding yourself needing to make use of a database in the cloud? Storing your data locally works for a lot of games, but there are many gaming scenarios where you'd need to leverage an external database. Maybe you need to submit your high score for a leaderboard, or maybe you need to save your player stats and inventory so you can play on numerous devices. There are too many reasons to list as to why a remote database might make sense for your game.
If you've been keeping up with the content publishing on the MongoDB Developer Hub and our Twitch channel, you'll know that I'm working on a game development series with Adrienne Tacke. This series is centered around creating a 2D multiplayer game with Unity that uses MongoDB as part of the online component. Up until now, we haven't actually had the game communicate with MongoDB.
In this tutorial, we're going to see how to make HTTP requests from a Unity game to a back end that communicates with MongoDB. The back end was already developed in a tutorial titled, Creating a User Profile Store for a Game With Node.js and MongoDB. We're now going to leverage it in our game.
To get an idea where we're at in the tutorial series, take a look at the animated image below:
To take this to the next level, it makes sense to send data to MongoDB when the player crosses the finish line. For example, we can send how many steps were taken by the player in order to reach the finish line, or how many times the player collided with something, or even what place the player ranked in upon completion. The data being sent doesn't truly matter as of now.
The assumption is that you've been following along with the tutorial series and are jumping in where we left off. If not, some of the steps that refer to our project may not make sense, but the concepts can be applied in your own game. The tutorials in this series so far are:
If you'd like to view the source code to the project, it can be found on GitHub.
Because Unity, as of now, doesn't have an official MongoDB driver, sending and receiving MongoDB data from Unity isn't handled for you. We're going to have to worry about marshalling and unmarshalling our data as well as making the request. In other words, we're going to need to manipulate our data manually to and from JSON and C# classes.
To make this possible, we're going to need to start with a class that represents our data model in MongoDB.
Within your project's Assets/Scripts directory, create a PlayerData.cs file with the following code:
Notice that this class does not extend the
MonoBehaviorclass. This is because we do not plan to attach this script as a component on a game object. The
public-defined properties in the
PlayerDataclass represent each of our database fields. In the above example, we only have a select few, but you could add everything from our user profile store if you wanted to.
It is important to use the
publicidentifier for anything that will have relevance to the database.
We need to make a few more changes to the
PlayerDataclass. Add the following functions to the class:
JsonUtilityclass that will convert objects to strings and strings to objects.
Stringifyfunction will take all
publicvariables in the class and convert them to a JSON string. The fields in the JSON object will match the names of the variables in the class. The
Parsefunction will take a JSON string and convert it back into an object that can be used within C#.
With a class available to represent our data model, we can now send data to MongoDB as well as retrieve it. Unity provides a UnityWebRequest class for making HTTP requests within a game. This will be used to communicate with either a back end designed with a particular programming language or a MongoDB Realm webhook. If you'd like to learn about creating a back end to be used with a game, check out my previous tutorial on the topic.
We're going to spend the rest of our time in the project's Assets/Scripts/Player.cs file. This script is attached to our player as a component and was created in the tutorial titled, Getting Started with Unity for Creating a 2D Game. In your own game, it doesn't really matter which game object script you use.
Open the Assets/Scripts/Player.cs file and make sure it looks similar to the following:
I've stripped out a bunch of code from the previous tutorial as it doesn't affect anything we're planning on doing. The previous code was very heavily related to moving the player around on the screen and should be left in for the real game, but is overlooked in this example, at least for now.
Two things to notice that are important are the imports:
The above two imports are important for the networking features of Unity. Without them, we wouldn't be able to properly make GET and POST requests.
Before we make a request, let's get our
PlayerDataclass included. Make the following changes to the Assets/Scripts/Player.cs code:
In the above code, notice that we are creating a new
PlayerDataobject and assigning the
plummie_tagfield a value. We're also making use of an
OnCollisionEnter2Dfunction to see if our game object collides with anything. Since our function is very vanilla, collisions can be with walls, objects, etc., and nothing in particular. The collisions will increase the
So, we have data to work with, data that we need to send to MongoDB. To do this, we need to create some
IEnumeratorfunctions and make use of coroutine calls within Unity. This will allow us to do asynchronous activities such as make web requests.
Within the Assets/Scripts/Player.cs file, add the following
Downloadfunction will be responsible for retrieving data from our database to be brought into the Unity game. It is expecting an
idwhich we'll use a
plummie_idfor and a
callbackso we can work with the response outside of the function. The response should be
PlayerDatawhich is that of the data model we just made.
After sending the request, we check to see if there were errors or if it succeeded. If the request succeeded, we can convert the JSON string into an object and invoke the callback so that the parent can work with the result.
Sending data with a payload, like that in a POST request, is a bit different. Take the following function:
Uploadfunction, we are expecting a JSON string of our profile. This profile was defined in the
PlayerDataclass and it is the same data we received in the
The difference between these two functions is that the POST is sending a payload. For this to work, the JSON string needs to be converted to
byteand the upload and download handlers need to be defined. Once this is done, it is business as usual.
It is up to you what you want to return back to the parent. Because we are creating data, I thought it'd be fine to just return
trueif successful and
falseif not. To demonstrate this, if there are no errors, the response is compared against an empty object string. If an empty object comes back, then false. Otherwise, true. This probably isn't the best way to respond after a creation, but that is up to the creator (you, the developer) to decide.
The functions are created. Now, we need to use them.
Let's make a change to the
When the script runs—or in our, example when the game runs—and the player enters the scene, the
StartCoroutinemethod is executed. We are providing the
plummie_tagas our lookup value and we are printing out the results that come back.
We might want the
Uploadfunction to behave a little differently. Instead of making the request immediately, maybe we want to make the request when the player crosses the finish line. For this, maybe we add some logic to the
In the above code, we check to see if the player position is beyond a certain value in the x-axis. If this is true, we execute the
Uploadfunction and print the results.
The above example isn't without issues though. As of now, if we cross the finish line, we're going to experience many requests as our code will continuously execute. We can correct this by adding a boolean variable into the mix.
At the top of your Assets/Scripts/Player.cs file with the rest of your variable declarations, add the following:
The idea is that when the
_isGameOvervariable is true, we shouldn't be executing certain logic such as the web requests. We are going to initialize the variable as false in the
Startmethod like so:
With the variable initialized, we can make use of it prior to sending an HTTP request after crossing the finish line. To do this, we'd make a slight adjustment to the code like so:
After the player crosses the finish line, the HTTP code is executed and the game is marked as game over for the player, preventing further requests.
You just saw how to use the
UnityWebRequestclass in Unity to make HTTP requests from a game to a remote web server that communicates with MongoDB. This is valuable for any game that needs to either store game information remotely or retrieve it.
There are plenty of other ways to make use of the
UnityWebRequestclass, even in our own player script, but the examples we used should be a great starting point.
This tutorial series is part of a series streamed on Twitch. To see these streams live as they happen, follow the Twitch channel and tune in.
Building a Crypto News Website in C# Using the Microsoft Azure App Service and MongoDB Atlas
Apr 17, 2023