Learn

Creating a Real-time Leaderboard with UE5 and Redis

Overview#

Where Redis fit into game development#

Redis's performance, low latency, and versatility make it an excellent choice for building responsive and scalable game backends, especially for real-time multiplayer games. There are several use cases where Redis can improve your game development pipeline, the most common and impactful are real-time leaderboards and matchmaking. Other use cases include: session management, game state data caching, inventory, analytics, rate limiting.

Empowering Real-Time Game Development with the Speed of Redis and the Power of Unreal

Unreal Engine is a cornerstone of the video game industry, trusted by AAA studios and indie developers alike for its unmatched graphical fidelity, flexible architecture, and powerful Blueprints system. As a real-time 3D creation platform, Unreal enables developers to build immersive, interactive experiences across platforms—from PC and consoles to mobile and XR.

Redis, the world’s most popular in-memory data store, brings extreme speed, simplicity, and scalability to real-time applications. With sub-millisecond response times, built-in data structures (such as lists, sets, sorted sets, hashes, and streams), and support for pub/sub messaging, Redis is a perfect match for the demanding performance needs of multiplayer games, live leaderboards, matchmaking, state synchronization, and analytics.

Tutorial#

NOTE

There is also a video version of this tutorial on the Redis YouTube channel.

Introduction#

Welcome to this tutorial on creating a real-time leaderboard with Redis and Unreal Engine 5. To get you started, we have an example project named Redis Racer that you can download from GitHub. There is also a written version of this tutorial for your reference. The links to which will be in the description below.  The prerequisites this tutorial are:

  • Unreal Engine 5
  • VS Code or an IDE of your choice
  • Docker Desktop
  • And optionally, Postman, to test our game backend RestAPI

For our leaderboard, we will be using a sorted set as the data type. A sorted set is a collection of unique strings (members) ordered by an associated score, which is perfect for showing the names and current scores of the top scoring players.

Build your leaderboard API#

Code Review

  1. 1.Download the project from GitHub and open the project in VS Code
  2. 2.Look at the game-backend leaderboard code, specifically the addLeaderboardEntry and the getLeaderboard functions:
addLeaderboardEntry
export async function addLeaderboardEntry(key, score, member) {
  const redis = await getClient();
  const date = new Date();

  //ZADD key score member, where member is a string that includes the member name and the date
  const result = await redis.zAdd(key,[{ value: member.toUpperCase() + "-" + date.toISOString(), score: score }]);

  if (result > 0) {
    return { status: 200, message: "ZADD success, added new leaderboard entry."};
  } else {
    return { status: 400, message: "ZADD failed..." };
  }  
}

In this function, we use the Redis client to send the ZADD command to add a new member to a sorted set. If there is currently no sorted set with this key, the ZADD command will also create a new sorted set. The member variable is the initials of the player appended to the current timestamp to allow for multiple entries of the same initials to be added to the leaderboard. If the result of the ZADD command is an integer greater than 0, the command was successful, otherwise the command failed.

getLeaderboard
export async function getLeaderboard(key, count) {
  const redis = await getClient();

  //ZRANGE key start stop [WITHSCORES] [REV]
  const result = await redis.zRangeWithScores(key, 0, count-1, { REV: 'true' });

  if (result.length === 0) {
    return { status: 404, message: "No leaderboard entries found." };
  }

  const leaderboard = {
    "leaderboard": result
  };

  return leaderboard;
}

In this function the Redis client is used to send a ZRANGE command with the WITHSCORES and REVERSE options. The count variable is the total number of entries we want to retrieve from the leaderboard. Since the result is a zero-based array, we will pass in 0 for the start parameter and count -1 for the stop parameter. If the result is an empty array, no leaderboard entries were found.

Testing the RestAPI

  1. 1.Run the Docker containers
cp .env.example .env
docker compose up -d

2. Test the RestAPI with curl or Postman:

GET /leaderboard/:key
curl -X GET "http://localhost:3000/api/leaderboard/<key-name>?count=<number_of_entries>"

// example
curl -X GET "http://localhost:3000/api/leaderboard/redis-racer?count=10"
POST /leaderboard
curl -X POST http://localhost:3000/api/leaderboard -H "Content-Type: application/json" -d "{\"key\": <key_name>, \"score\": <score>, \"member\": <player_initials>}"

// example
curl -X POST http://localhost:3000/api/leaderboard -H "Content-Type: application/json" -d "{\"key\": \"redis-racer\", \"score\": 1564, \"member\": \"rrt\"}"

Setup Redis Racer in UE5#

Add VaRest plugin

  1. 1.Claim the VaRest plugin in the Fab Marketplace
  2. 2.In the Epic Launcher, install the plugin to UE.
  3. 3.Open the RedisRacer project.
  4. 4.Go to Edit → Plugins
  5. 5.Search for VARest
  6. 6.Tick the checkbox to enable the plugin and restart the editor.

Create the BP_Leaderboard blueprint

  1. 1.In the Content Browser, create a new blueprint actor named BP_Leaderboard.
  2. 2.In the Event Graph, create 3 custom events:
  3. 3.AddLeaderboardEntry,
  4. 4.GetLeaderboard, and
  5. 5.CreateRaceEndWidget
  6. 6.For EventBeginPlay,
  7. 7.Get the game mode, cast it to RedisRacerGameMode and promote it to variable.
  8. 8.From the RedisRacerGameMode, bind event to event RaceEnd
  9. 9.Create a custom event called RaceEnd
  10. 10.Promote win, score, and initials to variables.
  11. 11.Call the AddLeaderboardEntry event
  12. 12.Followed by the GetLeaderboard event

EventBeginPlay
  1. 1.AddLeaderboardEntry ⇒ RestAPI call to update leaderboard
  2. 2.For the AddLeaderboardEntry, we’ll use the VARest subsystem
  3. 3.ConstructJsonRequest
  4. 4.It will be a POST API call with Json as the ContentType
  5. 5.From the return value of the Json Request, we will find SetRequestObject
  6. 6.Drag out from the JsonObject and MakeJson
  7. 7.Add 3 string elements to the JsonObject
  8. 8.key,
  9. 9.score, and
  10. 10.member
  11. 11.Create a new variable and name it LeaderboardKey, remember to compile and input the leaderboard key name later.
  12. 12.Connect the variables, LeaderboardKey, Score, and Initials to the MakeJson node.
  13. 13.Drag out from the return value of the ConstructJsonRequest and find ProcessURL
  14. 14.The URL will be http://localhost:3000/api/leaderboard
  15. 15.From the return value of the ConstructJsonRequest find BindEventToOnRequestComplete and BindEventToOnRequestFail
  16. 16.We will create one custom event for both of these events.
  17. 17.GetResponseContentAsString and connect it to a PrintString

AddLeaderboardEntry
  1. 1.GetLeaderboard ⇒ RestAPI call to retrieve leaderboard
  2. 2.Use the VARest subsystem.
  3. 3.ConstructJsonRequest
  4. 4.The verb is GET and ContentType is Json
  5. 5.From the return value, find ProcessURL
  6. 6.I’ll copy the URL from our testing in Postman
  7. 7.From the return value of the ConstructJsonRequest find BindEventToOnRequestFail and BindEventToOnRequestComplete, but this time we will create two separate custom events.
  8. 8.GetLeaderboardFail, will be the same GetResponseContentAsString connected to a PrintString for debugging.

GetLeaderboard
  1. 1.For GetLeaderboardComplete
  2. 2.Connect GetResponseObject to a BreakJson
  3. 3.Add one element named “leaderboard” (all lowercase), and it is an array of objects.
  4. 4.From the array of objects, get a ForEachLoop, for each array element:
  5. 5.GetStringField with the FieldName of “value”
  6. 6.GetIntegerField with the FieldName of “score”
  7. 7.For the string field
  8. 8.We’ll take a left with count 3, this is to get just the player initials
  9. 9.We will then append the array index + 1 to designate the player rank on our leaderboard.
  10. 10.Create a Leaderboard variable with the type of string:integer map to store the results from the GetLeaderboard API call.
  11. 11.Get a reference to the Leaderboard variable we just created and find Add to add entries.
  12. 12.Plug in the StringField result and the IntegerField into the Leaderboard.
  13. 13.When the ForEachLoop is completed, call CreateRaceEndWidget

