Cum de a codifica un tablou de bord PHP / SQL găzduit automat pentru jocul dvs.

Î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.


Introducere

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..


Crearea tabloului dvs. de bord

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.


Configurarea fișierelor PHP

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 și YOURDATABASE 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_TIMESTAMP

Aceasta 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 și GetRank.php.

Scoruri de top interogarea este pur și simplu:

 SELECTAREA * DIN SCRISOARE ORDER prin scor DESC, ASC LIMIT 10

Aceasta 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ți X și Y poziție (între 0 și 1); î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 lucru GameObject:

 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 și allowedToClick. În Start() putem stabili firstClick la fals și allowedToClick la Adevă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 lucru IEnumerator. 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 ajunge 0.

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 doi booleane. 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 numit Textul de sus; putem face asta într-un pentru 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ță de playerNameTemp ș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ște GetKey () (ș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 simplu 0,15 și 0,05 secunde, respectiv, apoi setați backspacepossible la Adevărat. Deci, după ce utilizatorul nostru a ținut înapoi backspace pentru 0,15 secunde, atâta timp cât încă mai țin înapoi, un personaj va fi șters fiecare 0,05 secunde (atâta timp cât lungime 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ât 0, 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„s Pune un nume() funcție, iar mai devreme am sunat SetScore (). 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 și nume de utilizator variabilele sunt setate utilizând două funcții publice numite SetScore () și Pune un nume(), pe care am folosit-o în secțiunea anterioară.

Bacsis: Este foarte important să puneți semne de întrebare după AddScore.php și GrabRank.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 ();  altceva 

Dar 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 și Scoruri.

 ș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 noi Scoruri 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ă.