Docs Menu

Create a Unity Game with Realm

On this page

In this tutorial, you will create a Unity project that uses Unity's Platformer Microgame and MongoDB Realm.

The project is made up of two parts:

  • The Platformer Microgame: A working microgame provided by Unity that does not store any data in a database.
  • The Realm Tutorial repository: A repository we've created to simplify the creation of the User Interface and storing of data in Realm.

In Part 1 of this tutorial, you will set up the platformer microgame, clone and import the Realm tutorial repository, and then add functionality to manage players and a list of game statistics in Realm Database.

Once you've completed the local version of the app, you can enhance your application in Part 2 Realm Sync to:

  • Register users with email and password.
  • Sign users into accounts with email and password and sign out later.
  • Create a synced global leaderboard of top user scores.

Part 1 should take around 30 minutes to complete. The optional part 2 should take an additional 30 minutes.

Note

If you prefer to explore on your own rather than follow a guided tutorial, you can check out the following documentation:

Important
Prerequisites

The prerequisites below list the latest versions of the necessary downloads at the time of writing this documentation:

1

Unity Hub comes pre-installed with templates, such as the Platformer Microgame, to help users learn to develop games for Unity. You'll start by loading the Platformer Microgame template. In later steps, you will import and add Realm functionality to the game.

Click the Learn tab on the left side of Unity Hub to view the list of template projects. Scroll to the Platformer Microgame and click it.

Unity Scroll to Platformer Microgame

On the Modal that appears, click the Download Project button to download the project. When the download is complete, click Open Project to open the project in Unity.

Download Project - Platformer Microgame

When you open the Platformer Microgame template, Unity prompts you with the option to Load Scene or Load Tutorials.

If you are new to developing with Unity, click Load Tutorials to learn Unity editor basics, interacting with Game Objects, and more. Once you feel comfortable working with the Unity Editor, you can continue to the next step. Alternatively, if you are already comfortable working with the Unity editor, click Load Scene and continue to the next step in this tutorial.

Once the scene is loaded, click the play button on the top of the screen to enter play mode and play the game.

View Game in Play Mode
Tip
Playing the Game

Try the game to get a feel of what your users will experience. Use the arrow keys for Player movement and the spacebar to jump. Navigate through a series of platforms and defeat the "red slime" enemies by hopping on them, making them disappear. Collect the yellow "tokens" by touching them. Get to the end of the level to win the game.

In later steps, you will use Realm Database to record:

  • how many enemies the Player defeated per playthrough
  • how many tokens were collected
  • a score that is calculated based on the statistics for the current playthrough and how quickly you completed the playthrough

Unity stores the game's files in a temporary directory when you load a template project from the Learn tab. To save them to a local folder on your machine, click the X button at the top of the screen to close Unity. A modal opens and prompts if you want to keep this project. Select the Keep button and pick a folder to save your new project in.

Keep Project

Navigate to the Unity Hub menu to reopen your game. Select the Add button in the menu and click your project folder to add your new locally saved project to your list of projects on Unity Hub.

Add Project
2

Unity uses the project manifest to determine packages to install into your project. You'll need to update your project's manifest file to install Realm Database, and the Unity UI ToolKit and UI Builder packages to develop user interfaces to interact with and display data stored in realm.

Open your project's manifest file by navigating to your project on Unity Hub and expanding the dropdown menu on the right-hand side of your project name. Click the Reveal in Finder option if you're on a Mac or the Show in Explorer option if you're on a Windows machine. Open your project's /Packages/manifest.json file in a text editor.

Reveal in Finder

Update your project's manifest file to include the following dependencies and scoped registries:

{
"dependencies": {
// ...
"io.realm.unity": "10.5.0",
"com.unity.ui": "1.0.0-preview.17",
"com.unity.ui.builder": "1.0.0-preview.17"
},
"scopedRegistries": [
{
"name": "NPM",
"url": "https://registry.npmjs.org/",
"scopes": [
"io.realm.unity"
]
}
]
}
Warning
UI ToolKit and UI Builder are Preview Packages

UI ToolKit and UI Builder are preview packages that are in development by the Unity engineering team. While they provide a simple API to develop user interfaces for a tutorial, they should not be used in production yet.

Warning
The type or namespace 'UIDocument' could not be found

