Tutorial Papan Peringkat Online Unity

Dalam tutorial ini, saya akan menunjukkan cara mengimplementasikan papan peringkat online di game Anda di Unity.

Ini merupakan kelanjutan dari tutorial sebelumnya: Unity Sistem Login Dengan PHP dan MySQL.

Memiliki papan peringkat adalah cara terbaik untuk meningkatkan replayability dengan menambahkan tingkat daya saing pada game Anda.

Sama seperti sebelumnya, tutorial ini membutuhkan server dengan cPanel bersama dengan PHP dan MySQLi (versi perbaikan dari MySQL).

Jangan ragu untuk memeriksa hosting premium VPS yang terjangkau atau alternatif Shared Hosting yang lebih murah.

Jadi mari kita lanjutkan!

Melakukan modifikasi terhadap script yang sudah ada

Jika Anda mengikuti tutorial di atas, Anda sekarang akan memiliki skrip bernama 'SC_LoginSystem'. Kami akan mengimplementasikan fitur papan peringkat dengan menambahkan beberapa kode ke dalamnya.

  • Buka skrip 'SC_LoginSystem'

Pertama, kita mulai dengan menambahkan variabel yang diperlukan:

    //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;

CATATAN: Variabel currentScore adalah variabel yang akan Anda gunakan dalam game untuk melacak skor pemain. Nilai ini akan dikirimkan ke server dan disimpan dalam database, disarankan untuk mengaburkan nilai tersebut untuk melindungi dari peretasan.

Selanjutnya, kami menambahkan 2 Enumerator yang akan bertanggung jawab untuk mengirimkan skor dan mengambil papan peringkat. Tambahkan kode di bawah ini pada akhir script sebelum penutupan tanda kurung terakhir:

    //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;
    }

Berikutnya adalah UI papan peringkat. Tambahkan kode di bawah ini setelah void 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();
        }
    }

Tambahkan kode di bawah ini di dalam void OnGUI() (sebelum tanda kurung tutup):

        //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());
                }
            }
        }

Dan yang terakhir, void Update(), yang akan berisi kode yang bertanggung jawab untuk mengirimkan skor pemain, setelah berubah. Tambahkan kode di bawah ini di awal skrip setelah semua variabel:

    //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;
            }
        }
    }

Perhatikan bagian **Testing**, karena kita tidak memiliki game yang dapat dimainkan, kita cukup menambah skor dengan menekan Q (Anda dapat menghapusnya nanti jika Anda sudah memiliki game dengan sistem penilaian, misal.mengumpulkan koin +1 poin, dll.)

Saat Anda menekan Putar dan masuk, Anda akan melihat 2 elemen baru: tombol 'Leaderboard' dan nilai Skor di bagian atas layar.

Sekarang kita lanjutkan membuat tabel MySQL.

Membuat tabel MySQL

Skor pengguna akan disimpan dalam tabel MySQL terpisah.

  • Masuk ke CPanel
  • Klik "phpMyAdmin" di bawah bagian DATABASES

  • Klik pada database yang Anda buat pada tutorial sebelumnya lalu klik tab SQL

  • Tempelkan kode di bawah ini ke editor kueri lalu klik "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;

Query di atas akan membuat tabel baru bernama 'sc_user_scores' yang akan menyimpan skor tertinggi beserta user_id sebagai referensi tabel utama 'sc_users'.

Bagian terakhir adalah mengimplementasikan logika sisi server.

Menerapkan logika sisi server

Logika sisi server akan terdiri dari skrip PHP yang bertanggung jawab untuk menerima/menyimpan skor dan mengambil papan peringkat.

Skrip pertama adalah score_submit.php.

  • Buat skrip PHP baru dan tempelkan kode di bawah ini ke dalamnya:

skor_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";
	}
?>

Skrip terakhir adalah leaderboard.php.

  • Buat skrip PHP baru dan tempelkan kode di bawah ini ke dalamnya:

papan peringkat.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";
	}
?>
  • Unggah score_submit.php dan leaderboard.php ke folder yang sama dengan tempat Anda mengunggah skrip PHP dari tutorial sebelumnya.

Setelah semuanya diatur, ketika Anda mengklik Papan Peringkat, skor/peringkat Anda akan dimuat bersama dengan 100 pemain teratas berdasarkan skor mereka:

Artikel yang Disarankan
Sistem Login Unity Dengan PHP dan MySQL
Membangun Game Jaringan Multipemain dalam Unity
Buat Game Mobil Multi Pemain dengan PUN 2
Unity Menambahkan Obrolan Multipemain ke Ruang PUN 2
Sinkronkan Rigidbodies Melalui Jaringan Menggunakan PUN 2
Buat Game Multiplayer di Unity menggunakan PUN 2
Pengantar Foton Fusion 2 di Unity