Adventures in Unity – 1.7b Highscores (In-Game Overview)

Official_unity_logo

 

The unify community ‘Server Side Highscores’ article provided basic code for connecting to the server, downloading the current high score list and uploading new entries.

But no real framework to manage downloads & uploads, nothing to display highscores or to add new entries to the table.

Prior to using the unify community code, I implementing the dreamlo highscore system – I set this up by following the excellent you tube guide ‘[Unity Tutorial] Online Highscores 01 (dreamlo)‘ put together by Sebastian Lague.

While I wasn’t able to get the dreamlo system to work with WebGL games – I did borrow elements of the framework Sabastian demonstrated when constructing high score code for this game.

The final framework can be split into three basic sections;

  1. Backend high score management.
  2. Display current high scores.
  3. Add new high score.

The code itself is surprisingly extensive, so for this post I’ll try and provide a top level overview for each section & try to explain how they all fit together in a high level – Then, for the next few posts I’ll focus on each section in turn and explain in more detail.

 


 

1. Backend high score management;

HighscoreComponent

HighScore (Component) – Holds both HighscoreController and HighscoreDisplay scripts

HighscoreController (script) – A singleton, this script contains methods to get the current highscore list from the server (used by HighScoreDisplay script), and add new highscore entries to the server (used by HighScoreAdd). Also contains a static array of type HighScore where the current list of highscores are held, and a static int lastHighScore which identifies the index of the last highscore added (set to -1 by default)

HighScore (Strut) – A simple object containing two variables, int score & string name. Used to hold the details of a single high score entry. A static list of these is maintained by the HighscoreController script.

GameController (script) – A currently very overloaded script. The original intend for this script was to manage the game states (title screen, in game, game over , new high score screen, display highscores, etc). However it currently also manages the player’s score, the title screen buttons and manages the various highscore states – It does this primarily through two methods;

CheckHighScore, which at game over, checks to see if the player’s score should be added to the highscore list – If the player has scored high, the script both sets the HighscoreController.lastHighScore int, and sets the game state to display the new high score screen.

A second method managed by GameController;  SetHighScore, called by the AddHighScore script once a new player names has been added, directly requests that the highscorecontroller add the new high score to the highscore list (locally and on the server) by calling highscorecontroller.AddNewHighScore, before passing control to the UpdateHighscore method, which changes the game state to Display the current highscore list.

 


 

2. Display High Scores;

HighscoreDisplay

HighScoreDisplay (script) – Attached to the HighScore component; this uses the HighScore (Strut) list in HighscoreController to populate a list of HighScorePanel (Panel)  when displaying the highscore list to the player. Also, every 30 seconds, it requests the HighscoreController to download an updated list of highscores.

HighscorePanel

HighScorePanel (prefab ) – This is a customised panel UI component. It has three Text UI children – PositionText, NameText ScoreText. The values are set by the HighScoreDisplay (script) and used to display the details of a single high score entry when displaying the highscore table.

HighscoreDisplay

HighScoreDisplay (Component) – An empty component, groups the UI elements used to display the high score screen.

HighScoreTextTop (Component ) – A Text UI element, child of the HighScoreDisplay (Component), used to display the title HIGH SCORE on the high score screen.

Scroll View (Component) – A ScrollView UI element, child of the HighScoreDisplay (Component), Used to hold up to 50 HighScorePanel (Panel) prefabs when displaying the highscore table.

 


 

3. Add High Score;

HighscoreAdd

HighscoreAdd (Script) – Contains a number of methods referenced by the UI components of the New Highscore screen. These methods validate and process user input when adding a player new to a new highscore. Ensuring the players name only contains valid characters, switches text case, ensure the players name doesn’t exceed a maximum length putting in a dummy name should the player not enter anything at all – Finally, it calls the SetHighScore method in the gamecontroller script once the new name has been entered.

HighscoreAdd

HighscoreAdd (Component) – An empty component, groups the UI elements used for the new highscore screen.

HighScoreTextTop (Component) – A Text UI element, child of the HighscoreAdd (Component), used to display the title NEW HIGH SCORE! on the add highscore screen.

Window (Component) – An empty component, child of the HighscoreAdd (Component), groups the UI elements used for the new high score screen.

InputField (Component) – An inputfield UI component, child of the window (component), used to display the players name when adding  new highscore. Players can also click directly onto the component and type their name using the computer’s keyboard.

Vertical Group (Component) – An empty component, child of the window (Component), groups and formats the UI elements used for the keyboard buttons on the new high score screen.

Grid (Component) – An empty component, child of the Vertical Group (Component), groups and formats the UI elements used for the keyboard buttons on the new high score screen.

GridCell-X (Component) – Button UI components, child of the Grid (Component). 40 of these operate as the buttons for a virtual keyboard that can be used to add a player name for a new highscore.

 


Play the game (WebGL)

Grab a copy of the project here


 

 


 

Next post: 1.7c Highscores (In-Game Backend)

Last post: 1.7a Highscores (Server Side MySQLi)

Contents page.

 


 

Adventures in Unity – 1.7a Highscores (Server Side)

Official_unity_logo

#1 What would I ideally like to achieve by the time I finish working today – Have a working online highscore system (backend), with the ability to display and update high scores (in-game).

#2 What’s the minimum I’d like to achieve by the close of play – Have a working online highscore system (backend).


To recap – Teaching myself Unity by building a simple game – An infinite runner.

In my opinion, with this style of game, a high score table is vital – I don’t think the game has enough content in and of itself to provide much entertainment beyond the first few plays.

A high score table provides motivation for players to return – In part to beat their own scores – But mainly to make sure nobody else has beaten them.

While a local high score table will add value to the game, I think an online high score table will do a much better job.

Fortunately there is plenty of code out there showing how to implement an online highscore table in Unity – Unfortunately there are far fewer sites showing how to implement an online high score for a WebGL game (my intended final format).

I was going to have to hack something together myself.


The highscore systems I eventually put together is fairly involved, so I’ve broken it into five separate sections;

  1. Server Side Code
  2. In-Game code structure
  3. Highscore Controller
  4. Display High Scores
  5. Add new high score.

This week;

  1. Server Side Code

Exciting stuff! 🙂


By server side code I’m referring to a location online where the highscores are stored, as well as the server side mechanisms to obtain and update the highscores.

To store the highscore I used the free hosting site 000webhost (Edit: 000webhost were terrible hosts I absolutely would NOT recommend them – currently I’m using NameCheap) – They are a hosting provider I’d not used before; but I’d noticed their name being thrown a few times when I was looking to see how to put this together. They provide a free domain, site & mySQL database – All of which were life-savers for me.

The server side code itself, I based the online highscore on the excellent ‘Server Side Highscores‘ sample from the UnifyCommunity website  – This won’t work straight out of the box, especially if you want for WebGL games – But it gave me a nice base to work from.


Following the instructions provided on the UnifyComunity Server Side Highscores page;

I created a simple mySQL highscore using my 000webhost webspace.

Important note: After creating a mySQL database – It takes a little time to actually get up and running – The website suggests a couple of minutes – For me it was a couple of hours.

Once the mySQL database is up and running, it’s time to upload the server side scripts (I used the PHP scripts).

There are three scripts provided in the UnifyComunity instructions;

  1. crossdomain.xml
  2. addscore.php
  3. display.php

crossdomain.xml

This is a required file; The Unity Manual explains ‘The Unity webplayer expects a http served policy file named crossdomain.xml to be available on the domain you want to access with the WWW class, (although this is not needed if it is the same domain that is hosting the unity3d file).

The crossdomain.xml code provided looks like this;

<cross-domain-policy>
  <allow-access-from domain="*"/>
</cross-domain-policy>

Though you may need to add ‘secure = “false”‘ parameter;

< ?xml version="1.0"?>
  <cross-domain-policy> 
  <allow-access-from domain="*" secure="false" />
< /cross-domain-policy>

This file needs to be placed in the root of the site, in the case of 000webhost that’s in the public_html folder crossdomain location


addscore.php

This is called by the game when adding a new highscore. By-and-large I used the PHP as provided;

$db = mysql_connect('mysql_host', 'mysql_user', 'mysql_password') or die('Could not connect: ' . mysql_error()); 
mysql_select_db('my_database') or die('Could not select database');
 
// Strings must be escaped to prevent SQL injection attack. 
$name = mysql_real_escape_string($_GET['name'], $db); 
$score = mysql_real_escape_string($_GET['score'], $db); 
$hash = $_GET['hash']; 
 
$secretKey="mySecretKey"; # Change this value to match the value stored in the client javascript below 

$real_hash = md5($name . $score . $secretKey); 
if($real_hash == $hash) { 
        // Send variables for the MySQL database class. 
        $query = "insert into scores values (NULL, '$name', '$score');"; 
        $result = mysql_query($query) or die('Query failed: ' . mysql_error()); 
}

Since I only want to store the top 50 scores; I added an extra operation to the end of the query ensuring only the top 50 scores are stored;

 $query = "delete from [high score table] Where id not in (select * from(select id from [high score table] order by score desc limit 50) as temp)"; 
 $result = mysql_query($query) or die('Query failed: ' . mysql_error());

resulting in;

$db = mysql_connect('mysql_host', 'mysql_user', 'mysql_password') or die('Could not connect: ' . mysql_error()); 
mysql_select_db('my_database') or die('Could not select database');
 
// Strings must be escaped to prevent SQL injection attack. 
$name = mysql_real_escape_string($_GET['name'], $db); 
$score = mysql_real_escape_string($_GET['score'], $db); 
$hash = $_GET['hash']; 
 
$secretKey="mySecretKey"; # Change this value to match the value stored in the client javascript below 

$real_hash = md5($name . $score . $secretKey); 
if($real_hash == $hash) { 
	// Send variables for the MySQL database class. 
	$query = "insert into blkrnnr_scores values (NULL, '$name', '$score');"; 
	$result = mysql_query($query) or die('Query failed: ' . mysql_error()); 
	//---
			
	$query = "delete from blkrnnr_scores Where id not in (select * from(select id from blkrnnr_scores order by score desc limit 50) as temp)"; 
	$result = mysql_query($query) or die('Query failed: ' . mysql_error()); 
} 

display.php

This script get the highscores from the database so they can be displayed in game. The code as provided returns the top 5 scores – I just amended

DESC LIMIT 5

added an extra 0 so that it would return the top 50 – Otherwise it’s a cut-&-paste from the website.

Also adding

header('Access-Control-Allow-Origin: *');

These scripts just need to be stored on the website, in a location the game can access. Since I’m hoping to use this site to manage the highscores for a number of games, I created a folder for this game – called BLK_RNR – and placed both scripts inside;

updatehighscore scripts location


So that’s almost everything. If you are building a PC game, these should be all the changes you need to make to setup the backend. However, if you want to create an online high score table for a webGL title – This setup won’t work – You’ll need to make one further edit to get everything up and running. After building a WebGL game using Unity 5, two folders will be created – Build and Release. In the build folder you’ll need find a .htaccess file which you’ll need to edit. The default the .htaccess file should look something like this;

Options +FollowSymLinks
RewriteEngine on

RewriteCond %{HTTP:Accept-encoding} gzip
RewriteCond %{REQUEST_FILENAME}gz -f
RewriteRule ^(.*)\.js$ $1\.jsgz [L]

RewriteCond %{HTTP:Accept-encoding} gzip
RewriteCond %{REQUEST_FILENAME}gz -f
RewriteRule ^(.*)\.data$ $1\.datagz [L]

RewriteCond %{HTTP:Accept-encoding} gzip
RewriteCond %{REQUEST_FILENAME}gz -f
RewriteRule ^(.*)\.mem$ $1\.memgz [L]

RewriteCond %{HTTP:Accept-encoding} gzip
RewriteCond %{REQUEST_FILENAME}gz -f
RewriteRule ^(.*)\.unity3d$ $1\.unity3dgz [L]

AddEncoding gzip .jsgz
AddEncoding gzip .datagz
AddEncoding gzip .memgz
AddEncoding gzip .unity3dgz

I stripped all the  AddEncoding lines from the file.

AddEncoding gzip .jsgz
AddEncoding gzip .datagz
AddEncoding gzip .memgz
AddEncoding gzip .unity3dgz

and added

AddType application/octet-stream .memgz .datagz .unity3dgz
AddType application/javascript .jsgz

Header set Access-Control-Allow-Origin "*"

Resulting in (you should be able to just copy and paste this);

Options +FollowSymLinks
RewriteEngine on

AddType application/octet-stream .memgz .datagz .unity3dgz
AddType application/javascript .jsgz
Header set Access-Control-Allow-Origin "*"

RewriteCond %{HTTP:Accept-encoding} gzip
RewriteCond %{REQUEST_FILENAME}gz -f
RewriteRule ^(.*)\.js$ $1\.jsgz [L]

RewriteCond %{HTTP:Accept-encoding} gzip
RewriteCond %{REQUEST_FILENAME}gz -f
RewriteRule ^(.*)\.data$ $1\.datagz [L]

RewriteCond %{HTTP:Accept-encoding} gzip
RewriteCond %{REQUEST_FILENAME}gz -f
RewriteRule ^(.*)\.mem$ $1\.memgz [L]

RewriteCond %{HTTP:Accept-encoding} gzip
RewriteCond %{REQUEST_FILENAME}gz -f
RewriteRule ^(.*)\.unity3d$ $1\.unity3dgz [L]

With this in place I am able to get a list of current high scores – and add new ones from within a Unity WebGL game.


Play the game (WebGL)

Grab a copy of the project here



Next post: 1.7a Highscores (Server Side MySQLi)

Last post: 1.6 PickUps

Contents page.