If you are experiencing the compilation error: "The type or namespace 'UIDocument could not be found", this is a bug in the UI Toolkit package. As a workaround, create an Assembly Definition Reference. Click Assets at the top of the screen. Then select Create in the dropdown that appears. Finally, select Assembly Definition Reference. This will cause Unity to recompile without the error.

3

Now that you have installed Realm, UI Builder, and UI Toolkit, you can begin recording data into Realm and building a user interface that can read and interact with that data.

We've already put together a repository with most of the code you'll need to accomplish that. You can clone or download the Realm Tutorial repository directly from GitHub:

4

In your text editor of choice, such as Visual Studio, you can see the script files you will need to add to the game. The relevant files are as follows:

File
Purpose
UI Toolkit/Leaderboard.uxml
UXML file; defines the structure of the Leaderboard user interface.
RealmScripts/LeaderboardManager.cs
Query Realm and display the top Stat objects.
UI Toolkit/ScoreCard.uxml
UXML file; defines the structure of the Scorecard user interface.
RealmScripts/ScoreCardManager.cs
Listen for changes to the current playthrough Stat and update the Scorecard UI.
UI Toolkit/Authentication.uxml
UXML file; defines the structure of the Authentication Screen user interface.
RealmScripts/AuthenticationManager.cs
Interacts with the TextField's of AuthenticationScreen.uxml to implement user login. In part 2, you'll add user authentication with the MongoDB Realm Email/Password provider.
RealmScripts/RealmController.cs
Open a realm, query Realm the realm for an existing Player or create a new one and a new Stat for the current playthrough. Perform write transactions on the realm objects.
RealmScripts/PlayerModel.cs
Defines the Player Realm Object Model.
RealmScripts/StatModel.cs
Defines the playthrough Stat Realm Object Model.
RealmScripts/Constants.cs (Part 2)
Part 2: Declare the Realm App ID for the Realm Sync portion of the tutorial.
5

In the realm-tutorial-unity project that you have cloned, navigate to the PlayerModel.cs and StatModel.cs files in a text editor. These C# classes define the schemas for the objects we store in our realms.

Note the following about the schemas:

  • A Stat is a realm object representing the statistics of a playthrough of the game. For instance, in one playthrough of the game, a Player may have defeated 10 enemies, collected 15 tokens, and had a score of 210. Each Stat has a statOwner that is a Player object.
  • A Player is a realm object representing the user's logged-in character. Each Player has a collection (IList) of Stat objects.
  • A Player has a string property, _id, that defines the Player objects' unique partition.
6

The RealmController.cs file is a script that contains boilerplate code for this tutorial, such as a Start() method that automatically generates Leaderboard, Authentication, and ScoreCard UI objects and attaches the AuthenticationManager.cs, LeaderboardManager.cs, and ScoreCardManager.cs scripts to their respective UI Game Objects. In later steps, you will add functionality to the RealmController script to perform the following:

  • Open a realm.
  • Set a logged-in Player.
  • Interact with the Player and Stat models.
  • Create, read, update, and delete the current playthrough Stat object.

Before you begin updating the code in the RealmController.cs file, you'll need to create a new Game Object for it.

Create an empty Game Object by right-clicking the hierarchy window and selecting Create Empty. Name the object RealmController.

Create empty game object

Next, open the inspector window by clicking the new RealmController object.

Note
Inspector Window Not Opening Automatically

If your inspector window does not open after clicking the Game Object, click Window > General > Inspector. Then click the Game Object again to open its inspector window.

Next, attach the RealmController.cs script to the RealmController object. Click the Add Component button, and select the Scripts option in the dropdown.

Attach a script

Finally, select the RealmController.cs script to attach it to the RealmController object.

Attach a script

Click the play button on the top of the screen to enter play mode and play the game. You should see something like the following:

Enter Play Mode - Start Login Screen

Notice, when you type a username and click Login & Start Game, Unity navigates you to the platformer game, but the username field on the right-hand side of the game screen is a static username rather than the one you inputted. You'll add functionality to replace the static username value with a dynamic user input later.

Enter Play Mode - Initial Platformer Screen
7

Open the RealmController.cs file with a text editor, where we'll implement a function to open a realm. To open a realm instance that you will use throughout the RealmController class, add the following code to the GetRealm() method:

return Realm.GetInstance();
8

In the RealmController.cs file, you will need to implement a function to set the logged-in user. The AuthenticationManager.cs file calls this function when a user clicks the Login & Start button.

The RealmController.SetLoggedInUser() method takes a string, userInput. You will create code to do the following in this method:

  • Query the realm to determine if a Player object already exists for the given userInput.
  • Create an if-else block that creates a new Player and a new Stat realm object if the Player does not exist, and uses the existing Player and creates a new Stat object if the Player does exist.

Add the following code to your RealmController.SetLoggedInUser() method:

// query the realm to find any Player objects with the matching name
var matchedPlayers = realm.All<Player>().Where(p => p.Name == userInput);
if (matchedPlayers.Count() > 0) // if the player exists
{
currentPlayer = matchedPlayers.First();
var stat = new Stat();
stat.StatOwner = currentPlayer;
realm.Write(() =>
{
currentStat = realm.Add(stat);
currentPlayer.Stats.Add(currentStat);
});
}
else
{
var player = new Player();
player.Id = ObjectId.GenerateNewId().ToString();
player.Name = userInput;
var stat = new Stat();
stat.StatOwner = player;
realm.Write(() =>
{
currentPlayer = realm.Add(player);
currentStat = realm.Add(stat);
currentPlayer.Stats.Add(currentStat);
});
}

After the above code is executed, the RealmController.SetLoggedInUser() function calls startGame() to begin the timer. The timer increments the RealmController's runTime variable, and we use the runTime variable to calculate bonus points in the playerWon() method when the game has completed.

9

You've set the current Scorecard to the initial playthrough statistics for the current playthrough of the game, but collecting tokens or defeating enemies will not impact the current Stat object yet. To implement that, you'll edit the CollectToken() and DefeatEnemy() methods in the RealmController.

Add the following code to the RealmController.CollectToken() method to perform a write transaction and update the variable currentStat that you created in the last step:

realm.Write(() =>
{
currentStat.TokensCollected += 1;
});

Call the CollectToken() method when the Player collides with the token instance. This occurs in the platformer microgame file:

/Assets/Scripts/Mechanics/TokenInstance.cs. In the TokenInstance.cs file, the OnPlayerEnter() occurs when a Player has collided with the token instance. Add the following code to OnPlayerEnter() after collected = true.

RealmController.CollectToken();

Add the following code to the RealmController.DefeatEnemy() method to perform a write transaction and update the variable currentStat that you created in the last step:

realm.Write(() =>
{
currentStat.EnemiesDefeated += 1;
});

Navigate to the /Assets/Scripts/Gameplay/EnemyDeath.cs file and call RealmController.DefeatEnemy() at the beginning of the Execute() method.

RealmController.DefeatEnemy();

Finally, create a listener that reacts to changes to the current playthrough's Stat and updates the Scorecard in the UI. In the` ScoreCardManager.cs file, you can add a change listener to the current Stat realm object defined as the RealmController.currentStat variable. In the WatchForChangesToCurrentStats() method, handle the Stat object's PropertyChanged event:

propertyHandler = new PropertyChangedEventHandler((sender, e) => UpdateCurrentStats());
currentStat.PropertyChanged += propertyHandler;

The Scorecard now updates if the Player defeats an enemy or collects a token.

Select Authentication.uxml as source asset
10

When the Player reaches the end of the Microgame level, the game is complete, but nothing else happens. To expand upon this, you will update the code to do the following when the Player wins the game:

  • Call the RealmController.PlayerWon() method to calculate and write the final score to realm
  • Display a dialog with the final score
  • Respawn the Player and restarts the game if the Player clicks the restart button

Navigate to the Platformer Microgame file, /Assets/Scripts/Gameplay/PlayerEnteredVictoryZone.cs, in your text editor. This script is executed when the Player enters the victory zone.

To create a dialogue, you'll need the UnityEditor.EditorUtility class. Add the following code to the top of the file:

using UnityEditor;

Replace the existing Execute() method in the PlayerEnteredVictoryZone script with the snippet below:

public override void Execute()
{
var finalScore = RealmController.PlayerWon();
var didClickRestart = EditorUtility.DisplayDialog("You won!", $"Final Score = {finalScore}", "restart game");
if (didClickRestart == true)
{
Simulation.Schedule<PlayerSpawn>(2);
RealmController.RestartGame();
}
}
11

When the Player loses by colliding with an enemy or by falling from a platform, the game starts a new playthrough and respawns the Player back to the start point but does not create a new Stat object for the new playthrough. To expand upon this, you'll perform the following when the Player loses the game:

  • Update the RealmController.DeleteCurrentStat() method to delete the current Stat object for the current playthrough
  • Call the RealmController.DeleteCurrentStat() method
  • Call the RealmController.RestartGame() method to create a new Stat object for the new playthrough

Navigate to the RealmController.DeleteCurrentStat() method and add the following code at the bottom of that method:

realm.Write(() =>
{
realm.Remove(currentStat);
currentPlayer.Stats.Remove(currentStat);
});

Next, open the Platformer Microgame file, /Assets/Scripts/Gameplay/PlayerDeath.cs, which handles the player death event. Add the following code to the end of the PlayerDeath's Execute() method:

RealmController.DeleteCurrentStat();
RealmController.RestartGame();

Return to the Unity Editor, and click play to enter play mode. The next time your Player loses, the game respawns the Player to the start point with a new Stat object showing 0 enemies defeated, 0 tokens collected.

12

To add a personal touch to the game, you can modify its assets from the Unity Editor. Navigate to Assets/Tiles in the Project Window to find a set of tiles that the game uses.

To modify a tile, click the tile to open its inspector window. From the inspector window, you can change the sprite that the tile uses and its color.

Customize Tiles

In the example below, we replaced the fence tile's sprite with the fence_brown sprite that comes with the Platformer Microgame.

Customize Fence Sprite

You can choose any tile colors and sprites you would like. We went with the following for an earth-toned and friendly look:

Tile Name
Sprite
Fence
fence_brown
House
house_green
Plant
plant_green
TileFloatingLeftEdge
TileFloatingLeftEdge_green
TileFloatingRightEdge
TileFloatingRightEdge_green
TileFloatingMiddle
TileFloatingMiddle_green
TileGround
TileGroundDark_green
TileGroundTop
TileGroundTop_green
Customize Unity Game - Final Look
13

Once you have completed the code, you can run the app and check its functionality.

Click the play button on the top of the screen to enter play mode and play the game. Once the Unity Editor opens the GameView, here are some things you can try out:

  • Logging in with a username.
  • Achieving a new high score and seeing the leaderboard update with it on the next playthrough.
  • Achieving a new high score using a different user and seeing the leaderboard update with the highscore on the next playthrough.
Tip

To view a complete synced version of the app:

  1. Navigate to the root directory of the Realm Tutorial repository:

    cd realm-tutorial-unity
  2. Check out the sync branch:

    git checkout sync
  3. In Constants.cs, replace <your-realm-app-ID-here> with your Realm app ID, which you can find in the Realm UI.
  4. Run the game by clicking the "Play" button at the top of the Unity UI to enter Play mode.
1

Before you can add Sync functionality within your game, you must enable Realm Sync in your MongoDB Realm app.

To enable Realm Sync, follow the steps in the Get Started with Sync guide. When enabling Sync, you will need to configure your app to:

  • use Development Mode
  • add a required Partition Key with the name "_partition" and with the type string
2

To get the app working with your backend, you first need to add your MongoDB Realm App ID to the Constants.cs file:

public const string AppId = "<your-realm-app-ID-here>";

Change the value of AppId to your MongoDB Realm app ID, which you can find in the MongoDB Realm UI.

3

To configure Realm to automatically synchronize data between devices, you'll need to replace your local realm with a synced realm.

In the RealmController.cs file, replace the existing GetRealm() method with the one that takes in a User as a parameter and asynchronously gets a synced realm instance:

// GetRealm() is an asynchronous method that returns a synced realm
// GetRealm() takes a logged in Realms.Sync.User as a parameter
private static async Task<Realm> GetRealm(User loggedInUser)
{
var syncConfiguration = new SyncConfiguration("UnityTutorialPartition", loggedInUser);
return await Realm.GetInstanceAsync(syncConfiguration);
}

Opening a synced realm requires a logged-in realm user; you'll set that up in the next step.

4

To add support for Email/Password Authentication, you'll need to update your Authentication Screen to have a password field and a button that allows users to toggle between a registration mode and login mode.

Replace the code in your Authentication.uxml with the following:

<ui:UXML xmlns:ui="UnityEngine.UIElements" xmlns:uie="UnityEditor.UIElements" xsi="http://www.w3.org/2001/XMLSchema-instance" engine="UnityEngine.UIElements" editor="UnityEditor.UIElements" noNamespaceSchemaLocation="../../UIElementsSchema/UIElements.xsd" editor-extension-mode="False">
<Style src="Stylesheet.uss" />
<ui:VisualElement name="auth-wrapper" class="auth-wrapper" >
<ui:Label text="Realm Platformer" display-tooltip-when-elided="true" name="game-title" class="game-title" />
<ui:Label text="Defeat Enemies and Collect Tokens!" display-tooltip-when-elided="true" name="defeat-enemies-subtitle" class="game-subtitle"/>
<ui:Label text="Earn Bonus Points by Completing the Game Quicker!" display-tooltip-when-elided="true" name="earn-bonus-points-subtitle" class="game-subtitle"/>
<ui:Label text="Play Your Way to the Top of the Leaderboard!" display-tooltip-when-elided="true" name="play-to-leaderboard-subtitle" class="game-subtitle" />
<ui:Label text="Login:" display-tooltip-when-elided="true" name="subtitle" class="subtitle" />
<ui:TextField picking-mode="Ignore" label="username" value="username" text="your.name@example.com" name="username-input" class="auth-input" />
<ui:TextField input-type="password" picking-mode="Ignore" label="password" value="password" name="password-input" class="auth-input" />
<ui:Button text="Login &amp; Start Game" display-tooltip-when-elided="true" name="start-button" class="start-button" />
<ui:Button text="Don&apos;t have an account yet? Register" display-tooltip-when-elided="true" name="toggle-login-or-register-ui-button" class="toggle-login-or-register-ui-button" />
</ui:VisualElement>
<ui:Button text="Logout" display-tooltip-when-elided="true" name="logout-button" class="logout-button hide" focusable="false" />
</ui:UXML>

You'll need to refactor your AuthenticationManager's Start() method to contain code that does the following:

  • Defines the passInput variable as a password TextField.
  • Defines the toggleLoginOrRegisterUIButton variable as a button.
  • Handles the toggle register/login button's click event and toggles between a register mode and login mode

In AuthenticationManager.cs, add the following code to your Start() method, replacing the existing startButton.clicked event handler with the one below:

logoutButton.clicked += RealmController.LogOutBackend;
passInput = root.Q<TextField>("password-input");
passInput.isPasswordField = true;
// when the start button is clicked, toggle between registration modes
startButton.clicked += () =>
{
if (isInRegistrationMode == true)
{
OnPressRegister();
}
else
{
OnPressLoginWithBackend();
}
};
toggleLoginOrRegisterUIButton = root.Q<Button>("toggle-login-or-register-ui-button");
toggleLoginOrRegisterUIButton.clicked += () =>
{
// if in registration mode, swap to the login mode
if (isInRegistrationMode == true)
{
SwitchToLoginUI();
isInRegistrationMode = false;
}
else
{
SwitchToRegisterUI();
isInRegistrationMode = true;
}
};

Next, create a AuthenticationManager.SwitchToLoginUI() and AuthenticationManager.SwitchToRegisterUI() method to toggle between authentication UI modes:

// SwitchToLoginUI() switches the UI to the Login UI mode
private static void SwitchToLoginUI()
{
subtitle.text = "Login";
startButton.text = "Login & Start Game";
toggleLoginOrRegisterUIButton.text = "Don't have an account yet? Register";
}
// SwitchToRegisterUI() switches the UI to the Register UI mode
private static void SwitchToRegisterUI()
{
subtitle.text = "Register";
startButton.text = "Signup & Start Game";
toggleLoginOrRegisterUIButton.text = "Have an account already? Login";
}

Replace the existing AuthenticationManager.OnPressLogin() method with the one below, called OnPressLoginWithBackend that uses Email/Password Authentication:

// OnPressLoginWithBackend() is an asynchronous method that calls
// RealmController.SetLoggedInUser to login and passes the currentPlayer to
// ScoreCardManager and LeaderboardManager; once logged in the login screen
// is hidden and the logout button is shown
private static async void OnPressLoginWithBackend()
{
try
{
var currentPlayer = await RealmController.SetLoggedInUser(userInput.value, passInput.value);
if (currentPlayer != null)
{
HideAuthenticationUI();
}
ScoreCardManager.SetLoggedInUser(currentPlayer.Name);
LeaderboardManager.Instance.SetLoggedInUser(currentPlayer.Name);
}
catch (Exception ex)
{
Debug.Log("an exception was thrown:" + ex.Message);
}
}

Notice that there is now a password parameter for the RealmController.SetLoggedInUser() method. You will update that method in the next step.

Finally, create an AuthenticationManager.OnPressRegister() method that calls a RealmController.OnPressRegister() method that you will create in the next step:

// OnPressRegister() passes RealmController.OnPressRegister() the values of
// the userInput and passInput TextFields in order to register a user
private static async void OnPressRegister()
{
try
{
var currentPlayer = await RealmController.OnPressRegister(userInput.value, passInput.value);
if (currentPlayer != null)
{
HideAuthenticationUI();
}
ScoreCardManager.SetLoggedInUser(currentPlayer.Name);
LeaderboardManager.Instance.SetLoggedInUser(currentPlayer.Name);
}
catch (Exception ex)
{
Debug.Log("an exception was thrown:" + ex.Message);
}
}
5

Currently, the project's only form of authentication is a username input. Let's replace that in the code with realm Email/Password Authentication

In the RealmController.cs, replace the SetLoggedInUser() with an asynchronous method that logs in using the LogInAsync() method to authenticate and obtain a User instance:

// SetLoggedInUser() is an asynchronous method that logs in as a Realms.Sync.User, creates a new Stat object for the current playthrough
// and returns the Player object that corresponds to the logged in Realms.Sync.User
// SetLoggedInUser() takes a userInput and passInput, representing a username/password, as a parameter
public static async Task<Player> SetLoggedInUser(string userInput, string passInput)
{
syncUser = await realmApp.LogInAsync(Credentials.EmailPassword(userInput, passInput));
if (syncUser != null)
{
realm = await GetRealm(syncUser);
currentPlayer = realm.Find<Player>(syncUser.Id);
if (currentPlayer != null)
{
var stat = new Stat();
stat.StatOwner = currentPlayer;
realm.Write(() =>
{
currentStat = realm.Add(stat);
currentPlayer.Stats.Add(currentStat);
});
StartGame();
}
else
{
Debug.Log("This player exists a MongoDB Realm User but not as a Realm Object, please delete the MongoDB Realm User and create one using the Game rather than MongoDB Atlas or Realm Studio");
}
}
return currentPlayer;
}

Next, in the RealmController.cs file, create an asynchronous method that registers a new user by passing a user-provided email and password to the RegisterUserAsync() method:

// OnPressRegister() is an asynchronous method that registers a user,
// creates a new Player and Stat object OnPressRegister takes a userInput
// and passInput, representing a username/password, as a parameter
public static async Task<Player> OnPressRegister(string userInput, string passInput)
{
await realmApp.EmailPasswordAuth.RegisterUserAsync(userInput, passInput);
syncUser = await realmApp.LogInAsync(Credentials.EmailPassword(userInput, passInput));
realm = await GetRealm(syncUser);
var player = new Player();
player.Id = syncUser.Id;
player.Name = userInput;
var stat = new Stat();
stat.StatOwner = player;
realm.Write(() =>
{
currentPlayer = realm.Add(player);
currentStat = realm.Add(stat);
currentPlayer.Stats.Add(currentStat);
});
StartGame();
return currentPlayer;
}

Finally, replace the RealmController.LogOut() method with the RealmController.LogOutBackend() method below that calls User.LogOutAsync() to log out the realm user when log out button is clicked.

// LogOutBackend() is an asynchronous method that logs out
// the current MongoDB Realm User
public static async void LogOutBackend()
{
await syncUser.LogOutAsync();
SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex);
}
6

The current leaderboard shows the top Stat objects when the game first loads. However, if another Player gets a new high score after the game loads on your device, the leaderboard on your device will not be updated.

To fix this, you will update the code in the LeaderboardManager.cs file to do the following:

  • Open a synced realm
  • Listen to changes on all Stat objects
  • Update the Leaderboard GameObject when there is a new high score

Navigate to LeaderboardManager.cs and add the following function that opens a synced realm using the RealmController.syncUser variable that you created earlier:

// GetRealm() is an asynchronous method that returns a synced realm
private static async Task<Realm> GetRealm()
{
var syncConfiguration = new SyncConfiguration("UnityTutorialPartition", RealmController.syncUser);
return await Realm.GetInstanceAsync(syncConfiguration);
}

In LeaderboardManager.cs make the SetLoggedInUser() an asynchronous function and replace the existing Realm.GetInstance() call with a call to the new GetRealm() method. Your code should look something like the following snippet:

// SetLoggedInUser() is an asynchronous method that opens a realm, calls the CreateLeaderboardUI() method to create the LeaderboardUI and adds it to the Root Component
// and calls SetStatListener() to start listening for changes to all Stat objects in order to update the global leaderboard
// SetLoggedInUser() takes a userInput, representing a username, as a parameter
public async void SetLoggedInUser(string userInput)
{
username = userInput;
realm = await GetRealm();

Next, create a Change Listener method:

// SetStatListener sets a listener on all Stat objects, and calls
// SetNewlyInsertedScores if one has been inserted
private void SetStatListener()
{
// Observe collection notifications. Retain the token to keep observing.
listenerToken = realm.All<Stat>()
.SubscribeForNotifications((sender, changes, error) =>
{
if (error != null)
{
// Show error message
Debug.Log("an error occurred while listening for score changes :" + error);
return;
}
if (changes != null)
{
SetNewlyInsertedScores(changes.InsertedIndices);
}
});
}

Create a method, SetNewlyInsertedScores() that does the following:

  • Loops through a list of indices of inserted elements to find the newly created Stat objects.
  • Loops through the current top scores to determine if the new Stat object's score is higher than the current score.
  • Updates the list of current top scores if there is a higher one and updates the UI to display the new list of top scores.
// SetNewlyInsertedScores() determines if a new Stat is
// greater than any existing topStats, and if it is, inserts it into the
// topStats list in descending order
// SetNewlyInsertedScores() takes an array of insertedIndices
private void SetNewlyInsertedScores(int[] insertedIndices)
{
foreach (var i in insertedIndices)
{
var newStat = realm.All<Stat>().ElementAt(i);
for (var scoreIndex = 0; scoreIndex < topStats.Count; scoreIndex++)
{
if (topStats.ElementAt(scoreIndex).IsValid == true && topStats.ElementAt(scoreIndex).Score < newStat.Score)
{
if (topStats.Count > 4)
{ // An item shouldn't be removed if the leaderboard has less than 5 items
topStats.RemoveAt(topStats.Count - 1);
}
topStats.Insert(scoreIndex, newStat);
root.Remove(listView); // remove the old listView
CreateTopStatListView(); // create a new listView
root.Add(listView); // add the new listView to the UI
break;
}
}
}
}

Call the SetStatListener() after the user is logged-in, at the end of the LeaderboardManager's SetLoggedInUser() method:

SetStatListener();

Finally, dispose of the listener token once the Leaderboard GameObject is disabled in the OnDisable() method:

if (listenerToken != null)
{
listenerToken.Dispose();
}
Note
Simulate New Top Scores Through Realm Studio or MongoDB Atlas

Since Unity Hub does not allow opening multiple instances of the same project by default, we recommend simulating a new top score by creating a new Stat object through Realm Studio or MongoDB Atlas. When you create a new Stat object with a high score, your leaderboard updates automatically and displays the new high score. Alternatively, you can use the Unity symbolic link workaround to open a second instance of the project and log in with a different user, and get a new high score.

7

Once you have completed the code, you can run the game and check its functionality. Click the play button on the top of the screen to enter play mode and play the game. Once the Unity Editor opens the GameView, you should see something like the following images.

Enter Play Mode Sync - Login Screen

After you register an account or log in to an existing account, you can play the complete game

Enter Play Mode Sync - Play Game

Here are some things you can try out:

  • Authenticating a new user.
  • Getting a new high score and seeing the leaderboard update in real-time.
  • Creating a new Stat object with a high score in Realm Studio or MongoDB Atlas and seeing the leaderboard update in real-time.
Note
Give Feedback

How did it go? Use the Give Feedback tab at the bottom right of the page to let us know if this tutorial was helpful or if you had any issues.

Give Feedback
© 2021 MongoDB, Inc.

About

  • Careers
  • Legal Notices
  • Privacy Notices
  • Security Information
  • Trust Center
© 2021 MongoDB, Inc.