الوحدة على الانترنت المتصدرين البرنامج التعليمي

في هذا البرنامج التعليمي، سأوضح كيفية تنفيذ لوحة المتصدرين عبر الإنترنت في لعبتك في Unity.

هذا استمرار للبرنامج التعليمي السابق: Unity نظام تسجيل الدخول باستخدام PHP وMySQL.

يعد الحصول على لوحة صدارة طريقة رائعة لزيادة إمكانية إعادة اللعب من خلال إضافة مستوى من القدرة التنافسية إلى لعبتك.

كما كان من قبل، يتطلب هذا البرنامج التعليمي خادمًا مزودًا بـ cPanel بالإضافة إلى PHP وMySQLi (نسخة محسنة من MySQL).

لا تتردد في التحقق من استضافة VPS المميزة ذات الأسعار المعقولة أو بديل Shared Hosting الأرخص.

لذلك دعونا نستمر!

إجراء تعديل على البرنامج النصي الموجود

إذا اتبعت البرنامج التعليمي أعلاه، فسيكون لديك الآن برنامج نصي يسمى 'SC_LoginSystem'. سنقوم بتنفيذ ميزة المتصدرين عن طريق إضافة بعض التعليمات البرمجية إليها.

  • افتح البرنامج النصي 'SC_LoginSystem'

أولاً نبدأ بإضافة المتغيرات الضرورية:

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

ملاحظة: المتغير currentScore هو ما ستستخدمه في اللعبة لتتبع نتائج اللاعبين. سيتم إرسال هذه القيمة إلى server وتخزينها في قاعدة البيانات، ومن المستحسن التعتيم تلك القيمة للحماية من الاختراق.

بعد ذلك، نضيف عدادين سيكونان مسؤولين عن إرسال النتائج واسترداد لوحة المتصدرين. أضف الكود أدناه في نهاية البرنامج النصي قبل إغلاق القوس الأخير:

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

التالي هو واجهة المستخدم المتصدرين. أضف الكود أدناه بعد 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();
        }
    }

أضف الكود أدناه داخل الفراغ OnGUI() (قبل قوس الإغلاق):

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

وأخيرًا، سيتم إلغاء Update()، والذي سيحتوي على الكود المسؤول عن إرسال نتيجة اللاعب، بمجرد تغييره. أضف الكود أدناه في بداية البرنامج النصي بعد كل المتغيرات:

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

لاحظ الجزء **Testing**، نظرًا لعدم وجود لعبة قابلة للعب، فإننا ببساطة نزيد النتيجة بالضغط على Q (يمكنك إزالتها لاحقًا إذا كان لديك بالفعل لعبة بنظام تسجيل، على سبيل المثال. جمع قطعة نقدية +1 نقطة، الخ.)

عندما تضغط على تشغيل وتسجيل الدخول، يجب أن تلاحظ عنصرين جديدين: الزر 'Leaderboard' وقيمة النتيجة في أعلى الشاشة.

ننتقل الآن إلى إنشاء جدول MySQL.

إنشاء جدول MySQL

سيتم تخزين نتائج المستخدم في جدول MySQL منفصل.

  • تسجيل الدخول إلى لوحة التحكم CPanel
  • انقر على "phpMyAdmin" ضمن قسم قواعد البيانات

  • انقر فوق قاعدة البيانات التي قمت بإنشائها في البرنامج التعليمي السابق ثم انقر فوق علامة التبويب SQL

  • الصق الكود أدناه في محرر الاستعلام ثم انقر فوق "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;

سيؤدي الاستعلام أعلاه إلى إنشاء جدول جديد يسمى 'sc_user_scores' والذي سيخزن أعلى الدرجات مع user_id كمرجع للجدول 'sc_users' الرئيسي.

الجزء الأخير هو تطبيق المنطق من جانب الخادم.

تنفيذ المنطق من جانب الخادم

سيتألف المنطق من جانب الخادم من نصوص PHP التي ستكون مسؤولة عن استلام/تخزين النتائج واسترداد لوحة المتصدرين.

النص الأول هو score_submit.php.

  • قم بإنشاء برنامج PHP جديد والصق الكود أدناه بداخله:

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

النص الأخير هو leaderboard.php.

  • قم بإنشاء برنامج PHP جديد والصق الكود أدناه بداخله:

المتصدرين.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";
	}
?>
  • قم بتحميل كل من Score_submit.php وleaderboard.php إلى نفس المجلد الذي قمت بتحميل نصوص PHP النصية فيه من البرنامج التعليمي السابق.

بعد إعداد كل شيء، عند النقر على لوحة المتصدرين، يجب تحميل درجاتك/تصنيفك مع أفضل 100 لاعب بناءً على نتائجهم:

المقالات المقترحة
نظام تسجيل الدخول الوحدة مع PHP وMySQL
بناء ألعاب شبكية متعددة اللاعبين في Unity
مزامنة الأجسام الصلبة عبر الشبكة باستخدام PUN 2
مقدمة إلى الفوتون فيوجن 2 في الوحدة
ضغط البيانات متعددة اللاعبين ومعالجة البتات
اصنع لعبة سيارات متعددة اللاعبين باستخدام PUN 2
PUN 2 تعويض التأخر