اتصال یونیتی به MySQL
موتور یونیتی برای ساخت بازیهای ویدیویی هست و زبان MySQL برای مدیریت دادهها استفاده میشود. شاید این دو در نگاه اول هیچ هدف مشترکی نداشته باشند، اما اگر دقیقتر بخواهیم نگاه کنیم برای ساخت بازیهای آنلاین ما نیازمند هردوی آنها هستیم. در این مقاله مسیری که برای اتصال یونیتی به پایگاه داده استفاده کردهام را بازگو میکنم.
با توجه به قابلیتهای فراوان زبان c#، در ابتدا تلاش کردم تا بدون استفاده از زبان تحت سرور موتور یونیتی را مستقیما به پایگاه داده وصل کنم. برای تلاش اول از این لینک استفاده کردم. برای این کار باید از پکیج اتصال یونیتی به MySQL استفاده کنیم، که به خطای زیر برخورد کردم:
برای حل این ارور راهحلهایی موجود هستند هیچکدام برای من پاسخگو نبودند. طبق پرسوجوهایی که داشتم به این نتیجه رسیدم که این ارور احتمالا بخاطر نصب اشتباه یونیتی و تحریمها بوده است.
همچنین یکی دیگر از مسیرهای پیشنهادی برای اتصال به پایگاه داده بدون استفاده از زبان PHP و دیگر زبانهای تحت سرور استفاده از کتابخانه اتصال یونیتی به MySQL بوده است که در این لینک پیشنهاد شده است، که متاسفانه در این راه نیز خطاهای بسیاری بوجود آمدند.
در انتها از زبان PHP برای اتصال استفاده کردم. در این راه از طریق کد سیشارپ فایل PHP فراخوانی میشود و اطلاعات لازم از طریق GET به آن داده میشود. فایل PHP به ساختمان دادهی ما متصل میشود و اطلاعات را میفرستد و یا دریافت میکند.
در ادامهی این نوشته، به ساخت بازیای میپردازیم که در آن نام کاربری و بالاترین امتیاز هر فرد را از طریق یونیتی در پایگاه داده ثبت میشود و بعد با فشار دادن دکمهی «تنظیم مجدد» بالاترین پنج امتیاز را نشان میدهد.
در این بازی از WampServer برای تبدیل سیستم خود به یک سرور داخلی و از PHPMyAdmin برای ساخت پایگاه داده استفاده میکنیم. از زبان PHP برای اسکریپتهای سرور و از موتور یونیتی و زبان سیشارپ برای ساخت بازی استفاده میکنیم.
ساخت پایگاه داده
ومپ سرور را روی سیستم خود نصب کنید. برای نصب آن میتوانید از این لینک کمک بگیرید. بعد از نصب آن را فعال کنید و به PhpMyAdmin بروید. یک پایگاه داده با نام دلخواه ایجاد کنید.
در تب SQL در PhpMyAdmin میتوانید از طریق کد جدولها را بسازید، اطلاعات اضافه کنید، حذف کنید یا تغییر دهید.
جدولی به نام highscore ایجاد میکنید که دو ستون "اسم" و "بالاترین امتیاز" دارد.
CREATE TABLE highscore (
name VARCHAR(15) NOT NULL DEFAULT 'anonymous',
score INT(10) UNSIGNED NOT NULL DEFAULT 0
);
شما میتوانید از طریق رابطکاربری PHPMyAdmin نیز جدولهای خود را ایجاد کنید
پیشنهاد میشود برای تست کردن اپلیکیشن اطلاعاتی را در همین ابتدا وارد آن کنید. در این صورت، راحتتر میتوانید بازی را در یونیتی تست کنید. پس اگر اطلاعاتی از قبل دارید به این صورت در جدول بالاترین امتیاز آنها را ثبت کنید.
INSERT INTO scores (name, score) VALUES ('John', 100),('Sally', 110), ('Phil', 90), ('Katy', 130), ('Jack', 150)
حال که پایگاه دادهی ما تنظیم شده است، زمان آن است که از طریق کد PHP پل ارتباطی بین یونیتی و پایگاه داده ایجاد کنیم. از طریق این کد ما بر روی پایگاه داده مینویسیم و همچنین دادهها را میخوانیم.
کدهای سمت سرور - display.php
در پوشهی سرور (اگر از ومپ استفاده میکنید در پوشه www) پوشهای به نام HighScoreGame برای مرتبسازی کدها ایجاد میکنیم. سپس فایلی با فرمت PHP به نام display ایجاد میکنیم. فایل را میتوانید از طریق نرمافزار هایی مثل Notepad++ ، PhpMyStorm و ... باز کنید و کد پایین را در آن کپی کنید. با توجه به تنظیمات خود مقادیر "hostname" , "username " و "password" را عوض کنید.
<?php
$servername = "localhost"
$username = "root"
$password = ""
$dbname = "unity_with_mysql"
try {
$conn = new PDO("mysql:host=$servername;dbname=$dbname", $username, $password);
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}
catch(PDOException $e)
{
echo "Connection failed: " . $e->getMessage();
}
$sth = $conn->query('SELECT * FROM highscore ORDER BY highscore DESC LIMIT 5');
$sth->setFetchMode(PDO::FETCH_ASSOC);
$result = $sth->fetchAll();
if (count($result) > 0
{
foreach($result as $r)
{
echo $r['username'], "\n _"
echo $r['highscore'], "\n _"
}
}
?>
این اسکریپت در مجموع، ساده است. در اول کد، اتصالی به پایگاه داده ایجاد میشود که اگر با خطا مواجه شود پیام "Connection failed" به همرار ارور آن نمایش داده میشود.
سپس در متغیر sth دستور SQL ای که برای انتخاب ۵ نفر با بالاترین امتیاز در جدول هست بر روی متغیر اتصال به پایگاه داده فراخوانی میشود. (این دستور SQL اجرا میشود.) نتیجهی دستور sql را بر اساس مود "FETCH_ASSOC" میگیرد و با کمک دستور foreach اسم و امتیاز آنها را چاپ میکند. پس با کمک این فایل ما نام و امتیاز ۵ نفر برتر را میگیریم و نمایش میدهیم.
کدهای سمت سرور - addscore.php
نام فایل بعدی کدهای سمت سرور addscore است. همانطور که از اسم آن مشخص است، با استفاده از این فایل امتیاز نفرات جدید را میگیریم و به پایگاهداده اضافه میکنیم. تمامی دادههایی که این فایل به پایگاه داده اضافه میکند از یونیتی سرچشمه میگیرند. مجددا در این فایل مقادیر متغیرها را با تنظیمات خود مطابقت دهید.
فراموش نکنید مقدار secretkey باید با مقداری که در یونیتی برای آن میگذارید مطابقت داشته باشد. این مقدار میتواند هرچیزی که دوست دارید باشد. اگر این مقدار مطابقت نداشته باشد مقادیر شما در پایگاه داده ثبت نمیشود.
<?php
$servername = "localhost"
$username = "root"
$password = ""
$dbname = "unity_with_mysql"
$secretKey = "2003"
try {
$conn = new PDO("mysql:host=$servername;dbname=$dbname", $username,
$password);
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}
catch(PDOException $e)
{
echo "Connection failed: " . $e->getMessage();
}
$hash = $_GET['hash'];
$realHash = hash('sha256', $_GET['name'] . $_GET['score'] . $secretKey);
if($realHash == $hash)
{
$sth = $conn->prepare('INSERT INTO highscore VALUES (null, :username,:highscore)');
try
{
$sth->bindParam(':username', $_GET['name'], PDO::PARAM_STR);
$sth->bindParam(':highscore', $_GET['score'], PDO::PARAM_INT);
$sth->execute();
}
catch(Exception $e)
{
echo '<h1>An error has ocurred.</h1><pre>',
$e->getMessage() ,'</pre>';
}
}
?>
برای مرتب نگه داشتن، مطمئن شوید این فایل در پوشهی مخصوص و در کنار فایل display.php است.
مجددا در این کد اتصالی به پایگاه داده با استفاده از مقدارهایی که برای متغیرهای نام کاربری، نام پایگاه داده و رمز عبور گذاشتهاید ایجاد میکنیم. از $_GET برای گرفتن پارامترهایی که از سمت کلاینت (کاربر) فرستاده شده است استفاده میکنیم. همچنین از تابع hash برای رمزنگاری اطلاعات استفاده میکنیم تا امنیت اطلاعات افزایش یابد.
در کد بالا برای صحتسنجی اطلاعات ما مجددا هش را تولید میکنیم تا مطمئن شویم اطلاعات فرستاده شده درست هستند. درصورتی که این دو مطابقت داشته باشند، با استفاده از prepare دستور SQL را آمادهی اجرا میکند و در صورتی که با خطا مواجه نشود (با استفاده از try catch) نام و امتیاز کاربر را در کد SQL قرار میدهد و آن را اجرا میکند. در صورت مواجهه با خطا، پیام خطا چاپ میشود.
حال زمان آن رسیده است که موتور یونیتی را باز کنید و شروع به ساخت برنامه کنید.
یونیتی - رابط کاربری
زمان آن رسیده تا با استفاده از یونیتی اپلیکیشن را بسازیم. از یونیتی برای طراحی رابط کاربری آن استفاده میکنیم. منظور از رابط کاربری چیدمان دکمهها، فیلدها و متونی است که استفاده میشوند. با ایجاد یک پروژه جدید در یونیتی شروع میکنیم.
هر کدام از قالب ها در این پروژه قابل استفاده هستند. اما در این مثال ما از قالب سهبعدی استفاده میکنیم. محل ذخیرهسازی پروژه را انتخاب میکنیم و دکمه "create" را میزنیم.
حال شروع به ایجاد رابط کاربری میکنیم. رابط کاربری این پروژه شامل ۹ المان است. دکمهی "submit" برای اضافه کردن اطلاعات به جدول و دکمه "Reset" برای نمایش اطلاعات جدول در دو برچسب "players" و "scores" هست. از دو فیلد برای نوشتن نام کاربری جدید و امتیاز آن استفاده میکنیم. همچنین از سه برچسب دیگر برای اطلاعات فیلدها و فرم استفاده میکنیم.
با کلیک راست بر روی قسمت Hierarchy (در چیدمان پیشفرض، این بخش در قسمت چپ پنجره قرار دارد) در زیرگروه UI که انواع المانهای رابط کاربری در آن موجود هستند، ۹ المان مورد نظر را به پروژه اضافه میکنیم. این المانها برای رابط کاربری از نوع "text" ، "button" و "input field" هستند.
با استفاده از ۶ ابزاری که در نوار ابزار این برنامه قرار دارد، میتوانید اندازه و موقعیت این اشیاء را تغییر دهید. همچنین با استفاده از میانبر های "Q" , "W" , "E" , "R" , "T" , "Y" به این ابزار دسترسی دارید.
یونیتی - HighScoreControl.cs
از طریق کلیک کردن در صفحه و انتخاب گزینه create و C# script یک فایل اسکریپت به نام HighScoreControl ایجاد کنید و فایل را در ادیتور باز کنید. این اسکریپت مسئول ارسال و دریافت اطلاعات از پایگاه داده است. از بین این دو، گرفتن امتیازها از پایگاه داده کار آسانتری است که به وسیله ارسال یک HTTP از طریق UnityWebReguest انجام میشود. در روند انجام این کار کد display.php به ما کمک میکند.اسکریپت سمت سرور اجرا میشود و اطلاعات را به یونیتی میفرستد. سپس یونیتی آن را در رابط کاربری نمایش میدهد.
ارسال اطلاعات از الگویی مشابه پیروی میکند اما لازم است شما از تابع POST برای ارسال اطلاعات با فایل addscore.php استفاده کنید. البته همانطور که در بالا اشاره شد، قبل از ارسال اطلاعات آنها را رمزنگاری میکنیم تا در سمت سرور صحت آنها سنجیده شود. در دنیای واقعی برای امنیت اطلاعات باید کارهای بیشتری انجام شود. اما برای هدف این مثال در این حد کافی است و تلاش میکنیم روند را ساده نگه داریم. پس از این که اطلاعات از طریق درخواست HTTP ارسال شدند فایل addscore.php بقیه روند اضافه کردن اطلاعات را مدیریت میکند.
برای این که کد ما به درستی کار کند نیاز است فضای نامیهایی که از آن استفاده شده است را صدا کنید.
using System;
using System.Collections;
using System.Security.Cryptography;
using System.Text.RegularExpressions;
using UnityEngine;
using UnityEngine.Networking;
using UnityEngine.UI;
در کلاس HighScoreControl متغیر های زیر را تعریف و آنها را مقداردهی میکنیم.
private string secretKey = "2003"
public string addScoreURL ="http://localhost/HighScoreGame/addscore.php?"
public string highscoreURL ="http://localhost/HighScoreGame/display.php?"
public Text nameTextInput;
public Text scoreTextInput;
public Text nameResultText;
public Text scoreResultText;
- مقدار متغیر secretKey باید با مقدار آن در addscore.php تطابق داشته باشد. حواستان باشد آنها کاملا یکسان باشند.
- همینطور دو آدرس URL مسیری است که کدهای سمت سرور در آنجا هستند و یونیتی اطلاعات را از آن میخواند و به آن ارسال میکند
- در نهایت 4 برچسب در این کد تعریف شدهاند که برای نمایش اطلاعات در رابط کاربری استفاده میشوند.
در یونیتی coroutines توابعی هستند که میتوان اجرای آنها را متوقف کرد و از خروجای آنها تا آن زمان استفاده کرد، سپس دوباره از همانجا که توقف کرده است به اجرا ادامه دهد.
در ادامه کد نیازمند دو تابع هستیم، یکی برای ارسال دادهها و دیگری برای دریافت آنها. این توابع در رابط کاربری به دکمههای شما نسبت داده میشوند که در ادامه خواهید دید و برچسبها را به مقدار پیشفرض خود تغییر میدهند.
public void GetScoreBtn()
{
nameResultText.text = "Player: \n \n"
scoreResultText.text = "Score: \n \n"
StartCoroutine(GetScores());
}
public void SendScoreBtn()
{
StartCoroutine(PostScores(nameTextInput.text,
Convert.ToInt32(scoreTextInput.text)));
nameTextInput.gameObject.transform.parent.GetComponent<InputField>().text = ""
scoreTextInput.gameObject.transform.parent.GetComponent<InputField>().text = ""
}
صحبت از کوروتینها شد، حال زمان مناسبی است که اولین کوروتین به نام GetScores را ایجاد کنیم. همانطور که از نام آن مشخص است، این تابع مسئول بازیابی ۵ امتیاز اول همراه با نام آنها از پایگاه داده است. همانگونه که قبلا گفته شد این تابع یک درخواست HTTP ارسال میکند تا با فایل display.php ارتباط برقرار کند و کد sql در پایگاه داده اجرا شود.
در کد display.php به ازای هر نام و امتیاز یک "_" چاپ میکند و در تابع GetScores در یونیتی با استفاده از همین علامت آنها را از هم جدا میکنیم و در برچسب های " nameResultText" و "scoreResultText" چاپ میکنیم.
IEnumerator GetScores()
{
UnityWebRequest hs_get = UnityWebRequest.Get(highscoreURL);
yield return hs_get.SendWebRequest();
if (hs_get.error != null)
Debug.Log("There was an error getting the high score: "+ hs_get.error);
else
{
string dataText = hs_get.downloadHandler.text;
MatchCollection mc = Regex.Matches(dataText, @"_");
if (mc.Count > 0)
{
string[] splitData = Regex.Split(dataText, @"_");
for (int i =0; i < mc.Count; i++)
{
if (i % 2 == 0)
nameResultText.text +=splitData[i];
else
scoreResultText.text +=splitData[i];
}
}
}
}
قدم بعدی کوروتین PostScores است که ارسال امتیاز و نام کاربر را مدیریت میکند. این تابع دو پارامتر دارد. اول بر اساس نام کاربری و امتیاز و کلید، آنها را هش میکند تا بتوان آنها را در سمت سرور صحتسنجی کرد. بعد از طریق HTTP یک درخواست ارسال میشود تا اطلاعات از طریق addscore.php در پایگاه داده اضافه شوند. ادامهی کار را فایل سرور مدیریت میکند که در بالا به آن اشاره کردیم.
IEnumerator PostScores(string name, int score)
{
string hash = HashInput(name + score + secretKey);
string post_url = addScoreURL + "name=" + UnityWebRequest.EscapeURL(name) +
"&score="+ score + "&hash=" + hash;
UnityWebRequest hs_post = UnityWebRequest.Post(post_url, hash);
yield return hs_post.SendWebRequest();
if (hs_post.error != null)
Debug.Log("There was an error posting the high score: "+ hs_post.error);
}
تابع PostScores از طریق تابع HasInput اطلاعات را هش میکند اما هنور این تابع نوشته نشده است پس با نوشتن این تابع فایل اسکریپت را کامل میکنیم. کاری که این تابع انجام میدهد این است که یک رشته بر اساس نام کاربری و امتیاز و کلید مخفی میگیرد و آن را بر اساس الگوریتم SHA256 هش میکند. خروجی این تابع به سمت سرور ارسال میشود.
public string HashInput(string input)
{
SHA256Managed hm = new SHA256Managed();
byte[] hashValue = hm.ComputeHash(System.Text.Encoding.ASCII.GetBytes(input));
string hash_convert = BitConverter.ToString(hashValue).Replace("-", "").ToLower();
return hash_convert;
}
حال فایل HighScoreControl.cs کامل است. اطمینان حاصل کنید که آن را ذخیره کردهاید و به ادیتور یونیتی برگردید.
یونیتی - اتمام پروژه
کد HighScoreControl.cs را به عنوان component به دوربین اصلی بدهید.
مطمئن شوید تمام زیرگروه های canvas باز است تا آنها را به component اسکریپت در دوربین نسبت دهیم.
حال نوبت آن است تا دکمهها را فعال کنیم. submit-btn را انتخاب کنید و به کامپوننت button بروید و تابع SendScoreButton را به آن نسبت بدهید. برای انجام این کار در قسمت زیر بر روی دکمه + کلیک کنید تا یک رویداد به آن اضافه کنید.
سپس دوربین را به عنوان شئ به قسمت زیر بکشید. دلیل استفاده از دوربین این است که کد به این شئ نسبت داده شده است.
بر روی منوی no function کلیک کنید تا تابع SendScoreButton را از آنجا صدا بزنیم.
مجددا این کار را برای دکمه Reset نیز تکرار کنید و از تابع GetScoreBtn استفاده کنید.
بعد از اتمام تمام اینها از دکمهی play که در بالا و وسط نوار ابزار قرار دارد میتوانید برای تست بازی خود استفاده کنید. تست را با این شروع کنید که میتوانید اطلاعات را بخوانید و بعد مشخصاتی را به آن اضافه کنید و مجددا اطلاعات را بخوانید. پروژهی شما کامل است.
شما میتوانید دانش خود را با تجربههایی از این قبیل، افزایش تعداد جدولها و ذخیرهسازی دادههای مختلف افزایش دهید. برنامه را تغییر دهید و برای خود پروژههای جدید با خلاقیت و هوشمندی به وجود بیارید.
سارا برادران افتخاری
دکتر مریم حاجی اسمعیلی. دکترای علوم کامپیوتر از دانشگاه کینگستون لندن
Dr.Maryam Hajiesmaeili
PhD of computer science from Kingston university of London
https://ir.linkedin.com/in/dr-maryam-hajiesmaeili-90930743
منابع:
https://www.w3schools.com
https://www.php.net
https://www.red-gate.com
https://docs.microsoft.com
https://forum.unity.com
https://www.mono-project.com
https://stackoverflow.com
مطلبی دیگر از این انتشارات
بازی های مستقل چطور روح صنعت بازی را زنده نگه داشتهاند؟
مطلبی دیگر از این انتشارات
لود کردن فایل ها از پوشه به بازی در یونیتی -Loading Resources at Runtime
مطلبی دیگر از این انتشارات
نصب و بررسی آنریل انجین ۵ روی لینوکس