I denne artikkelen skal vi lage vårt første MySQL leaderboard for å være vert for et nettsted eller en webserver ved hjelp av enkle PHP og noen SQL. Vi vil da lage et enkelt Unity eksempel i C # bruker GUIText
objekter for å legge til nye poeng til vårt toppliste, vise de ti toppene og vise en brukers poengsum og rangering.
Enkeltspiller spill er morsomme, men å slå din egen highscore kan bli kjedelig. Hvis du legger til et leaderboard i spillet, får du virkelig motivasjon for spillerne til å forbedre sine poeng og spille spillet mer, og det kan til og med brukes til å finne ut om spillet ditt er for lett eller hardt. I spill som fortsetter for alltid, kan leaderboards være den eneste grunnen til at spillerne dine spiller. Hvis du har din egen nettside eller server, vil du kanskje være vert for ditt eget leaderboard, så du har full kontroll over spillet ditt.
Først av alt må du ha en SQL-database på serveren eller nettstedet ditt. Nettsteder kommer ofte med en innebygd MySQL-database. Detaljer om dette vil variere avhengig av hvilken tjeneste du bruker, men du bør kunne finne din SQL-verts, brukernavn og passord (samt databasens navn) fra administrasjonspanelet eller registreringsmeldingen.
I dette eksemplet brukes phpMyAdmin for å få tilgang til databasen (bygget rett inn i administrasjonspanelet). Du vil åpne databasen og åpne SQL-fanen. Hvis du har mer kontroll over serveren din, kan du opprette en ny database.
Deretter legger du inn følgende SQL:
CREATE TABLE Scores (navn VARCHAR (10) IKKE NULL DEFAULT 'Anonym' PRIMARY KEY, score INT (5) UTSIGNERT IKKE NULL DEFAULT '0', ts TIMESTAMP IKKE NULL DEFAULT CURRENT_TIMESTAMP) ENGINE = InnoDB;
Dette vil skape et bord med tre variabler:
Navn
, som inneholder brukernavnene dine, og som lagrer 10 tegn. Dette er vårt bords hovedidentifikator, så det betyr at det kun kan lagre én rad per brukernavn.poengsum
, som holder hver brukers høyeste poengsum. I dette eksemplet er det en usignert variabel, så det kan bare være positivt. Hvis du vil ha negative poeng, må du endre det.ts
, et tidsstempel vi kan bruke til å endre rekkefølgen på vårt toppliste.Nå, hvis du bruker SQL Server og ikke MySQL, kan du fortsatt bruke TIMESTAMP
, men for verdien må du bruke GetDate ()
i stedet for CURRENT_TIMESTAMP
.
En ekstra ting å huske på: Hvis du lager et veldig enkelt spill, vil du kanskje ikke knytte poeng til navn (for å tillate at hver spiller har flere poeng i topp 10, for eksempel). Dette kan være en dårlig ide skjønt; du kan ha en spiller som er så god at de kan dominere hele topp 10! I dette tilfellet vil du ikke ha det Navn
som en primærnøkkel, og du vil også legge til dette:
ID INT (8) UTSIGNERT IKKE NULL AUTO_INCREMENT PRIMARY KEY
Dette vil sørge for at en merkbar ny rad blir lagt til for hver score.
Klikk Gå og du er ferdig! Ditt bord er alt klart nå.
Nå må du lage noen PHP-filer. Dette er de midtre mannene i operasjonen, og gir en måte for Unity å få tilgang til serveren din. Den første PHP-filen du trenger, er AddScore.php
. Du må kjenne serverinformasjonen fra før.
(Erstatte
SQLHOST
,SQLUSER
,SQLPASSWORD
ogYOURDATABASE
med din egen informasjon.)Her har vi nettopp prøvd å koble til databasen. Hvis tilkoblingen mislykkes, vil Unity bli informert om at forespørselen mislyktes. Nå skal du sende informasjon til serveren:
$ brukernavn = mysql_real_escape_string ($ _ GET ['navn'], $ db); $ score = mysql_real_escape_string ($ _ GET ['score'], $ db); $ hash = $ _GET ['hash']; $ Privat = "ADDYOURKEY";The hash brukes til å kryptere dataene dine og stoppe folk fra hacking på ditt leaderboard. Det blir generert med en skjult nøkkel i Unity og her, og hvis de to hashene passer, får du tilgang til databasen din.
$ expected_hash = md5 ($ brukernavn. $ score. $ privateKey); hvis ($ expected_hash == $ hash)Her genererer vi hash oss selv og kontrollerer at hash vi sender fra Unity er identisk med hash vi forventer. Hvis det er, kan vi sende forespørselen vår!
$ query = "INSERT INTO Scores SET-navn = '$ name', score = '$ score', ts = CURRENT_TIMESTAMPDette er første halvdel av SQL-spørringen. Det skal være ganske selvforklarende; Innleverte poeng og brukernavn legges til bordet, og tidsstempelet oppdateres. Den andre halvdelen er mer komplisert:
PÅ DUPLIKATE KEY UPDATE ts = hvis ('$ score'> score, CURRENT_TIMESTAMP, ts), score = hvis ('$ score'> score, '$ score', score); ";Vi kontrollerer først om brukernavnet (vår primære nøkkel) allerede har en rad. Hvis det gjøres, i stedet for å sette inn en ny oppføring, blir oppføringen oppdatert. Vi vil oppdatere tidsstempel og score, men bare hvis den nye poengsummen er høyere!
Ved hjelp av
hvis
uttalelser, vi sørger for at de nye verdiene bare brukes hvis den nye poengsummen er større enn dagens poeng, ellers brukes de opprinnelige verdiene.$ result = mysql_query ($ query) eller die ('Query failed:'. mysql_error ()); ?>Til slutt løper vi spørringen vår og lukker vår PHP.
Denne filen går på vår server. Du må huske nettadressen. På samme måte må vi lage to andre PHP-filer med forskjellige søk, som vi vil ringe
TopScores.php
ogGetRank.php
.De
TopScores
spørringen er rett og slett:SELECT * FROM Scores ORDER ved score DESC, ts ASC LIMIT 10Dette vil ta de 10 beste verdiene basert på poengsummen, og for bundet lag setter spillere som har fått poengsummen først lengst oppe på bordet. Denne gangen ønsker vi å trekke ut data også, så legger vi også til:
$ result_length = mysql_num_rows ($ resultat); for ($ i = 0; $ i < $result_length; $i++) $row = mysql_fetch_array($result); echo $row['name'] . "\t" . $row['score'] . "\n";Dette vil trekke ut våre resultater og tabulere dem på en måte som vi kan sette dem inn i arrays i Unity.
Endelig har vi
GrabRank
:SELECT uo. *, (SELECT COUNT (*) FRA Scorer ui WHERE (ui.score, -ui.ts)> = (uo.score, -uo.ts)) AS rang FRA Scores uo WHERE navn = '$ navn' ;Dette vil gi oss vår spillers rangering i resultattavlen. Vi kan da pakke ut det ved å ekko
$ Rad [ 'rang']
.Vår kildekode inneholder også en sanitiser-funksjon, som forhindrer brukere i å legge inn sverdetord på topplisten eller forsøke et SQL-injeksjonsangrep.
Å lage et enkelt minigame i enhet
Nå trenger vi et spill å bruke vår highscore bord med! Vi skal bare teste hvor mange klikk hver bruker kan gjøre innen ti sekunder, men du kan legge til topplisten til et hvilket som helst spill.
Oppsett
Vi starter med å lage fire
GUIText
objekter. Disse bør være forankret om midt senter for enkelhets skyld. Du kan justere disse med pikselforskyvning for å få dem på riktig sted, men hvis du vil at de skal justere posisjonen for enhver oppløsning, er det enklere å endreX
ogY
posisjon (mellom0
og1
); Ellers må du justere dem ved oppstart.Du må imidlertid justere skriftstørrelsen ved oppstart hvis du vil kjøre i alle oppløsninger. En rask måte å gjøre dette på er å basere dem på skjermens høyde. Vi kan gjøre dette ved å lage en klasse som gjør dette og feste det til alle våre tekstobjekter, men det er mye enklere å gjøre alt dette fra en klasse.
Det spiller ingen rolle hva objektet vi velger som vår "manager", så vi kan bare sette denne klassen på våre klikkteller. Så i vår første klasse skriver vi:
void Start () foreach (GUIText chosentext i FindObjectsOfType (typeof (GUIText)) som GUIText []) chosentext.blah.fontSize = Mathf.FloorToInt (Screen.height * 0.08f);Dette vil finne hver tekstobjekt i scenen og skalere den til en fornuftig størrelse.
Nå ønsker vi at klikktelleren skal være større enn den andre teksten, så hvis vi holder denne klassen der, har vi den ekstra bonusen at vi også kan sjekke om
guiText
i spørsmålet er den tilknyttet detteGameObject
:hvis (blah == guiText) blah.fontSize = Mathf.FloorToInt (Screen.height * 0.18f); ellers [etc.]gameplay
Klikkkomponenten i spillet vil være veldig enkelt. I starten ønsker vi ikke at timeren skal telle ned til første klikk, så vi skal lage to private
bools
i vår klasse -firstClick
ogallowedToClick
. IStart()
vi kan settefirstClick
tilfalsk
ogallowedToClick
tilekte
.Nå trenger vi disken til å faktisk registrere klikkene, og det er et par måter å gjøre dette på. Vi kunne holde en heltallvariabel som sporer poenget, eller vi kunne gjøre det litt mindre effektivt, men i en linje (og med noe så enkelt vi ikke trenger å optimalisere, men det er god praksis). Så vi registrerer klikket i
Oppdater()
funksjon, og øk verdien ved å lese strengen.ugyldig oppdatering () if (allowedToClick && Input.GetMouseButtonUp (0)) hvis (! firstClick) firstClick = true; StartCoroutine (nedtellings ()); guiText.text = (System.Int32.Parse (guiText.text) + 1) .ToString ();Som du kan se her, oppnås inkrementet ved å lese strengen som et heltall, legge til en, og deretter konvertere tilbake til en streng. Du vil også se her at vi har kjørt en coroutine så snart brukeren klikker først, som starter nedtellingen.
Vi bruker rekursjon i denne funksjonen. Igjen kan vi bruke et heltall som holder nedtellingen verdi for effektivitet, men vi vil bruke streng manipulering igjen.
IEnumerator Countdown () yield returnere nye 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 = "Skriv inn brukernavnet ditt."; GetComponent () .enabled = true; Merk: Det er viktig at vi brukte
StartCoroutine ()
og kalte ikke bare denne funksjonen fordi den er enIEnumerator
. Deutbytte
uttalelse får det til å vente et sekund før noen tiltak blir tatt. Den fjerner en fra disken, og hvis verdien ikke er null, kalles den igjen. På denne måten teller funksjonen til den når0
.Navn Oppføring
Etter dette stopper brukeren å klikke, spør etter brukernavnet, og får tilgang til våre andre og tredje klasser (som vi skal skrive!). Vi tar en titt på hva disse gjør nå, fra og med
NameEnter
.I
NameEnter ()
Vi skal tillate en bruker å skrive inn brukernavnet sitt, med noen begrensninger. I utgangspunktet ønsker vi å vise understrekkskarakteren_
, som blir slettet så snart de begynner å skrive navnet sitt. På toppen av dette ønsker vi ikke at de skal kunne bruke tegn som\
eller'
, som disse ville ødelegge våre SQL spørringer.Vi skal bruke en strengbygger for å lage dette. Først plasserer vi noen variabler øverst i vår klasse:
privat int MaxNameLength = 10; privat StringBuilder playerName; privat bool backspacepossible; privat bool startpress;De
MaxNameLength
bør settes på samme lengde som du brukte for dinVARCHAR
lengde når du lagde bordet ditt. Her har vi vår strengbygger,spillernavn
, og tobooleans
. Den første,backspacepossible
, er å kontrollere brukerens evne til å holde nede bakrommet for å slette tegn. Det andre er å angi om de har begynt å skrive navnet sitt enda.I
Start()
, vi må ta vare på noen få ting. Vi deaktiverer all tekst bortsett fra den som heterTopp tekst
; vi kan gjøre det i enfor hver
sløyfe, som før.void Start () foreach (GUIText tekst i FindObjectsOfType (typeof (GUIText)) som GUIText []) if (text.name! = "Toptext") text.guiText.enabled = false; GetComponent() .enabled = false; playerNameTemp = new StringBuilder (); playerNameTemp.Append ( "_"); backspacepossible = true; initialpress = false; Her kan du se at vi har gjort noen ting. Vi har deaktivert vår første klasse (
ClickTimes
) siden vi ikke bruker det lenger. Vi har også opprettet en forekomst avplayerNameTemp
og vedlagt den med_
, så spillerne kan se hvor navnet deres går, og vi har initialisert våre variabler.Nå må vi la spilleren faktisk skrive inn navnet sitt. Ved slutten av
Oppdater()
vi plasserer følgende utdrag:guiText.text = playerNameTemp.ToString ()Dette vil sørge for at teksten viser hva vår strengbygger registrerer.
Deretter håndterer vi tegninngang:
hvis (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);Så, forutsatt at strengbyggerens lengde er mindre enn den maksimale navnlengden, og så lenge brukeren skriver inn tegn som er enten bokstaver, siffer, mellomrom eller understreker (selv om du kanskje bare velger å tillate alfanumeriske tegn), vil strengen være vedlagt det nye sifferet. I tilfelle at dette er det første trykket, vil den opprinnelige underskriften fjernes før det nye brevet er lagt til.
Neste:
hvis (playerNameTemp.Length> 0) if (Input.GetKeyDown (KeyCode.Backspace)) hvis (! startpress) initialpress = true; backspacepossible = false; StartCoroutine (BackspaceInitialHold ()); playerNameTemp.Remove (playerNameTemp.Length - 1, 1); annet hvis (backspacepossible && Input.GetKey (KeyCode.Backspace)) backspacepossible = false; StartCoroutine (BackspaceConstantHold ()); playerNameTemp.Remove (playerNameTemp.Length - 1, 1);Så lenge det ikke er noen tegn igjen i vår strengerbygger, og det er mulig å bruke backspace, kan brukeren fjerne tegn. Legg merke til forskjellen mellom første og andre setninger. Den tidligere bruker
GetKeyDown ()
, mens sistnevnte brukerGetkey ()
(og sjekker vår bool). Sondringen er at vi bør slette et tegn hver gang brukeren trykker på backspace, men ikke konstant mens brukeren holder den nede.The coroutines
BackspaceInitialHold ()
og()
bare vent0,15
og0.05
sekunder, henholdsvis, og sett deretterbackspacepossible
tilekte
. Så etter at brukeren har holdt nede på backspace for0,15
sekunder, så lenge de fortsatt holder tilbake plass, slettes et tegn hver gang0.05
sekunder (så lenge somlengde
er større enn kode> 0).Vi fastsetter også at hvis dette er den første knappen, trykker brukeren,
initialpress
utløses (så det vil ikke prøve å fjerne et tegn når de trykker på noe annet).På toppen av alt, må vi tillate brukeren å trykke Komme tilbake for å fullføre navnetinngangen.
hvis (playerNameTemp.Length> 0 && initialpress) if (Input.GetKeyDown (KeyCode.Return)) foreach (GUIText-tekst i FindObjectsOfType (typeof (GUIText)) som GUIText []) text.guiText.enabled = false; GetComponent() .SetName (guiText.text); GetComponent () .enabled = true; aktivert = false; Så lenge brukeren har gjort noen form for inngang og
lengde
er større enn0
, navnet vil bli akseptert Alle våre tekstobjekter blir slettet, vi deaktiverer denne klassen, og vi slår på vår tredje klasse,Høy poengsum
. Alle tre klassene må settes på vårt objekt i redaktøren.Vi ringte nettopp
Høy poengsum
'sSetName ()
funksjon, og tidligere vi ringteSetScore ()
. Hver av disse funksjonene setter bare verdiene av private variabler som vi sender inn nå til vårt rangliste.
Få tilgang til ditt leaderboard i enhet
På toppen av
Høy poengsum
Vi ønsker å erklære noen variabler. Først:offentlig GameObject BaseGUIText;Dette er
GUIText
prefab at vi baserer vårt toppliste på. Du bør sørge for at teksten er forankret til midten til venstre, og venstrejustert. Du kan også velge en skrift her også.privat streng privateKey = "NØKKEN DU GENERERTE FØR"; privat streng AddScoreURL = "http://yoursite.com/AddScore.php?"; privat streng TopScoresURL = "http://yoursite.com/TopScores.php"; privat streng RankURL = "http://yoursite.com/GrabRank.php?"; privat int highscore; privat streng brukernavn; privat int rangering;Vi trenger alle våre verdier fra før - nøkkelen du genererte, nettadressene du lastet opp dine PHP-filer til, og så videre. De
høy poengsum
ogbrukernavn
variabler er satt ved hjelp av to offentlige funksjoner kaltSetScore ()
ogSetName ()
, som vi brukte i forrige seksjon.Tips: Det er veldig viktig at du legger spørsmålstegn etter
AddScore.php
ogGrabRank.php
! Disse lar deg passere variabler til PHP-filene dine.Vi må bruke
IEnumerators
her for å håndtere våre SQL-spørringer, fordi vi må vente på svar. Vi starter vår første coroutine,AddScore ()
, så snart klassen er aktivert.IEnumerator AddScore (strengnavn, int score) string hash = Md5Sum (navn + score + privateKey); WWW ScorePost = nytt WWW (AddScoreURL + "name =" + WWW.EscapeURL (navn) + "& score =" + score + "& hash =" + hash); yield return ScorePost; hvis (ScorePost.error == null) StartCoroutine (GrabRank (navn)); ellers Feil ();Først lager vi vår hash med den private nøkkelen, ved hjelp av en funksjon for å lage en MD5 hash på samme måte som PHP
MD5 ()
. Det er et eksempel på dette på Unitys community wiki.Her, hvis serveren er utilgjengelig av en eller annen grunn, kjører vi en
Feil()
funksjon. Du kan velge hva du vil oppstå i feilsøkeren din. Hvis poengsummen blir riktig lagt opp, lanserer vi vår neste koroutin:GrabRank ()
.IEnumerator GrabRank (strengnavn) WWW RankGrabAttempt = ny WWW (RankURL + "name =" + WWW.EscapeURL (navn)); avkastning RankGrabAttempt; hvis (RankGrabAttempt.error == null) rank = System.Int32.Parse (RankGrabAttempt.text); StartCoroutine (GetTopScores ()); ellers Feil ();Igjen, vi får tilgang til nettstedet, og denne gangen lagrer vi rangen hvis den er vellykket tatt.
Nå kan vi bruke vår siste coroutine. Denne vil knytte alt sammen. Vi starter med å få tilgang til nettadressen for en sluttid:
IEnumerator GetTopScores () WWW GetScoresAttempt = nytt WWW (TopScoresURL); yield return GetScoresAttempt; hvis (GetScoresAttempt.error! = null) Feil (); annetMen denne gangen vil vi dele opp dataene vi mottar i en rekke strenger. Først av alt kan vi bruke en strengspalt som:
streng [] textlist = GetScoresAttempt.text.Split (ny streng [] "\ n", "\ t", System.StringSplitOptions.RemoveEmptyEntries);Dette sørger for at hvert resultat blir et nytt element i strengen vår, så lenge en ny linje eller en fane er funnet (som den vil være!). Vi kommer nå til å splitte dette inn i to ytterligere arrays som heter
navnene
ogPoeng
.streng [] Navn = ny streng [Mathf.FloorToInt (textlist.Length / 2)]; streng [] Scores = ny streng [Names.Length]; for (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];Vi har nå laget to nye arrays som hver halvdel av størrelsen på den første gruppen. Vi deler deretter hver første streng i vår
navnene
array og hver andre i vårPoeng
matrise.Nå vil vi vise disse som tekst, så vi må posisjonere våre tre kolonner. Vi skal skalere dem på skjermen, slik at de passer til enhver oppløsning. Først vil vi erklære de opprinnelige posisjonene der vår titteltekst vil gå:
Vector2 LeftTextPosition = ny Vector2 (0.22f, 0.85f); Vector2 RightTextPosition = ny Vector2 (0.76f, 0.85f); Vector2 CentreTextPosition = ny Vector2 (0.33f, 0.85f);Og nå er vi klare til å lage våre tekstobjekter, basert på vår
BaseGUIText
ferdighus. Vi ordner titlene individuelt og setter inn tekst - for eksempel:GameObject Scoresheader = Instantiate (BaseGUIText, ny Vector2 (0.5f, 0.94f), Quaternion.identity) som GameObject; Scoresheader.guiText.text = "High Scores"; Scoresheader.guiText.anchor = TextAnchor.MiddleCenter; Scoresheader.guiText.fontSize = 35;Når vi har gjort dette for alle våre titler, justerer vi våre stillinger slik at den nye teksten vises lavere ned.
LeftTextPosition - = ny Vector2 (0, 0,062f); RightTextPosition - = ny Vector2 (0, 0,062f); CentreTextPosition - = ny Vector2 (0, 0,062f);Neste kjører vi a
til
sløyfe som vil deterere gjennom hele vår topp 10 liste, sette navn, rang og score (og sørg for at teksten er forankret fornuftig), og deretter justere stillingene enda en gang. Hver iterasjon, vi kontrollerer om brukerens rang er lik rangeringen som vises, og i så fall rekryterer vi teksten slik at brukerens poengsum er uthevet i gult:for (int i = 0; iOg så til slutt sjekker vi om brukerens rang er over 10. Hvis det er, legger vi inn poengsummen deres nederst i ellevte spor, sammen med deres rang, og farger den gult.
hvis (rang> 10) GameObject Score = Instantiate (BaseGUIText, RightTextPosition, Quaternion.identity) som GameObject; Score.guiText.text = "" + highscore; Score.guiText.anchor = TextAnchor.MiddleCenter; GameObject Name = Instantiate (BaseGUIText, CentreTextPosition, Quaternion.identity) som GameObject; Name.guiText.text = brukernavn; GameObject Rank = Instantiate (BaseGUIText, LeftTextPosition, Quaternion.identity) som 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;
Konklusjon
voilà; Vårt leaderboard er fullført! I kildefilene har jeg også inkludert en PHP-fil som vil ta tak i rekkene over og under brukerens brukernavn, slik at du kan vise dem nøyaktig hvor de er på tavlen.