Unity Online Leaderboard-opplæring
I denne opplæringen skal jeg vise hvordan du implementerer en online leaderboard i spillet ditt i Unity.
Dette er en fortsettelse av en tidligere opplæring: Unity Innloggingssystem med PHP og MySQL.
Å ha en ledertavle er en fin måte å øke gjenspillbarheten ved å legge til et nivå av konkurranseevne til spillet ditt.
Samme som før, denne opplæringen krever en server med cPanel sammen med PHP og MySQLi (en forbedret versjon av MySQL).
Sjekk gjerne den rimelige premium VPS hostingen eller et billigere Shared Hosting alternativ.
Så la oss fortsette!
Gjøre en endring i det eksisterende skriptet
Hvis du fulgte opplæringen ovenfor, ville du nå ha et skript kalt 'SC_LoginSystem'. Vi skal implementere ledertavlefunksjonen ved å legge til litt kode til den.
- Åpne 'SC_LoginSystem'-skriptet
Først begynner vi med å legge til de nødvendige variablene:
//Leaderboard
Vector2 leaderboardScroll = Vector2.zero;
bool showLeaderboard = false;
int currentScore = 0; //It's recommended to obfuscate this value to protect against hacking (search 'obfuscation' on sharpcoderblog.com to learn how to do it)
int previousScore = 0;
float submitTimer; //Delay score submission for optimization purposes
bool submittingScore = false;
int highestScore = 0;
int playerRank = -1;
[System.Serializable]
public class LeaderboardUser
{
public string username;
public int score;
}
LeaderboardUser[] leaderboardUsers;
MERK: Variabelen currentScore er det du skal bruke i spillet for å spore spillers poeng. Denne verdien vil bli sendt til serveren og lagret i databasen. Det anbefales å obfuscate den verdien for å beskytte mot hacking.
Deretter legger vi til 2 Enumeratorer som vil være ansvarlige for å sende inn poeng og hente resultattavlen. Legg til koden nedenfor på slutten av skriptet før den siste parentesen lukkes:
//Leaderboard
IEnumerator SubmitScore(int score_value)
{
submittingScore = true;
print("Submitting Score...");
WWWForm form = new WWWForm();
form.AddField("email", userEmail);
form.AddField("username", userName);
form.AddField("score", score_value);
using (UnityWebRequest www = UnityWebRequest.Post(rootURL + "score_submit.php", form))
{
yield return www.SendWebRequest();
if (www.isNetworkError)
{
print(www.error);
}
else
{
string responseText = www.downloadHandler.text;
if (responseText.StartsWith("Success"))
{
print("New Score Submitted!");
}
else
{
print(responseText);
}
}
}
submittingScore = false;
}
IEnumerator GetLeaderboard()
{
isWorking = true;
WWWForm form = new WWWForm();
form.AddField("email", userEmail);
form.AddField("username", userName);
using (UnityWebRequest www = UnityWebRequest.Post(rootURL + "leaderboard.php", form))
{
yield return www.SendWebRequest();
if (www.isNetworkError)
{
print(www.error);
}
else
{
string responseText = www.downloadHandler.text;
if (responseText.StartsWith("User"))
{
string[] dataChunks = responseText.Split('|');
//Retrieve our player score and rank
if (dataChunks[0].Contains(","))
{
string[] tmp = dataChunks[0].Split(',');
highestScore = int.Parse(tmp[1]);
playerRank = int.Parse(tmp[2]);
}
else
{
highestScore = 0;
playerRank = -1;
}
//Retrieve player leaderboard
leaderboardUsers = new LeaderboardUser[dataChunks.Length - 1];
for(int i = 1; i < dataChunks.Length; i++)
{
string[] tmp = dataChunks[i].Split(',');
LeaderboardUser user = new LeaderboardUser();
user.username = tmp[0];
user.score = int.Parse(tmp[1]);
leaderboardUsers[i - 1] = user;
}
}
else
{
print(responseText);
}
}
}
isWorking = false;
}
Neste er ledertavlens brukergrensesnitt. Legg til koden nedenfor etter den ugyldige OnGUI():
//Leaderboard
void LeaderboardWindow(int index)
{
if (isWorking)
{
GUILayout.Label("Loading...");
}
else
{
GUILayout.BeginHorizontal();
GUI.color = Color.green;
GUILayout.Label("Your Rank: " + (playerRank > 0 ? playerRank.ToString() : "Not ranked yet"));
GUILayout.Label("Highest Score: " + highestScore.ToString());
GUI.color = Color.white;
GUILayout.EndHorizontal();
leaderboardScroll = GUILayout.BeginScrollView(leaderboardScroll, false, true);
for (int i = 0; i < leaderboardUsers.Length; i++)
{
GUILayout.BeginHorizontal("box");
if(leaderboardUsers[i].username == userName)
{
GUI.color = Color.green;
}
GUILayout.Label((i + 1).ToString(), GUILayout.Width(30));
GUILayout.Label(leaderboardUsers[i].username, GUILayout.Width(230));
GUILayout.Label(leaderboardUsers[i].score.ToString());
GUI.color = Color.white;
GUILayout.EndHorizontal();
}
GUILayout.EndScrollView();
}
}
Legg til koden nedenfor i den void OnGUI() (før den avsluttende parentesen):
//Leaderboard
if (showLeaderboard)
{
GUI.Window(1, new Rect(Screen.width / 2 - 300, Screen.height / 2 - 225, 600, 450), LeaderboardWindow, "Leaderboard");
}
if (!isLoggedIn)
{
showLeaderboard = false;
currentScore = 0;
}
else
{
GUI.Box(new Rect(Screen.width / 2 - 65, 5, 120, 25), currentScore.ToString());
if (GUI.Button(new Rect(5, 60, 100, 25), "Leaderboard"))
{
showLeaderboard = !showLeaderboard;
if (!isWorking)
{
StartCoroutine(GetLeaderboard());
}
}
}
Og til slutt, void Update(), som vil inneholde en kode som er ansvarlig for å sende inn spillerens poengsum, når den endres. Legg til koden nedenfor i begynnelsen av skriptet etter alle variablene:
//Leaderboard
void Update()
{
if (isLoggedIn)
{
//Submit score if it was changed
if (currentScore != previousScore && !submittingScore)
{
if(submitTimer > 0)
{
submitTimer -= Time.deltaTime;
}
else
{
previousScore = currentScore;
StartCoroutine(SubmitScore(currentScore));
}
}
else
{
submitTimer = 3; //Wait 3 seconds when it's time to submit again
}
//**Testing** Increase score on key press
if (Input.GetKeyDown(KeyCode.Q))
{
currentScore += 5;
}
}
}
Legg merke til **Testing**-delen, siden vi ikke har et spillbart spill, øker vi ganske enkelt poengsummen ved å trykke på Q (du kan fjerne den senere hvis du allerede har et spill med en poengsystem, f.eks. samle en mynt +1 poeng osv.)
Når du trykker på Play og logger på, bør du legge merke til 2 nye elementer: 'Leaderboard'-knappen og poengverdien øverst på skjermen.
Nå går vi for å lage en MySQL-tabell.
Opprette MySQL-tabell
Brukerpoeng vil bli lagret i en egen MySQL-tabell.
- Logg inn på CPanel
- Klikk på "phpMyAdmin" under DATABASER-delen
- Klikk på databasen du opprettet i forrige veiledning og klikk deretter på SQL-fanen
- Lim inn koden nedenfor i søkeredigeringsprogrammet, og klikk deretter "Go"
SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO";
SET AUTOCOMMIT = 0;
START TRANSACTION;
SET time_zone = "+00:00";
--
-- Table structure for table `sc_user_scores`
--
CREATE TABLE `sc_user_scores` (
`row_id` int(11) NOT NULL,
`user_id` int(11) NOT NULL,
`user_score` int(11) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
--
-- Indexes for table `sc_user_scores`
--
ALTER TABLE `sc_user_scores`
ADD PRIMARY KEY (`row_id`),
ADD UNIQUE KEY `user_id` (`user_id`);
--
-- AUTO_INCREMENT for table `sc_user_scores`
--
ALTER TABLE `sc_user_scores`
MODIFY `row_id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=1;
COMMIT;
Spørringen ovenfor vil opprette en ny tabell kalt 'sc_user_scores' som vil lagre de høyeste poengsummene sammen med user_id som en referanse til hovedtabellen 'sc_users'.
Den siste delen er implementering av logikk på serversiden.
Implementering av logikk på serversiden
Server-side logikk vil bestå av PHP-skript som vil være ansvarlig for å motta/lagre poengsummene og hente ledertavlen.
Det første skriptet er score_submit.php.
- Lag et nytt PHP-skript og lim inn koden nedenfor i det:
score_submit.php
<?php
if(isset($_POST["email"]) && isset($_POST["username"]) && isset($_POST["score"])){
$errors = array();
$email = $_POST["email"];
$username = $_POST["username"];
$submitted_score = intval($_POST["score"]);
$user_id = -1;
$current_highscore = -1;
//Connect to database
require dirname(__FILE__) . '/database.php';
//Check if the user already registered, retrieve its user_id and score value (if exist)
if ($stmt = $mysqli_conection->prepare("SELECT u.user_id,
(SELECT user_score FROM sc_user_scores WHERE user_id = u.user_id LIMIT 1) as user_score
FROM sc_users u WHERE u.email = ? AND u.username = ? LIMIT 1")) {
/* bind parameters for markers */
$stmt->bind_param('ss', $email, $username);
/* execute query */
if($stmt->execute()){
/* store result */
$stmt->store_result();
if($stmt->num_rows > 0){
/* bind result variables */
$stmt->bind_result($user_id_tmp, $score_tmp);
/* fetch value */
$stmt->fetch();
$user_id = $user_id_tmp;
$current_highscore = $score_tmp;
}else{
$errors[] = "User not found.";
}
/* close statement */
$stmt->close();
}else{
$errors[] = "Something went wrong, please try again.";
}
}else{
$errors[] = "Something went wrong, please try again.";
}
//Submit new score
if(count($errors) == 0){
if(is_null($current_highscore) || $submitted_score > $current_highscore){
if(is_null($current_highscore)){
//Insert new record
if ($stmt = $mysqli_conection->prepare("INSERT INTO sc_user_scores (user_id, user_score) VALUES(?, ?)")) {
/* bind parameters for markers */
$stmt->bind_param('ii', $user_id, $submitted_score);
/* execute query */
if($stmt->execute()){
/* close statement */
$stmt->close();
}else{
$errors[] = "Something went wrong, please try again.";
}
}else{
$errors[] = "Something went wrong, please try again.";
}
}else{
//Update existing record
if ($stmt = $mysqli_conection->prepare("UPDATE sc_user_scores SET user_score = ? WHERE user_id = ? LIMIT 1")) {
/* bind parameters for markers */
$stmt->bind_param('ii', $submitted_score, $user_id);
/* execute query */
if($stmt->execute()){
/* close statement */
$stmt->close();
}else{
$errors[] = "Something went wrong, please try again.";
}
}else{
$errors[] = "Something went wrong, please try again.";
}
}
}else{
$errors[] = "Submitted score is lower than the current highscore, skipping...";
}
}
if(count($errors) > 0){
echo $errors[0];
}else{
echo "Success";
}
}else{
echo "Missing data";
}
?>
Det siste skriptet er leaderboard.php.
- Lag et nytt PHP-skript og lim inn koden nedenfor i det:
leaderboard.php
<?php
//Retrieve our score along with leaderboard
if(isset($_POST["email"]) && isset($_POST["username"])){
$returnData = array();
$email = $_POST["email"];
$username = $_POST["username"];
//Connect to database
require dirname(__FILE__) . '/database.php';
//Get our score and rank
$returnData[] = "User";
if ($stmt = $mysqli_conection->prepare("SELECT us.user_score,
(SELECT COUNT(row_id) FROM sc_user_scores WHERE user_score >= us.user_score LIMIT 1) as rank
FROM sc_user_scores us
WHERE us.user_id = (SELECT user_id FROM sc_users WHERE email = ? AND username = ? LIMIT 1) LIMIT 1")) {
/* bind parameters for markers */
$stmt->bind_param('ss', $email, $username);
/* execute query */
if($stmt->execute()){
/* store result */
$stmt->store_result();
if($stmt->num_rows > 0){
/* bind result variables */
$stmt->bind_result($score_tmp, $user_rank);
/* fetch value */
$stmt->fetch();
//Append
$returnData[0] .= "," . $score_tmp . "," . $user_rank;
}
/* close statement */
$stmt->close();
}
}
//Get top 100 players
if ($stmt = $mysqli_conection->prepare("SELECT u.username, us.user_score
FROM sc_users u RIGHT JOIN sc_user_scores us ON u.user_id = us.user_id
WHERE u.user_id IS NOT NULL ORDER BY us.user_score DESC LIMIT 100")) {
/* execute query */
if($stmt->execute()){
$result = $stmt->get_result();
while ($row = $result->fetch_assoc())
{
$returnData[] = $row["username"] . "," . $row["user_score"];
}
/* close statement */
$stmt->close();
}
}
//The returned string will use '|' symbol for separation between player data and ',' for separation inside the player data
echo implode('|', $returnData);
}else{
echo "Missing data";
}
?>
- Last opp både score_submit.php og leaderboard.php til samme mappe som du lastet opp PHP-skript fra forrige veiledning.
Når alt er satt opp, når du klikker på Leaderboard, skal det laste inn poengsummen/rangeringen din sammen med de 100 beste spillerne basert på poengsummen deres: