Hoe een zelf gehoste PHP / SQL Leaderboard voor uw spel te coderen

In dit artikel gaan we ons eerste MySQL-leaderboard maken om te hosten op een website of webserver met behulp van eenvoudige PHP en een beetje SQL. We zullen dan een eenvoudig Unity-voorbeeld maken in C # using GUIText objecten om nieuwe scores aan ons leaderboard toe te voegen, de top tien scores weer te geven en de score en rang van een gebruiker weer te geven.


Invoering

Single-player games zijn leuk, maar het verslaan van je eigen highscore kan saai worden. Het toevoegen van een leaderboard aan je spel biedt echte motivatie voor spelers om hun scores te verbeteren en je spel meer te spelen, en het kan zelfs worden gebruikt om erachter te komen of je spel te gemakkelijk of te moeilijk is. In games die voor altijd doorgaan, kunnen leaderboards de enige reden zijn waarom je spelers spelen. Als je een eigen website of server hebt, wil je misschien je eigen scorebord hosten, zodat je volledige controle over je spel hebt.


Uw leaderboard creëren

Allereerst moet je een SQL-database op je server of site hebben. Websites worden vaak geleverd met een ingebouwde MySQL-database. Details hiervan zijn afhankelijk van de service die u gebruikt, maar u moet uw SQL-host, gebruikersnaam en wachtwoord (evenals uw databasenaam) kunnen vinden in uw beheerdersvenster of registratie-e-mailadres..

In dit voorbeeld wordt phpMyAdmin gebruikt om toegang te krijgen tot de database (ingebouwd in het admin-paneel). U wilt uw database openen en het SQL-tabblad openen. Als u meer controle hebt over uw server, kunt u een nieuwe database maken.

Voeg vervolgens de volgende SQL in:

 MAKEN TAFEL Scores (naam VARCHAR (10) NIET NULL DEFAULT 'Anoniem' PRIMAIRE SLEUTEL, score INT (5) ONGESCHAKELD NIET NULL DEFAULT '0', ts TIMESTAMP NIET NULL DEFAULT CURRENT_TIMESTAMP) MOTOR = InnoDB;

Hiermee wordt een tabel met drie variabelen gemaakt:

  • naam, die de naam van uw gebruikers bevat en die 10 tekens zal opslaan. Dit is de hoofd-ID van onze tabel, dus dat betekent dat het slechts één rij per gebruikersnaam kan opslaan.
  • partituur, die de hoogste score van elke gebruiker bevat. In dit voorbeeld is het een niet-ondertekende variabele, dus deze kan alleen positief zijn. Als je negatieve scores wilt hebben, moet je dat veranderen.
  • ts, een tijdstempel die we kunnen gebruiken om de volgorde van ons scorebord te wijzigen.

Nu, als u SQL Server gebruikt en niet MySQL, kunt u nog steeds gebruiken TIMESTAMP, maar voor de waarde die je moet gebruiken KRIJG DATUM() in plaats van CURRENT_TIMESTAMP.

Een extra ding om in gedachten te houden: als je een heel eenvoudig spel maakt, wil je misschien niet scores aan namen koppelen (om elke speler toe te laten meerdere scores in je Top 10 te hebben, bijvoorbeeld). Dit kan echter een slecht idee zijn; je hebt misschien een speler die zo goed is dat ze je hele Top 10 kunnen domineren! In dit geval zult u niet willen naam als een primaire sleutel, en u wilt dit ook toevoegen:

 id INT (8) UNSIGNED NOT NULL AUTO_INCREMENT PRIMAIRE SLEUTEL

Dit zorgt ervoor dat voor elke score een waarneembare nieuwe rij wordt toegevoegd.

Klik Gaan en je bent klaar! Je tafel is nu helemaal klaar.


Uw PHP-bestanden instellen

Nu moet je wat PHP-bestanden maken. Dit zijn de middelste mannen van de operatie, die Unity een manier bieden om toegang te krijgen tot uw server. Het eerste PHP-bestand dat je nodig hebt is AddScore.php. U moet de serverinformatie van tevoren kennen.

  

(Vervangen SQLHOST, SQLUSER, SQLPASSWORD en YOURDATABASE met uw eigen informatie.)

Hier hebben we zojuist geprobeerd verbinding te maken met de database. Als de verbinding mislukt, wordt Unity geïnformeerd dat het verzoek niet is gelukt. Nu wil je wat informatie doorgeven aan de server:

 $ username = mysql_real_escape_string ($ _ GET ['name'], $ db); $ score = mysql_real_escape_string ($ _ GET ['score'], $ db); $ hash = $ _GET ['hash']; $ PrivateKey = "ADDYOURKEY";

De hash wordt gebruikt om uw gegevens te versleutelen en voorkomt dat mensen uw scorebord hacken. Het wordt gegenereerd met een verborgen sleutel in Unity en hier, en als de twee hashes overeenkomen, krijgt u toegang tot uw database.

 $ expected_hash = md5 ($ gebruikersnaam. $ score. $ privateKey); if ($ expected_hash == $ hash) 

Hier genereren we de hash zelf en controleren we dat de hash die we van Unity indienen identiek is aan de hash die we verwachten. Als dat zo is, kunnen we onze vraag verzenden!

 $ query = "INSERT INTO scores SET naam = '$ naam', score = '$ score', ts = CURRENT_TIMESTAMP

Dit is de eerste helft van onze SQL-query. Het moet redelijk zelfverklarend zijn; de ingediende score en gebruikersnaam worden toegevoegd aan de tabel en de tijdstempel wordt bijgewerkt. De tweede helft is ingewikkelder:

 OP DUPLICATE KEY UPDATE ts = if ('$ score'> score, CURRENT_TIMESTAMP, ts), score = if ('$ score'> score, '$ score', score); ";

We controleren eerst of de gebruikersnaam (onze primaire sleutel) al een rij bevat. Als dat het geval is, wordt onze invoer bijgewerkt in plaats van een nieuw item in te voegen. We willen de tijdstempel bijwerken en scoren, maar alleen als de nieuwe score hoger is!

Gebruik makend van als statements, we zorgen ervoor dat de nieuwe waarden alleen worden gebruikt als de nieuwe score groter is dan de huidige score, anders worden de originele waarden gebruikt.

 $ result = mysql_query ($ query) of die ('Query failed:'. mysql_error ()); ?>

Eindelijk voeren we onze vraag uit en sluiten we onze PHP.

Dit bestand gaat op onze server. U moet de URL onthouden. Evenzo moeten we nog twee PHP-bestanden maken met verschillende zoekopdrachten, die we zullen noemen TopScores.php en GetRank.php.

De Topscores zoekopdracht is gewoon:

 SELECT * FROM Scores BESTELLEN op score DESC, ts ASC LIMIT 10

Dit neemt de top 10-waarden op basis van de score, en voor gekoppelde rangen plaatst de speler die de score het verst heeft bereikt de tafel op. Deze keer willen we ook gegevens extraheren, dus we voegen ook toe:

 $ result_length = mysql_num_rows ($ resultaat); voor ($ i = 0; $ i < $result_length; $i++)  $row = mysql_fetch_array($result); echo $row['name'] . "\t" . $row['score'] . "\n"; 

Hiermee worden onze resultaten geëxtraheerd en op een zodanige manier getabelleerd dat we ze in arrays in Unity kunnen plaatsen.

Eindelijk, we hebben GrabRank:

 SELECT uo. *, (SELECT COUNT (*) FROM Scores ui WHERE (ui.score, -ui.ts)> = (uo.score, -uo.ts)) AS rank FROM Scores uo WHERE name = '$ name' ;

Dit geeft ons de rang van onze speler in het scorebord. We kunnen het dan extraheren door te echoën $ Row [ 'rang'].

Onze broncode bevat ook een ontsmettingsfunctie, die voorkomt dat gebruikers scheldwoorden invoeren op je scorebord, of een poging tot een SQL-injectie probeert.


Een eenvoudige minigame in eenheid maken

Nu hebben we een spel nodig om ons highscore bord te gebruiken! We gaan gewoon testen hoeveel klikken elke gebruiker in tien seconden kan maken, maar je kunt je scorebord toevoegen aan elke game.

lay-out

We beginnen met het maken van vier GUIText voorwerpen. Deze moeten voor het gemak in het midden worden verankerd. U kunt deze aanpassen met pixel-offset om ze op de juiste plaats te krijgen, maar als u wilt dat ze hun positie aanpassen voor elke resolutie, is het eenvoudiger om de X en Y positie (tussen 0 en 1); anders moet u ze bij het opstarten aanpassen.

U zult echter de lettergrootte bij het opstarten moeten aanpassen als u bij alle resoluties wilt werken. Een snelle manier om dit te doen is door ze te baseren op de hoogte van het scherm. We kunnen dit doen door een klasse te maken die dit doet en deze aan al onze tekstobjecten te koppelen, maar het is veel eenvoudiger om dit allemaal vanuit één klasse te doen.

Het maakt niet echt uit welk object we kiezen als onze "manager", dus we kunnen deze klasse gewoon op onze klikbalie plaatsen. Dus in onze eerste klas schrijven we:

 void Start () foreach (GUIText chosentext in FindObjectsOfType (typeof (GUIText)) als GUIText []) chosentext.blah.fontSize = Mathf.FloorToInt (Screen.height * 0.08f); 

Dit vindt elk tekstobject in de scène en schaalt dit naar een verstandige grootte.

Nu willen we dat de klikmeter groter is dan de andere tekst, dus als we deze klasse daar ophangen, hebben we de toegevoegde bonus dat we ook kunnen controleren of de guiText in kwestie is degene die hieraan is gehecht GameObject:

 if (blah == guiText) blah.fontSize = Mathf.FloorToInt (Screen.height * 0.18f); anders [etc.]

gameplay

Het klikgedeelte van de game is heel eenvoudig. In het begin willen we niet dat de timer aftelt tot de eerste klik, dus we maken er twee privé bools in onze klas - firstClick en allowedToClick. In Begin() we kunnen instellen firstClick naar vals en allowedToClick naar waar.

Nu hebben we de teller nodig om de klikken daadwerkelijk vast te leggen en er zijn een aantal manieren om dit te doen. We zouden een integer-variabele kunnen behouden die de score bijhoudt, of we zouden het iets minder efficiënt kunnen maken, maar op één regel (en met zoiets eenvoudigs hoeven we niet echt te optimaliseren, maar het is een goede gewoonte). Dus we registreren de klik in de Bijwerken() functie, en verhoog de waarde door de string te lezen.

 void Update () if (allowedToClick && Input.GetMouseButtonUp (0)) if (! firstClick) firstClick = true; StartCoroutine (Countdown ());  guiText.text = (System.Int32.Parse (guiText.text) + 1) .ToString (); 

Zoals u hier kunt zien, wordt de incrementatie bereikt door de tekenreeks als een geheel getal te lezen, er een toe te voegen en vervolgens terug te converteren naar een tekenreeks. U ziet ook hier dat we een coroutine hebben uitgevoerd zodra de gebruiker voor het eerst klikt, waardoor het aftellen begint.

We gebruiken recursie in deze functie. Nogmaals, we zouden een geheel getal kunnen gebruiken dat de aftelwaarde voor efficiëntie bevat, maar we zullen stringmanipulatie opnieuw gebruiken.

 IEnumerator Countdown () rendement opleveren nieuwe WaitForSeconds (1); counter.guiText.text = (System.Int32.Parse (counter.guiText.text) - 1) .ToString (); if (counter.guiText.text! = "0") StartCoroutine (Countdown ());  else allowedToClick = false; GetComponent() .Setscore (System.Int32.Parse (guiText.text)); toptext.guiText.text = "Voer uw gebruikersnaam in."; GetComponent() .enabled = true; 

Opmerking: het is belangrijk dat we het hebben gebruikt StartCoroutine () en noemde deze functie niet gewoon, omdat het een is IEnumerator. De opbrengst verklaring zorgt ervoor dat het een seconde wacht voordat er actie wordt ondernomen. Het verwijdert een van de teller en als de waarde niet nul is, roept deze zichzelf opnieuw op. Op deze manier telt de functie af totdat deze bereikt is 0.

Naam invoeren

Hierna stopt het de gebruiker om te klikken, vraagt ​​het om de gebruikersnaam en krijgt toegang tot onze tweede en derde klas (die we gaan schrijven!). We zullen kijken naar wat deze nu doen, te beginnen met NameEnter.

In NameEnter () we gaan een gebruiker toestaan ​​om zijn gebruikersnaam in te typen, met enkele beperkingen. Aanvankelijk willen we het onderstrepingsteken weergeven _, die worden gewist zodra ze hun naam beginnen te typen. Bovendien willen we niet dat ze karakters zoals kunnen gebruiken \ of ', omdat deze onze SQL-query's zouden verknoeien.

We gaan een stringbuilder gebruiken om dit te maken. Eerst plaatsen we enkele variabelen bovenaan onze klasse:

 private int MaxNameLength = 10; private StringBuilder playerName; private bool backspaceopunt; private bool initialpress;

De MaxNameLength moet op dezelfde lengte worden ingesteld als je hebt gebruikt voor je VARCHAR lengte wanneer je je tafel hebt gemaakt. Hier hebben we onze snarenbouwer, Naam speler, en twee Booleans. De eerste, backspacepossible, is om de mogelijkheid van de gebruiker om de backspace ingedrukt te houden om tekens te wissen, te regelen. De tweede is om aan te geven of ze hun naam al zijn gaan typen.

In Begin(), we moeten voor een paar dingen zorgen. We schakelen alle tekst uit, behalve degene die is gebeld Toptext; we kunnen dat doen in a foreach loop, zoals eerder.

 void Start () foreach (GUIText-tekst in FindObjectsOfType (typeof (GUIText)) als GUIText []) if (text.name! = "Toptext") text.guiText.enabled = false;  GetComponent() .enabled = false; playerNameTemp = new StringBuilder (); playerNameTemp.Append ( "_"); backspacepossible = true; initialpress = false; 

Hier kunt u zien dat we een paar dingen hebben gedaan. We hebben onze initiële klasse uitgeschakeld (ClickTimes) omdat we het niet meer gebruiken. We hebben ook een instantie gemaakt van playerNameTemp en voegde eraan toe _, zodat spelers kunnen zien waar hun naam naartoe gaat en we onze variabelen hebben geïnitialiseerd.

Nu moeten we toestaan ​​dat de speler zijn naam daadwerkelijk invoert. Aan het einde van Bijwerken() we plaatsen het volgende fragment:

 guiText.text = playerNameTemp.ToString ()

Dit zorgt ervoor dat de tekst weergeeft wat onze tekenreeksbouwer opneemt.

