Making Cyberglads 8: leaderboard

This is a lightly edited transcript of the above Youtube video. It's the eighth part of a series on game development where I'll be building an entire game step by step and sharing the process in public.

UPDATE 6 JANUARY 2020: starting from version 0.5.0 of the plugin all of the SilentWolf signals have been renamed with the prefix "sw_" to avoid potential collisions with signals from your games. Scores and Leaderboards are backward compatible (connecting to the old signal names will still work for now), but it's a good idea to switch to the new signal names. The video above still shows the old signal names but the transcript below (including the code samples) has the new ones.

Hi and welcome to this 8th instalment in the Making Cyberglads series.

I'm building a game called Cyberglads using the Godot game engine.

Adding a Leaderboard

In the last episode we introduced a scoring system for Cyberglads. Every time the player goes through the entire current version of the game, playing all three NPCs one after the other, a total score is calculated.

That adds some replayability to the game. Players will want to replay multiple times to improve on their score.

As an added incentive, it would be nice to create a leaderboard with the high scores from all players.

In order to do that we'll need to persist these scores in a backend server of some sort, so that we can remember all the scores and we can show the same leaderboard to all the players wherever they are in the world.

For the backend part I'm going to use SilentWolf.

SilentWolf is a service that I built myself for the purposes of Cyberglads.

It's available as a Godot plugin so go ahead and use it if you want to add a leaderboard to your own game for free.

Otherwise there are alternative backend solutions and of course you can build your own game server.

One advantage with SilentWolf is that it plays nice with Godot. It's also really easy to use.

I'm going to download it now from the SilentWolf website.

Getting started with the SilentWolf plugin for Godot engines

Let's quickly create an account.

	
		Browse to the silentwolf.com website.
		Click on "Login" and fill in the Sign up form.
		When the confirmation code form appears, check your mailbox for the code and submit the form.
	
	

Ok I have an account, now I'm going to request an API key.

	
		Still on the SilentWolf website, click on "Get Started". The "Add a Leaderboard for free" page appears.
	
	

I'm going to request an API key for my game Cyberglads.

	
		Enter "Cyberglads" in the game name field and press "Create API key". An API key and game id are generated.
		Keep the tab open.
	
	

Ok so I have an API key and a game id for Cyberglads.

Next step, I'm going to download the SilentWolf plugin by just clicking on this link.

	
		Click on the "Download SilentWolf plugin for Godot engine" link. Download should start.
		Right-click on the downloaded item and open its containing folder.
	
	

Now I'm going to take the plugin and put it my project's addons folder.

	
		Copy the plugin folder zip file to the Godot project's addons folder.
		Unzip the file. Once the contents are unzipped remove the zip file.
	
	

Now when we open Godot Editor we should see the addons folder containing the SilentWolf plugin in the file system explorer pane.

	
		Switch back to Godot Editor
		Open addons folder in the file system explorer pane.
	
	

So you can see the SilentWolf plugin there.

As a side note you might have noticed that I'm now using Godot 3.1 for the first time in this series. Porting the game to 3.1 didn't imply any changes to the game so far so it's been really smooth. But so don't be surprised if the Godot Editor interface looks a little different from previous episodes. It's just that I've upgraded to the latest stable version of Godot.

Back to our plugin, you'll want to make sure that the plugin is autoloaded into your game, just like our global script.

	
		Go to 'Project settings', 'Autoload' tab, and add a new autoloaded script.
		For the Path choose 'res://addons/silent_wolf/silent_wolf.gd' and select 'SilentWolf' as Name. Press Add.
	
	

One last step before we can start using it, we need to configure the plugin.

If you open the GDScript file called SilentWolf.gd at the root of the plugin you'll see the expected configuration.

	
		Open the SilentWolf.gd file
	
	

You could just fill in the values in the two json objects that you see there, but I don't recommend it because if you want to install a newer version of the plugin in the future it will overwrite your config along with the rest of the plugin.

The best way to configure the SilentWolf plugin is by using the configure functions.

Ideally you would call them from an autoloaded singleton. For us that would be the global script.

	
		Open the global.gd script
	
	

In our ready function we're going to make two calls to SilentWolf's configuration API.

	
		In the _ready function, add:
	

	SilentWolf.configure({
          "api_key": "tB2rSreUBaaarTJjrXmKE7bnKg7Tglkn7ZgEF61S",
          "game_id": "Cyberglads",
          "game_version": "0.2.0",
          "log_level": 1
        })

	SilentWolf.configure_scores({
          "open_scene_on_close": "res://scenes/Splash.tscn"
        })
	

Important: In the above function calls, replace the api_key, game_id and game_version fields with your own values (use the api_key and game_id you generated above), otherwise your game will not work with SilentWolf.

The first one is just to set some general configuration for your game; your api key, your game id and game version.

There's also configuration specific to the Scores feature that we're going to use to make our leaderboard. We'll see what this is used for later.

Now that that's done I'm ready to add a Leaderboard to my game.

Displaying the leaderboard at the end of the game

So back to Cyberglads.

You remember that I set up a loop where the player can play against the three named NPCs in succession.

When the loop finishes, so either when the player character dies at some point or if the player defeats Kermit, the final opponent in the game loop, we need to call the server to save the new score.

	
	Open the Arena scene (2D view) and click on the little eye next to the NameDialog node. On orange overlay appears.
	
	

I've added a little overlay that is triggered when that happens, so that the player can enter his name and submit their score.

submit score at the end of the game
This score doesn't really warrant a good name

When the "Submit Score" button is pressed I'm going to call a new function, so let's add a signal to the "pressed" event of the Submit button.

	
	Select the "Submit Score" button in the Arena scene tre view and double-click on the pressed event in the Node tab.
	Connect the signal with a new function in the Arena.gd script.
	
	

This signal will trigger a function in the Arena script.

	
	Add a function in the Arena scene:
	

	func _on_SubmitScoreButton_pressed():
		var name = $"NameDialog/NameInput".text
		global.set_player_name(name)
		SilentWolf.Scores.persist_score(global.player_name, global.total_score)
		SilentWolf.Scores.get_high_scores()
		$"NameDialog".hide()
	

The function takes the player's chosen name from the dialog input field and sets it in a variable in the global object.

It then calls the SilentWolf persist_score() function, passing the player's name and the game's total score.

Finally I'm getting the latest high scores from the server using the get_high_score() function, and I'm hiding the dialog box that we don't need anymore.

Behind the scenes, SilentWolf uses Godot's HTTPRequest node to perform these calls over https, but you don't need to know anything about that because SilentWolf takes care of that complexity for you.

Now that we have the top scores for our game we want to show them in the form of a leaderboard. We could build a new leaderboard from scratch using Container, Panel and Label nodes. You can find out how to play with these node types to build a UI in Godot in my previous videos on building a splash page and a head-up display.

But here I'm just going to use the built-in Leaderboard scene that comes with the SilentWolf plugin.

	
	In the Arena script, add the following function:
	

		func load_leaderboard_screen():
			get_tree().change_scene("res://addons/silent_wolf/Scores/Leaderboard.tscn")
	

In my Arena scene I've added another button to show the Leaderboard at the end of a fight, or at the end of the three character fight loop.

Pressing this button will load SilentWolf's out of the box Leaderboard scene.

a new button to display the leaderboard
Are you sure you want to see this?

You can of course customize the layout of the Leaderboard scene. If you do I recommend you create a new scene that extends from the plugin's Leaderboard scene and attach a script that extends from thee Leaderboard script, otherwise your changes will once again be overwritten by future versions of the plugin.

You could also look at the scene and its attached script to draw inspiration from it and make your own leaderboard.

Let's test the Leaderboard end-to-end. I'm going to go through the 3 NPC's to get a new score and then take a look at the Leaderboard.

		
		Run the game
		In OBS, switch to the game run window
		Play against Ernie, Bert and Kermit
	
	

I've faced all three NPCs one after the other and here is my score in the bottom left.

	
	Press the "Show Leaderboard" button
	
	

So here's the latest leaderboard and you can see that my latest score has already been added.

How the Leaderboard scene works

The Leaderboard scene checks whether the top scores have been loaded from the backend and if not, it fetches them.

In either case it displays the latest results.

By default it retrieves an ordered list the top 10 scores, but you can request more or less through the plugin's configuration.

That's how easy it is to integrate a Leaderboard in your own game.

You can dig into the code to see how the Leaderboard scene works under the hood.

	
	Open Leaderboard.gd script in the script editor.
	
	

When the Leaderboard is loaded it checks whether we've already retrieved the scores from the backend.

If we have, we have still have to make sure that we have all of the player's latest game scores to display.

The plugin saves all of the player's own scores locally and the Leaderboard will merge the scores from the backend with the local scores to show the player how his latest efforts match up with the best without having to do another roundtrip to the server.