GetLeaderboardComplete
  1. 1.CreateRaceEndWidget
  2. 2.CreateWidget, find W_RaceEnd
  3. 3.Connect the Win, Leaderboard, and Score variables to the input of the W_RaceEnd Widget.
  4. 4.Then add the widget to viewport.

CreateRaceEndWidget
  1. 1.Compile and set the default value for the LeaderboardKey variable, which is “redis-racer” for this tutorial.
  2. 2.Add the BP_Leaderboard blueprint to the level, and test play!

Connecting to Redis Cloud#

Creating a new Redis database

  1. 1.Go to https://redis.io/try-free/ to create a new account or sign into the Redis Cloud console.  
  2. 2.Click New database, you can try 30 MB Redis Essentials for free with no time limit.
  3. 3.Rename the database if you’d like
  4. 4.Select your preferred cloud vendor and the region closest to you.
  5. 5.Select the 30 MB free option and click Create database.
  6. 6.Wait for the database to be provisioned and copy the Public endpoint.

Updating environment variables

Now we need to update our game backend code to use the Redis Cloud database. Here, we have two choices: continue using Docker to run our game backend container or run the game backend server with NodeJS. Both methods require updating the Redis URL to point to the Redis Cloud database. The difference is which environment variable file to update.

To continue using Docker
  1. 1.Open the .env.docker environment variable file and replace the localhost connection string with your Redis Cloud public endpoint.
  2. 2.Go back to the Redis Cloud console and scroll down to find the Security section.
  3. 3.Click on the header to expand the security section
  4. 4.Then click Copy under Default user password to copy the password.
  5. 5.Note that you can change the password by clicking on the Edit button near the top right corner.
  6. 6.Go back to the .env.docker file in VS Code
  7. 7.Append the username and password to the connection string

REDIS_URL="redis://default:<password>@<public_endpoint>"

8. Save and run the docker containers

docker compose down
docker compose up -d

9. In Docker Desktop

10. Stop the local Redis container but leave the game backend leaderboard running.

If you would rather use NodeJS
  1. 1.Create an .env file by copying the .env.example file
  2. 2.Make changes to the .env file instead of the .env.docker file

cp .env.example .env
npm install
npm run dev

Testing the Redis Cloud database

  1. 1.Go back to our project folder and open the Redis Racer UE5 project by clicking on the uproject file.
  2. 2.There’s a shortcut to reach the finish line without going through the entire course and that’s to go in reverse.
  3. 3.Test play the game, wait until the timer in the top right corner starts, and drive backwards!
  4. 4.This will call the RaceEnd event in the BP_Leaderboard which will add an entry to the Redis Cloud leaderboard and get the top scores to display in the W_RaceEnd widget.
  5. 5.Do this a few more times to add a few entries into our leaderboard.

View the Redis Cloud database using Redis Insight

We can use Redis Insight to get a graphical user interface of our Redis databases. Previously, you had to install the desktop application and connect it to your cloud database but now we have Redis Insight in the cloud! That means you can look at your Redis Cloud database in your web browser without having to install the desktop application.

  1. 1.To access Redis Insight in the Cloud, simply go to your database in the Redis Cloud console, under the General section, click Connect.
  2. 2.In the panel that opens up, click Redis Insight dropdown and click Launch Redis Insight web.

There we have it, we can see our leaderboard entries! Our game backend is now connected to Redis Cloud and we have a real-time leaderboard with Redis and Unreal Engine 5! If you liked this tutorial, take a look at the video version on YouTube!