Vervolgens verwerken we tekeninvoer:

 if (playerNameTemp.Length < MaxNameLength)  foreach (char c in Input.inputString)  if (char.IsLetterOrDigit(c) || c == '_' || c ==")  if (!initialpress)  initialpress = true; playerNameTemp.Remove(0, 1);  playerNameTemp.Append(c);   

Dus, op voorwaarde dat de lengte van de tekenreeksbouwer kleiner is dan de maximale naamlengte, en zolang de gebruiker tekens invoert die letters, cijfers, spaties of onderstrepingstekens zijn (hoewel u er misschien alleen voor kiest om alfanumerieke tekens toe te staan), zal de tekenreeks toegevoegd met het nieuwe cijfer. In het geval dat dit de eerste keer is, wordt het oorspronkelijke onderstrepingsteken verwijderd voordat de nieuwe letter wordt toegevoegd.

Volgende:

 if (playerNameTemp.Length> 0) if (Input.GetKeyDown (KeyCode.Backspace)) if (! initialpress) initialpress = true;  backspacepossible = false; StartCoroutine (BackspaceInitialHold ()); playerNameTemp.Remove (playerNameTemp.Length - 1, 1);  else if (backspacepossible && Input.GetKey (KeyCode.Backspace)) backspacepossible = false; StartCoroutine (BackspaceConstantHold ()); playerNameTemp.Remove (playerNameTemp.Length - 1, 1);

Zolang er geen tekens over zijn in onze tekenreeksbuilder en backspace mogelijk is, kan de gebruiker tekens verwijderen. Let op het verschil tussen de eerste en tweede stelling. Het eerste gebruikt GetKeyDown (), terwijl de laatste gebruikt GETKEY () (en controleert onze bool). Het onderscheid is dat we een karakter moeten wissen elke keer dat de gebruiker op de backspace drukt, maar niet constant terwijl de gebruiker hem tegenhoudt.

De coroutines BackspaceInitialHold () en () gewoon wachten 0.15 en 0.05 seconden, en stel vervolgens in backspacepossible naar waar. Dus nadat onze gebruiker backspace heeft ingedrukt 0.15 seconden, zolang ze nog steeds een spatie bevatten, wordt er elk teken gewist 0.05 seconden (zolang de lengte is groter dan code> 0).

Ook bepalen we dat als dit de eerste knop is die de gebruiker drukt, initialpress wordt geactiveerd (dus het zal niet proberen een personage te verwijderen als ze eenmaal op iets anders drukken).

Als klap op de vuurpijl moeten we toestaan ​​dat de gebruiker op drukt terugkeer om de naaminvoer te voltooien.

 if (playerNameTemp.Length> 0 && initialpress) if (Input.GetKeyDown (KeyCode.Return)) foreach (GUIText-tekst in FindObjectsOfType (typeof (GUIText)) als GUIText []) text.guiText.enabled = false;  GetComponent() .SetName (guiText.text); GetComponent() .enabled = true; enabled = false; 

Zolang de gebruiker een soort van invoer heeft gemaakt en de lengte is groter dan 0, de naam wordt geaccepteerd. Al onze tekstobjecten worden verwijderd, we schakelen deze klasse uit en we schakelen onze derde klas in, Hoogste score. Alle drie onze klassen moeten in de editor op ons object worden geplaatst.

We hebben net gebeld Hoogste score's SetName () functie, en eerder hebben we gebeld SetScore (). Elk van deze functies stelt eenvoudig de waarden in van privévariabelen die we nu voorleggen aan ons leaderboard.


Toegang tot uw Leaderboard in Unity

Bovenop Hoogste score we willen enkele variabelen declareren. Eerste:

 openbare GameObject BaseGUIText;

Dit is de GUIText prefab dat we ons leaderboard zullen baseren. Zorg ervoor dat de tekst is verankerd aan de middelste linker en linker uitgelijnde tekst. U kunt hier ook een lettertype kiezen.

 private string privateKey = "DE SLEUTEL DIE U VOORAF HEEFT GEGENEREERD"; private string AddScoreURL = "http://yoursite.com/AddScore.php?"; private string TopScoresURL = "http://yoursite.com/TopScores.php"; private string RankURL = "http://yoursite.com/GrabRank.php?"; privé int highscore; eigen string gebruikersnaam; privé int rang;

We hebben al onze waarden van tevoren nodig: de sleutel die u heeft gegenereerd, de URL's waarnaar u uw PHP-bestanden hebt geüpload, enzovoort. De hoogste score en gebruikersnaam variabelen worden ingesteld met behulp van twee openbare functies genaamd SetScore () en SetName (), die we in het vorige gedeelte hebben gebruikt.

Tip: Het is erg belangrijk dat je vraagtekens achterlaat AddScore.php en GrabRank.php! Hiermee kunt u variabelen doorgeven aan uw PHP-bestanden.

We moeten gebruiken IEnumerators hier om onze SQL-query's af te handelen, omdat we moeten wachten op een antwoord. We beginnen onze eerste coroutine, AddScore (), zodra de klasse is ingeschakeld.

 IEnumerator AddScore (string naam, int score) string hash = Md5Sum (naam + score + privateKey); WWW ScorePost = nieuwe WWW (AddScoreURL + "name =" + WWW.EscapeURL (naam) + "& score =" + score + "& hash =" + hash); opbrengst rendement ScorePost; if (ScorePost.error == null) StartCoroutine (GrabRank (naam));  else Fout (); 

Eerst maken we onze hash met de private key, gebruiken we een functie om een ​​MD5-hash te maken op dezelfde manier als PHP's md5 (). Er is een voorbeeld hiervan op de communitywiki van Unity.

Hier, als de server om welke reden dan ook ontoegankelijk is, voeren we een Fout() functie. U kunt kiezen wat u in uw foutenbehandelaar wilt doen. Als de score correct wordt gepost, lanceren we onze volgende coroutine: GrabRank ().

 IEnumerator GrabRank (stringnaam) WWW RankGrabAttempt = new WWW (RankURL + "name =" + WWW.EscapeURL (name)); rendement rendement RankGrabAttempt; if (RankGrabAttempt.error == null) rank = System.Int32.Parse (RankGrabAttempt.text); StartCoroutine (GetTopScores ());  else Fout (); 

Nogmaals, we hebben toegang tot de site en deze keer slaan we de rang op als deze met succes wordt genomen.

Nu kunnen we onze laatste coroutine gebruiken. Deze zal alles vastbinden. We beginnen met het benaderen van de URL voor een laatste keer:

 IEnumerator GetTopScores () WWW GetScoresAttempt = new WWW (TopScoresURL); opbrengst rendement GetScoresAttempt; if (GetScoresAttempt.error! = null) Error ();  else 

Maar deze keer willen we de gegevens die we ontvangen opsplitsen in een array van strings. Allereerst kunnen we een string split gebruiken zoals:

 string [] textlist = GetScoresAttempt.text.Split (nieuwe reeks [] "\ n", "\ t", System.StringSplitOptions.RemoveEmptyEntries);

Dit zorgt ervoor dat elk resultaat een nieuw element in onze reeksreeksen is, zolang een nieuwe regel of een tabblad wordt gevonden (wat het ook zal zijn!). We gaan dit nu splitsen in twee nieuwe arrays genaamd namen en scores.

 string [] Names = new string [Mathf.FloorToInt (textlist.Length / 2)]; string [] Scores = nieuwe string [Names.Length]; voor (int i = 0; i < textlist.Length; i++)  if (i % 2 == 0)  Names[Mathf.FloorToInt(i / 2)] = textlist[i];  else Scores[Mathf.FloorToInt(i / 2)] = textlist[i]; 

We hebben nu twee nieuwe arrays gemaakt die elk half zo groot zijn als de eerste array. Vervolgens splitsten we elke eerste reeks in onze namen array en elke seconde in onze scores rangschikking.

Nu willen we deze als tekst laten zien, dus we moeten onze drie kolommen positioneren. We gaan ze op het scherm schalen, zodat ze in elke resolutie passen. Eerst zullen we de beginposities aangeven waar onze titeltekst zal komen:

 Vector2 LeftTextPosition = new Vector2 (0.22f, 0.85f); Vector2 RightTextPosition = new Vector2 (0.76f, 0.85f); Vector2 CentreTextPosition = new Vector2 (0.33f, 0.85f);

En nu zijn we klaar om onze tekstobjecten te maken op basis van onze BaseGUIText prefab. We instantiëren de titels individueel en stellen hun tekst in - bijvoorbeeld:

 GameObject Scoresheader = Instantiate (BaseGUIText, nieuwe Vector2 (0.5f, 0.94f), Quaternion.identity) als GameObject; Scoresheader.guiText.text = "Hoge scores"; Scoresheader.guiText.anchor = TextAnchor.MiddleCenter; Scoresheader.guiText.fontSize = 35;

Zodra we dit voor al onze titels hebben gedaan, passen we onze posities aan zodat de nieuwe tekst lager wordt weergegeven.

 LeftTextPosition - = new Vector2 (0, 0.062f); RightTextPosition - = new Vector2 (0, 0.062f); CentreTextPosition - = new Vector2 (0, 0.062f);

Vervolgens voeren we een voor loop die door onze hele top 10 lijst zal herhalen, de naam, rang en score zal instellen (en ervoor zal zorgen dat de tekst op een verstandige manier verankerd is), en dan de posities opnieuw zal aanpassen. Elke iteratie controleren we of de rang van de gebruiker gelijk is aan de weergegeven rang, en zo ja, verkleinen we de tekst zodat de score van de gebruiker geel wordt gemarkeerd:

 voor (int i = 0; i 

En dan, ten slotte, controleren we of de rang van de gebruiker hoger is dan 10. Als dat zo is, plaatsen we hun score onderaan in het elfde vakje, samen met hun rang, en kleurt het geel.

 if (rangorde> 10) GameObject Score = Instantiate (BaseGUIText, RightTextPosition, Quaternion.identity) als GameObject; Score.guiText.text = "" + highscore; Score.guiText.anchor = TextAnchor.MiddleCenter; GameObject Name = Instantiate (BaseGUIText, CentreTextPosition, Quaternion.identity) als GameObject; Name.guiText.text = gebruikersnaam; GameObject Rank = Instantiate (BaseGUIText, LeftTextPosition, Quaternion.identity) als GameObject; Rank.guiText.text = "" + (rang); Rank.guiText.anchor = TextAnchor.MiddleCenter; Score.guiText.material.color = Color.yellow; Name.guiText.material.color = Color.yellow; Rank.guiText.material.color = Color.yellow; 

Conclusie

Voila; ons leaderboard is voltooid! In de bronbestanden heb ik ook een PHP-bestand toegevoegd dat de rangorde boven en onder de gebruikersnaam van de gebruiker zal halen, zodat je ze precies kunt laten zien waar ze zich op het bord bevinden.