If no backend scores have been loaded yet the Leaderboard will try to retrieve them. While we're waiting for the backend call to complete we want to show a Loading message and then replace it with the list of scores once we have them.

To do this we're going to use a Godot feature called a coroutine.

Coroutines lets you stop the execution of a function until something happens and then resume it at the point where you stopped it earlier.

This "yield" function does the trick. In this case we're waiting for a signal called "sw_scores_recived" to be emitted before rendering the Leaderboard.

The signal in question is emitted by the plugin's Scores script once we get an answer back from the backend to our get_high_scores() request.

Coroutines are really useful in combination with signals. We're going to use them a lot more in the future.

Of course since we started from scractch with a new game I'm the first to play it and so there's only one score in the Leaderboard.

Let's add a little script that will run the next time the Arena loads to add some more scores so we can have a proper leaderboard rendered.

	
	Open Splash.gd and add the following to the load_arena() function:
	

	# one time leaderboard fill up
	SilentWolf.Scores.persist_score("alan_duval", 34)
	SilentWolf.Scores.persist_score("HamsterToad", 49)
	SilentWolf.Scores.persist_score("Bolivar", 17)
	SilentWolf.Scores.persist_score("appendage", 6)
	SilentWolf.Scores.persist_score("masterofdisaster", 28)
	SilentWolf.Scores.persist_score("ken", 24)
	SilentWolf.Scores.persist_score("rabidtiger", 39)
	SilentWolf.Scores.persist_score("Bloodnoon", 32)
	SilentWolf.Scores.persist_score("jessie", 41)
	# end leaderboard fill up
	

We'll remove this after the next game. Right now let's play the game again.

I don't have to face all three NPCs to see the leaderboard again, it's also displayed after regular fights.

	
	Play against Bert
	At the end of the fight, press the Show Leaderboard button
	
	

And now we see all the scores in the leaderboard.

built-in Leaderboard scene from the SilentWolf plugin
Behold this gorgeous leaderboard!

Right so this was a pretty run-of-the-mill implementation of a Leaderboard, but there are lots of other things you can do with leaderboards and lists of player scores and stats.

How to use Leaderboards in a game

Ours is a global leaderboard, meaning that it remembers all the scores of all the players who have ever played this version of the game. But sometimes this isn't the most compelling leaderboard you can show especially to new players because if they're not one of the top players of the game their names will never appear at the top of the global leaderboard.

Instead if the player isn't in the top 10, you may want to show a part of the leaderboard that isn't the top of the rankings but maybe the five players who rank just above and below the player's latest score. That way the players have an idea of where they stand and it gives them a short term target in terms of who they can overtake if they improve their performance just a little.

Grouping users by skill level also has benefits in multiplayer games where you could use this information to do matchmaking.

To go one step further you could then organise tournament and leagues where getting the top of a certain leaderboard unlocks the next stage of the competition.

You may want to group players differently. Say you have an RPG type game with different classes of characters like wizards, knights and barbarians. You might want to have different leaderboards for each class.

Or if your game has a geographical dimension like in the case of an augmented reality game that you play outdoors, you could implement a neighborhood leaderboard and a city leaderboard geography.

If the purpose of your game is to have players play with their friends you could also have a social-centric leaderboard where you could see how well you did compared to people you know.

Time-based leaderboards are another option, just rank players alongside whoever else is playong on that day or at that moment.

Mungosgameroom has a video about how Fortnite uses different leaderboards to keep players interested whatever their level. In addition to Global Wins you get a revolving leaderboard that is reset every week and tells you how often you’ve been in the top 10 or top 20. You also have alternate leaderboards where you are grouped together with a random batch of 50 other players.

What should you display on the leaderboard? Here we just put the player's in-game name along with the score and the ranking, but you might want to create a richer leaderboard including each player's avatar. We could have also added the scores that each player got for fighting each of the three NPC's, and then maybe rank the players' performance per NPC.

So there are plenty of ways of getting creative with leaderboards.

If you're looking for a great read on the subject, I recommend a Medium article called "How to motivate using leaderboards" where the authors (Omar Ganai and Steven Ledbetter explore the psychology of leaderboards beyond the scope of video games, and how to design the perfect leaderboard for your application.

If you have an interesting use case for a leaderboard I'd love to hear about it so don't hesitate to reach out.

That's it for this edition of Cyberglads. As always you can download the latest version of the game on the cyberglads.com website

Let's see if you can make the leaderboard!



Never miss a blog entry! Enter your email address here to subscribe to the Cyberglads newsletter: