Підручник з онлайн-таблиці лідерів Unity
У цьому посібнику я покажу, як запровадити онлайн-таблицю лідерів у вашій грі в Unity.
Це продовження попереднього підручника: Unity Система входу за допомогою PHP і MySQL.
Наявність таблиці лідерів — це чудовий спосіб збільшити можливість відтворення, додавши рівень конкурентоспроможності вашій грі.
Як і раніше, для цього посібника потрібен сервер із cPanel разом із PHP і MySQLi (покращена версія MySQL).
Не соромтеся перевірити доступний преміум-хостинг VPS або дешевшу альтернативу Спільний хостинг.
Тож продовжимо!
Внесення змін до існуючого сценарію
Якщо ви дотримувалися підручника вище, тепер у вас буде сценарій під назвою '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 — це те, що ви використовуватимете в грі для відстеження результатів гравців. Це значення буде надіслано на сервер і збережено в базі даних. Рекомендується обфускатувати це значення, щоб захистити від злому.
Далі ми додаємо 2 лічильників, які відповідатимуть за подання балів і отримання таблиці лідерів. Додайте наведений нижче код у кінці сценарію перед закриттям останньої дужки:
//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());
}
}
}
І, нарешті, void 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;
}
}
}
Зверніть увагу на частину **Тестування**, оскільки у нас немає гри, у яку можна грати, ми просто збільшуємо рахунок, натискаючи Q (Ви можете видалити її пізніше, якщо у вас уже є гра з системою підрахунку очок, напр..зберіть монету +1 очко тощо)
Коли ви натискаєте «Грати» та входите в систему, ви повинні помітити 2 нові елементи: кнопку 'Leaderboard' і значення Score у верхній частині екрана.
Тепер переходимо до створення таблиці 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 найкращими гравцями на основі їхніх результатів: