În acest articol, vom crea primul nostru leaderboard MySQL pentru a găzdui pe un site web sau pe un server web folosind PHP simplu și unele SQL. Apoi vom face un simplu exemplu de Unity în C # folosind GUIText
obiecte pentru a adăuga noi scoruri în clasamentul nostru, afișați primele zece scoruri și afișați scorul și rangul utilizatorului.
Jocurile cu un singur jucător sunt distractive, dar baterea propriului clasament poate deveni plictisitoare. Adăugarea unui tabel în joc vă oferă o motivație reală pentru jucătorii dvs. de a-și îmbunătăți scorurile și de a-și juca mai mult jocul și poate fi chiar folosit pentru a afla dacă jocul dvs. este prea ușor sau greu. În jocurile care continuă pentru totdeauna, clasamentele pot fi singurul motiv pentru care jucătorii dvs. joacă. Dacă aveți propriul site web sau server, ați putea dori să găzduiți propriul leaderboard, astfel încât să aveți un control complet asupra jocului dvs..
Mai întâi de toate, va trebui să aveți o bază de date SQL pe serverul sau site-ul dvs. Site-urile Web vin adesea cu o bază de date bazată pe MySQL. Detaliile despre acest lucru vor varia în funcție de serviciul pe care îl utilizați, dar ar trebui să găsiți gazda dvs. SQL, numele de utilizator și parola (precum și numele bazei dvs. de date) din panoul de admin sau din e-mailul de înregistrare.
În acest exemplu, phpMyAdmin este folosit pentru a accesa baza de date (construită chiar în panoul admin). Veți dori să vă deschideți baza de date și să deschideți fila SQL. Dacă aveți mai mult control asupra serverului dvs., puteți crea o bază de date nouă.
Apoi, introduceți următorul SQL:
CREATE TABLE Scoruri (nume VARCHAR (10) NOT NULL DEFAULT "Anonymous" KEY PRIMARY, scor INT (5) UNSIGNED NOT NULL DEFAULT '0', ts TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP) ENGINE = InnoDB;
Aceasta va crea un tabel cu trei variabile:
Nume
, care deține numele utilizatorilor dvs. și care vor stoca 10 caractere. Acesta este principalul identificator al tabelului nostru, astfel încât acesta poate să stocheze doar un rând pe nume de utilizator.scor
, care deține cel mai mare scor al fiecărui utilizator. În acest exemplu este o variabilă nesemnată, deci poate fi doar pozitivă. Dacă doriți să obțineți scoruri negative, va trebui să modificați acest lucru.ts
, un marcaj de timp pe care îl putem folosi pentru a schimba ordinea clasamentului nostru.Acum, dacă utilizați SQL Server și nu MySQL, puteți utiliza în continuare TIMESTAMP-UL
, ci pentru valoarea pe care o veți folosi GetDate ()
in loc de CURRENT_TIMESTAMP
.
Un lucru suplimentar pe care trebuie să-l rețineți: dacă faceți un joc foarte simplu, este posibil să nu doriți să legați scorurile de nume (pentru a permite fiecărui jucător să aibă mai multe scoruri în Top 10, de exemplu). Aceasta poate fi o idee proastă; ați putea avea un jucător care este atât de bun încât să poată domina întreaga ta Top 10! În acest caz nu veți dori Nume
ca cheie primară și veți dori să adăugați și acest lucru:
id INT (8) AUTO_INCREMENT PRIMARY KEY
Acest lucru va face ca un rând nou să fie adăugat pentru fiecare scor.
Clic Merge și ați terminat! Masa ta e gata acum.
Acum trebuie să creați câteva fișiere PHP. Aceștia sunt oamenii de mijloc ai operațiunii, oferind un mod pentru unitatea de a accesa serverul. Primul fișier PHP de care aveți nevoie este AddScore.php
. Va trebui să cunoașteți mai întâi informațiile despre server.
(A inlocui
SQLHOST
,SQLUSER
,SQLPASSWORD
șiYOURDATABASE
cu propriile informații.)Aici am încercat să ne conectăm la baza de date. Dacă conexiunea eșuează, Unity va fi informată că cererea a eșuat. Acum, veți dori să transmiteți câteva informații serverului:
$ username = mysql_real_escape_string ($ _GET ['nume'], $ db); $ scor = mysql_real_escape_string ($ _GET ['scor'], $ db); $ hash = $ _GET ['hash']; $ PrivateKey = "ADDYOURKEY";Hash-ul este folosit pentru a cripta datele dvs. și pentru a opri oamenii de la hacking leaderboard-ul dvs. Se generează cu o cheie ascunsă în Unity și aici, iar dacă cele două hash-uri se potrivesc, vi se permite accesul la baza dvs. de date.
$ expected_hash = md5 ($ nume utilizator, scor $, $ privateKey); dacă ($ expected_hash == $ hash)Aici generăm hash-ul și verificăm că hash-ul pe care îl trimitem de la Unity este identic cu hash-ul pe care îl așteptăm. Dacă este, putem trimite întrebarea noastră!
$ query = "INSERTE IN SCOARTE SET nume = '$ name', scor = 'scor $', ts = CURRENT_TIMESTAMPAceasta este prima jumătate a interogării SQL. Ar trebui să fie destul de explicativ; scorul și numele de utilizator trimise sunt adăugate în tabel, iar timestampul este actualizat. A doua jumătate este mai complicată:
DUPLICATE KEY UPDATE ts = dacă (scorul "scor", CURRENT_TIMESTAMP, ts), scorul = dacă (scorul "scor", "scorul $", scorul); ";Mai întâi verificăm dacă numele de utilizator (cheia noastră primară) are deja un rând. În caz contrar, în loc de a introduce o nouă înregistrare, intrarea noastră va fi actualizată. Vrem să actualizăm marcajul de timp și scorul, dar numai dacă noul scor este mai mare!
Utilizarea
dacă
declarații, ne asigurăm că noile valori sunt utilizate numai dacă scorul nou este mai mare decât scorul curent, altfel valorile inițiale sunt utilizate.$ result = mysql_query ($ query) sau mor ('Query failed:' mysql_error ()); ?>În cele din urmă, rulați interogarea noastră și închideți PHP-ul nostru.
Acest fișier merge pe serverul nostru. Va trebui să vă amintiți adresa URL. De asemenea, trebuie să facem încă două fișiere PHP cu interogări diferite, pe care le vom apela
TopScores.php
șiGetRank.php
.
Scoruri de top
interogarea este pur și simplu:SELECTAREA * DIN SCRISOARE ORDER prin scor DESC, ASC LIMIT 10Aceasta va lua primele 10 valori bazate pe scor, iar pentru rândurile legate, jucătorii care au obținut scorul vor fi plasați mai întâi în masă. De data aceasta vrem să extragem și datele, deci adăugăm și:
$ result_length = mysql_num_rows ($ rezultat); pentru ($ i = 0; $ i < $result_length; $i++) $row = mysql_fetch_array($result); echo $row['name'] . "\t" . $row['score'] . "\n";Aceasta ne va extrage rezultatele și le vom tabula în așa fel încât să le putem pune în tablouri în Unitate.
În cele din urmă, avem
GrabRank
:SELECT uo. *, (SELECT COUNT (*) FROM SCULE ui WHERE (ui.score, -ui.ts)> = (uo.score, -uo.ts)) AS rank FROM SCULE uo WHERE nume = '$ name' ;Acest lucru ne va da rangul jucătorului nostru în tabloul de bord. Putem extrage apoi prin ecou
$ Rândul [ 'rang']
.Codul sursă include, de asemenea, o funcție de dezinfectare, care va împiedica utilizatorii să introducă cuvinte înjurătoare în clasamentul dvs. sau să încerce un atac de injecție SQL.
Efectuarea unui minigame simplă în unitate
Acum avem nevoie de un joc pentru a folosi bordul nostru cu scoruri mari! Vom încerca doar câte clicuri să poată face fiecare utilizator în zece secunde, dar puteți adăuga clasamentul în orice joc.
schemă
Vom începe prin a face patru
GUIText
obiecte. Acestea ar trebui ancorate în centrul centrului pentru confort. Aveți posibilitatea să le reglați cu decalajul pixelilor pentru a le obține în locul potrivit, dar dacă doriți ca acestea să își ajusteze poziția pentru orice rezoluție, este mai simplu să schimbațiX
șiY
poziție (între0
și1
); în caz contrar, va trebui să le ajustați la pornire.Cu toate acestea, va trebui să ajustați dimensiunea fontului la pornire dacă doriți să rulați la toate rezoluțiile. O modalitate rapidă de a face acest lucru este bazându-le pe înălțimea ecranului. Putem face acest lucru făcând o clasă care face acest lucru și atașând-o la toate obiectele noastre text, dar este mult mai simplu să faceți asta dintr-o clasă.
Nu contează exact ce obiect alegem ca "manager", așa că putem pune această clasă pe contorul nostru de clicuri. Deci, în prima noastră clasă, scriem:
void Start () foreach (GUIText chosentext în FindObjectsOfType (typeof (GUIText)) ca GUIText []) chosentext.blah.fontSize = Mathf.FloorToInt (Screen.height * 0.08f);Acest lucru va găsi fiecare obiect text în scenă și îl va scala la o dimensiune sensibilă.
Acum vrem ca contorul de clic să fie mai mare decât celălalt text, așa că, dacă păstrăm această clasă acolo, avem bonusul suplimentar pe care îl putem verifica, de asemenea, dacă
guiText
în discuție este cea atașată acestui lucruGameObject
:dacă (blah == guiText) blah.fontSize = Mathf.FloorToInt (Screen.height * 0.18f); altceva [etc]Modul de joc
Componenta click a jocului va fi foarte simpla. La început, nu vrem ca cronometrul să contorizeze până la primul clic, așa că vom face două persoane private
bools
in clasa noastra -firstClick
șiallowedToClick
. ÎnStart()
putem stabilifirstClick
lafals
șiallowedToClick
laAdevărat
.Acum avem nevoie de contor pentru a înregistra efectiv clicurile și există câteva moduri de a face acest lucru. Am putea păstra o variabilă întregă care să urmărească scorul, sau am putea să o facem puțin mai puțin eficientă, dar într-o singură linie (și cu ceva atât de simplu încât nu avem nevoie să optimizăm cu adevărat, ci este o practică bună). Deci, vom înregistra clicul în
Actualizați()
funcția și crește valoarea prin citirea șirului.void Actualizare () if (allowedToClick && Input.GetMouseButtonUp (0)) if (! firstClick) firstClick = true; StartCoroutine (numărătoare inversă ()); guiText.text = (System.Int32.Parse (guiText.text) + 1) .ToString ();După cum puteți vedea aici, incrementarea este realizată prin citirea șirului ca un număr întreg, adăugând unul și apoi convertirea înapoi la un șir. De asemenea, veți vedea aici că am executat o corutină de îndată ce utilizatorul a dat clic pentru prima dată, care începe numărătoarea inversă.
Vom folosi recurența în această funcție. Din nou, am putea folosi un întreg care deține valoarea inversă pentru eficiență, dar vom folosi din nou manipularea șirului.
IEnumerator Countdown () randament randament nou WaitForSeconds (1); counter.guiText.text = (System.Int32.Parse (contra.guiText.text) - 1) .ToString (); dacă (counter.guiText.text! = "0") StartCoroutine (Countdown ()); altceva allowedToClick = false; GetComponent() .Setscore (System.Int32.Parse (guiText.text)); toptext.guiText.text = "Introduceți numele de utilizator."; GetComponent () .enabled = true; Notă: este important să le folosim
StartCoroutine ()
și nu a numit doar această funcție, pentru că este un lucruIEnumerator
.Randament
declarația o face să aștepte o secundă înainte de a se întreprinde orice acțiune. Elimină unul de pe tejghea și dacă valoarea nu este zero, se recheamă din nou. În acest fel, funcția va conta în jos până când ajunge0
.Nume de intrare
După aceasta, oprește utilizatorul să facă clic, să solicite numele de utilizator și să acceseze clasele a doua și a treia (pe care urmează să le scriem!). Vom arunca o privire la ceea ce fac acum, începând cu
NameEnter
.În
NameEnter ()
vom permite unui utilizator să introducă numele de utilizator, cu anumite constrângeri. Inițial dorim să afișăm caracterul de subliniere_
, care vor fi șterse de îndată ce vor începe să-și scrie numele. În plus, nu vrem să fie capabili să folosească caractere asemănătoare\
sau'
, deoarece acestea ar dezordine interogările SQL.Vom folosi un constructor de șir pentru a crea acest lucru. Mai întâi vom plasa câteva variabile în partea de sus a clasei noastre:
int int MaxNameLength = 10; private StringBuilder playerName; private bool backspacepossible; private bool initialpress;
MaxNameLength
ar trebui să fie setată la aceeași lungime ca cea pe care o utilizați pentru dvs.VARCHAR
când ai făcut masa. Aici avem constructorul nostru de șir,numele jucatorului
, si doibooleane
. Primul,backspacepossible
, este de a controla abilitatea utilizatorului de a ține apăsat backspace pentru a șterge caracterele. Al doilea este de a indica dacă au început să-și scrie numele încă.În
Start()
, trebuie să avem grijă de câteva lucruri. Vom dezactiva tot textul, cu excepția celui numitTextul de sus
; putem face asta într-unpentru fiecare
cum ar fi înainte.void Start () foreach (textul GUIText în FindObjectsOfType (typeof (GUIText)) ca GUIText []) if (text.name! = "Toptext") text.guiText.enabled = false; GetComponent() .enabled = false; playerNameTemp = nou StringBuilder (); playerNameTemp.Append ( "_"); backspacepossible = true; initialpress = false; Aici puteți vedea că am făcut câteva lucruri. Am dezactivat clasa noastră inițială (
ClickTimes
), deoarece nu o mai folosim. Am creat, de asemenea, o instanță deplayerNameTemp
și l-au atașat_
, astfel încât jucătorii pot vedea unde se află numele lor și ne-am inițializat variabilele.Acum trebuie să permitem jucătorului să își introducă efectiv numele. La sfârșitul
Actualizați()
plasăm următorul fragment:guiText.text = playerNameTemp.ToString ()Acest lucru va face ca textul să afișeze ceea ce înregistrează constructorul nostru de caractere.
Apoi ne ocupăm de introducerea caracterelor:
dacă (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);Deci, cu condiția ca lungimea constructorului de coarde să fie mai mică decât lungimea maximă a numelui și atâta timp cât utilizatorul introduce caractere care sunt fie litere, cifre, spații sau subliniere (deși ați putea alege doar să permiteți caractere alfanumerice), șirul va fi anexat cu noua cifră. În cazul în care aceasta este prima apăsare, sublinierea originală va fi eliminată înainte de adăugarea literei noi.
Următor →:
dacă playerNameTemp.Length> 0) dacă (Input.GetKeyDown (KeyCode.Backspace)) if (! inipress) initialpress = true; backspacepossible = false; StartCoroutine (BackspaceInitialHold ()); playerNameTemp.Remove (playerNameTemp.Length - 1, 1); altfel dacă (backspacepossible && Input.GetKey (KeyCode.Backspace)) backspacepossible = false; StartCoroutine (BackspaceConstantHold ()); playerNameTemp.Remove (playerNameTemp.Length - 1, 1);Atâta timp cât nu există caractere rămase în constructorul nostru de caractere și spate este posibil, utilizatorul poate elimina caracterele. Observați diferența dintre prima și a doua declarație. Primul foloseste
GetKeyDown ()
, în timp ce acesta din urmă foloseșteGetKey ()
(și verifică boolul nostru). Distincția este că ar trebui să ștergem un caracter de fiecare dată când utilizatorul apasă backspace, dar nu în mod constant în timp ce utilizatorul îl ține jos.Corutinele
BackspaceInitialHold ()
și()
așteptați pur și simplu0,15
și0,05
secunde, respectiv, apoi setațibackspacepossible
laAdevărat
. Deci, după ce utilizatorul nostru a ținut înapoi backspace pentru0,15
secunde, atâta timp cât încă mai țin înapoi, un personaj va fi șters fiecare0,05
secunde (atâta timp câtlungime
este mai mare decât codul> 0).De asemenea, stipulăm că dacă acesta este primul buton pe care îl apasă utilizatorul,
initialpress
este declanșată (deci nu va încerca să elimine un caracter odată ce apasă altceva).Pentru a elimina totul, trebuie să permitem utilizatorului să apese Întoarcere pentru a finaliza introducerea numelui.
dacă playerNameTemp.Length> 0 && initialpress) if (Input.GetKeyDown (KeyCode.Return)) foreach (textul GUIText în FindObjectsOfType (typeof (GUIText)) ca GUIText []) text.guiText.enabled = false; GetComponent() .SetName (guiText.text); GetComponent () .enabled = true; enabled = false; Atâta timp cât utilizatorul a făcut un fel de intrare și
lungime
este mai mare decât0
, numele va fi acceptat. Toate obiectele noastre de text se șterg, dezactivam această clasă și pornim clasa a treia,Cel mai mare scor
. Toate cele trei clase trebuie să fie puse pe obiectul nostru în editor.Tocmai am sunat
Cel mai mare scor
„sPune un nume()
funcție, iar mai devreme am sunatSetScore ()
. Fiecare dintre aceste funcții stabilește pur și simplu valorile variabilelor private pe care le vom trimite acum în clasamentul nostru.
Accesarea tabloului dvs. de bord în unitate
În partea superioară a
Cel mai mare scor
vrem să declarăm câteva variabile. Primul:public GameObject BaseGUIText;Acesta este
GUIText
prefab ca ne vom baza tabela de clasament. Ar trebui să vă asigurați că textul este ancorat la stânga din mijloc și aliniat la stânga. De asemenea, puteți alege un font și aici.string privateKey = "KEY-ul pe care l-ați generat înainte"; string-ul privat AddScoreURL = "http://yoursite.com/AddScore.php?"; coardă privată TopScoresURL = "http://yoursite.com/TopScores.php"; string propriu RankURL = "http://yoursite.com/GrabRank.php?"; private int highscore; nume de utilizator cu șir privat; grad personal privat;Avem nevoie de toate valorile noastre de acum înainte - cheia pe care ați generat-o, adresele URL pe care le-ați încărcat fișierele PHP și așa mai departe.
cel mai mare scor
șinume de utilizator
variabilele sunt setate utilizând două funcții publice numiteSetScore ()
șiPune un nume()
, pe care am folosit-o în secțiunea anterioară.Bacsis: Este foarte important să puneți semne de întrebare după
AddScore.php
șiGrabRank.php
! Acestea vă permit să transmiteți variabile fișierelor dvs. PHP.Trebuie să folosim
IEnumerators
aici pentru a rezolva interogările SQL, pentru că trebuie să așteptăm un răspuns. Vom începe prima noastră corutină,AddScore ()
, de îndată ce clasa este activată.IEnumerator AddScore (numele șirului, scorul int) string hash = Md5Sum (nume + scor + privateKey); WWW ScorePost = noul WWW (AddScoreURL + "nume =" + WWW.EscapeURL (nume) + "& score =" + scor + "& hash =" + hash); randamentul randamentului ScorePost; dacă (ScorePost.error == null) StartCoroutine (GrabRank (nume)); altceva Error ();Mai întâi, creăm hash-ul cu cheia privată, folosind o funcție pentru a crea un hash MD5 la fel ca PHP
md5 ()
. Există un exemplu de acest lucru pe wiki-ul comunității Unity.Aici, dacă serverul este inaccesibil din orice motiv, vom rula un
Eroare()
funcţie. Puteți alege ce doriți să se întâmple în instrumentul de eroare. Dacă scorul este postat corect, cu toate acestea, vom lansa următoarea noastră corutină:GrabRank ()
.IEnumerator GrabRank (nume șir) WWW RankGrabAttempt = nou WWW (RankURL + "nume =" + WWW.EscapeURL (nume)); randament randament RankGrabAttempt; dacă (RankGrabAttempt.error == null) rank = System.Int32.Parse (RankGrabAttempt.text); StartCoroutine (GetTopScores ()); altceva Error ();Din nou, accesăm site-ul, iar de data aceasta păstrăm rangul dacă este reușit.
Acum putem folosi ultima noastră corutină. Aceasta va lega totul. Începem accesând adresa URL pentru ultima oară:
IEnumerator GetTopScores () WWW GetScoresAttempt = nou WWW (TopScoresURL); randamentul randamentului GetScoresAttempt; dacă (GetScoresAttempt.error! = null) Eroare (); altcevaDar de data aceasta vrem să împărțim datele pe care le primim într-o serie de șiruri de caractere. Mai întâi de toate putem folosi un șir de fire ca:
șir [] textlist = GetScoresAttempt.text.Split (nou șir [] "\ n", "\ t", System.StringSplitOptions.RemoveEmptyEntries);Acest lucru va face ca fiecare rezultat să fie un element nou în matricea noastră de șir, atât timp cât se găsește o nouă linie sau o filă (care va fi!). Acum o vom împărți în două alte rețele numite
Nume
șiScoruri
.șir [] Denumiri = șir nou [Mathf.FloorToInt (textlist.Length / 2)]; string [] Scoruri = șir nou [Nume.Lungime]; pentru (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];Am realizat acum două noi tablouri care sunt fiecare la jumătate din dimensiunea primei matrice. Apoi am împărțit fiecare prim șir în nostru
Nume
și fiecare al doilea în noiScoruri
mulțime.Acum vrem să le arătăm ca text, așa că trebuie să ne poziționăm cele trei coloane. Le vom scala pe ecran, așa că se potrivesc cu orice rezoluție. În primul rând, vom declara pozițiile inițiale în care va apărea textul titlului:
Vector2 LeftTextPosition = vector2 nou (0.22f, 0.85f); Vector2 RightTextPosition = vector2 nou (0.76f, 0.85f); Vector2 CenterTextPosition = vector2 nou (0.33f, 0.85f);Și acum suntem gata să creăm obiectele noastre text, pe baza noastră
BaseGUIText
prefabricat. Inițializăm titlul individual și setăm textul - de exemplu:GameObject Scoresheader = Instantiate (BaseGUIText, nou Vector2 (0.5f, 0.94f), Quaternion.identity) ca GameObject; Scoresheader.guiText.text = "Scoruri ridicate"; Scoresheader.guiText.anchor = TextAnchor.MiddleCenter; Scoresheader.guiText.fontSize = 35;După ce am făcut acest lucru pentru toate titlurile, ne ajustăm pozițiile astfel încât noul text să fie afișat mai jos.
LeftTextPosition - = Vector2 nou (0, 0.062f); RightTextPosition - = Vector2 nou (0, 0.062f); CenterTextPosition - = Vector2 nou (0, 0.062f);Apoi conducem a
pentru
care va itera prin întreaga noastră listă de top 10, stabilind numele, rangul și scorul (și asigurându-vă că textul este ancorat în mod sensibil) și apoi ajustând pozițiile din nou. Fiecare iterație verifică dacă rangul utilizatorului este egal cu rangul afișat și, dacă este așa, reambalăm textul astfel încât scorul utilizatorului să fie evidențiat în galben:pentru (int i = 0; iȘi apoi, în cele din urmă, verificăm dacă rangul utilizatorului este mai mare de 10. Dacă este, vom posta scorul lor în partea de jos în slotul al unsprezecelea, împreună cu rangul lor, iar culoarea este galben.
dacă (rang> 10) GameObject Score = Instanțiate (BaseGUIText, RightTextPosition, Quaternion.identity) ca GameObject; Score.guiText.text = "" + scoruri mari; Score.guiText.anchor = TextAnchor.MiddleCenter; GameObject Name = Instantiate (BaseGUIText, CenterTextPosition, Quaternion.identity) ca GameObject; Nume.guiText.text = nume de utilizator; GameObject Rank = Instanțiate (BaseGUIText, LeftTextPosition, Quaternion.identity) ca 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;
Concluzie
Voilà; clasamentul nostru este complet! În fișierele sursă am inclus, de asemenea, un fișier PHP care va apuca rândurile deasupra și sub numele de utilizator al utilizatorului, astfel încât să le puteți arăta exact unde se află pe tablă.