English French German Spain Italian Dutch Russian Portuguese Japanese Korean Arabic Chinese Simplified

20 февр. 2012 г.

Unity3d. Сохранение результата в базу данных расположенную на сервере. Часть вторая - клиент.

     И так мы создали базу данных, создали скрипт, который осуществляет соединение с базой и чтение-запись данных. Теперь нам необходимо соединиться из нашей игры с php-скриптом, и получить из него данные, которые он получает с базы данных.
    Т.к. данные которые мы будем получать из php-скрита будут в xml виде то первым делом создадим скрипт XmlParser который будет осуществлять чтение xml-данных, выбор из данных атрибутов name и score, и сохранять значения этих атрибутов в массивы name[] и score[].



using UnityEngine;
using System.Collections;
using System.Xml;

public static class XmlParser 
{
    private static XmlDocument doc;
    private static XmlNode root;
    private static string[] names; // массив имен
    private static int[] scores; // массив результатов
    private static int userLenght; // длинна массива


    public static void Parse(string xml)
    {
        doc = new XmlDocument();
        doc.LoadXml(xml);

        root = doc.LastChild;
        if (root.HasChildNodes)
        {
            userLenght = root.ChildNodes.Count;
            names = new string[userLenght];
            scores = new int[userLenght];

            for (int i = 0; i < userLenght; i++)
            {
                XmlAttribute nameAtt = root.ChildNodes[i].Attributes["name"];
                XmlAttribute scoreAtt = root.ChildNodes[i].Attributes["score"];
                names[i] = (string)nameAtt.Value;
                scores[i] = ConvertStringToInt((string)scoreAtt.Value);
            }
        }
    }

    private static int ConvertStringToInt(string s)
    {
        int j;
        bool result = System.Int32.TryParse(s, out j);
        if (result = true)
        {
            return j;
        }
        else
        {
            Debug.Log("Error convert string to int");
            return 0;
        }
    }

    public static string Name(int index)
    {
        return names[index];
    }

    public static int Score(int index)
    {
        return scores[index];
    }

    public static int UserLength()
    {
        return userLenght;
    }
}



     Теперь создадим скрипт ServerHiScore который будет осуществлять доступ к php-скрипту, получение и отправку данных из приложения на сервер.

using UnityEngine;
using System.Collections;

public class ServerHiScore : MonoBehaviour 
{
    //Set the PHP url here
    public string PHPUrl = "http://yourDomainName.byethost15.com/HiScore.php"; // адрес скрипта
    //Set the hash key id 
    public string hashKey = "BRIGHTWORLDGAMES"; // ключ для шифрования данных

    private WWWForm obj_WWW;
    private bool b_loaded;

    private delegate void LoadXmlDel(string str);

 // Use this for initialization
 void Start () 
    {

 }
 
 // Update is called once per frame
 void Update () 
    {
 
 }

  

//Отправка данных
public void SendScore( int score, string name)
{
 WWWForm w_form = new WWWForm();
 //Telling PHP that the user is submiting the data
 w_form.AddField("action", "PostScore");
 //Sending hash code key to prevent unwanted user 
 w_form.AddField("hash", MD5.Md5Sum(name + "-" + score.ToString() + "-" + hashKey)); //Encrypt with MD5
 //Sending the user score
 w_form.AddField("score", score);
 //Sending the user name
 w_form.AddField("name", name);
 //Start waiting for the response back from the server
 StartCoroutine(WaitingForResponse(new WWW(PHPUrl, w_form), null));
}

//
public IEnumerator WaitingForResponse(WWW www, System.Func callback) 
{
    yield return www; // ожидаем пока получим с сервера данные
 
 if (www.error == null) 
    {
 // Debug.Log("Successful.");
 }
    else 
    {
 // Debug.Log("Failed.");
 }
 
 if (callback != null) {
  callback(www.text);
  callback = null;
 }
 
 //Очищаем данные
 www.Dispose();
}

//Получение данных
public void GetScores() 
{
 b_loaded = false;
 WWWForm w_form = new WWWForm();
 //Telling PHP that the user is loading the data
 w_form.AddField("action", "GetScore");
 //Start waiting for the response back from the server
 StartCoroutine(WaitingForResponse(new WWW(PHPUrl, w_form), LoadXMLData));
}


//Parse the XML data from the server
public bool LoadXMLData(string str)
{
 XmlParser.Parse(str);
 b_loaded = true;
    return true;
}

//Getting User length
public int GetUserLength() 
{
  return XmlParser.UserLength();
}
//Getting User Name by index
public string GetNameData(int index)
{
  return XmlParser.Name(index);
}
//Getting User Score by index
public int GetScoreData(int index)
{
  return XmlParser.Score(index);
}
//Loaded XML
public bool IsLoaded()
{
 return b_loaded;
} 

}

     При передаче данных, мы также передаем ключ в зашифрованном виде, чтобы никакие злоумышленники не могли записать свои данные на сервер, в php-скрипте мы проверяем совпадает ли ключ который храниться на сервере с ключом который мы передали, и записывает данные только если ключи совпадают. При создании аккаунта на byethost у вас автоматически создается домен с названием как выше имя, http://yourDomainName.byethost15.com/, где вместо byethost15 будет ваше название, посмотрите на панели слева в админке. Также вы всегда может создать другой домен, если этот вас не устраивает. 
     Теперь реализуем метод MD5. Создайте js-скрипт с следующим содержанием:
#pragma strict

static function Md5Sum(strToEncrypt: String)
{
    var encoding = System.Text.UTF8Encoding();
    var bytes = encoding.GetBytes(strToEncrypt);
 
    // encrypt bytes
    var md5 = System.Security.Cryptography.MD5CryptoServiceProvider();
    var hashBytes:byte[] = md5.ComputeHash(bytes);
 
    // Convert the encrypted bytes back to a string (base 16)
    var hashString = "";
 
    for (var i = 0; i < hashBytes.Length; i++)
    {
        hashString += System.Convert.ToString(hashBytes[i], 16).PadLeft(2, "0"[0]);
    }
 
    return hashString.PadLeft(32, "0"[0]);
}
     Т.к. это js-скрипт, а мы используем его в C# скрипте, то обязательно поместите скрипт MD5 в папку Plugins, иначе компилятров Unity3d будет выдавать вам ошибку. На этом вобщем-то всё. Теперь вы можете просто вызывать методы GetScores() и SendScores() скрипта ServerHiScore. Например у меня есть меню, в котором есть кнопка HiScore, при нажатии на которую появляется окно с результатами, тогда получать данные я буду следующим образом:

using UnityEngine;
using System.Collections;

public class Menu : MonoBehaviour 
{
    public GUISkin customSkin;

    enum Page {MENU, HOWTOPLAY, HISCORE, HISCOREMENU, EMPTY};
    private Page page;
    private Vector2 scrollPosition = Vector2.zero;
    private int maxUsers = 10;

    private GameObject cloud;

    private ServerHiScore objServerHighScore; // объявляем экземпляр класса ServerHiScore

    void Awake()
    {
    
    }

 void Start ()  
    {
        Time.timeScale = 1f;

        objServerHighScore = this.GetComponent();
        page = Page.MENU;

        cloud = GameObject.Find("HiScoreCloud");
 }
 
 void Update () 
    {
 }

    void OnGUI()
    {
        GUI.skin = customSkin;

        switch (page) 
        {
            case Page.MENU:
                MenuPage();
                break;
            case Page.HISCORE:
                HiScorePage();
                break;
            case Page.EMPTY:
                break;
        };

    }

    private void MenuPage() 
    {
        GUI.BeginGroup(new Rect(Screen.width / 1.2f - 125, Screen.height / 1.6f - 100, 250, 200));
      
        if (GUI.Button(new Rect(25, 20, 200, 30), "Start Game", GUI.skin.GetStyle("StartButton")))
        {
            Application.LoadLevel("MainScene");
        }
        if (GUI.Button(new Rect(10, 70, 220, 30), "How to Play", GUI.skin.GetStyle("HowToButton")))
        {
            StartCoroutine(ChangePage(Page.HOWTOPLAY)); 
        }
        if (GUI.Button(new Rect(25, 120, 180, 30), "Hi-Score", GUI.skin.GetStyle("QuitButton")))
        {
            objServerHighScore.GetScores(); // если нажали кнопку Hi-Score вызываем метод для получения данных
            StartCoroutine(ChangePage(Page.HISCORE));
        }
        GUI.EndGroup();
    }

    private void HiScorePage() 
    {
        GUI.BeginGroup(new Rect(Screen.width / 2f - 300, Screen.height / 2f - 210, 600, 420));
        GUI.Label(new Rect(200, 50, 200, 50), "Top 10", GUI.skin.GetStyle("HiScoreLabelCenter"));
        if (objServerHighScore.IsLoaded()) // если данные загружены
        {
            int numUsers = objServerHighScore.GetUserLength();
            if (numUsers > maxUsers)
                numUsers = maxUsers;

            scrollPosition = GUI.BeginScrollView(new Rect(100, 110, 400, 180), scrollPosition,
                                                 new Rect(0, 0, 250, 30 * numUsers));

            for (int i = 0; i < numUsers; i++)
            {
                GUI.Label(new Rect(0, i * 30, 35, 30), (i + 1).ToString() + ". ", GUI.skin.GetStyle("HiScoreLabel"));
                GUI.Label(new Rect(35, i * 30, 220, 30), objServerHighScore.GetNameData(i), GUI.skin.GetStyle("HiScoreLabel")); // получаем имя
                GUI.Label(new Rect(255, i * 30, 145, 30), objServerHighScore.GetScoreData(i).ToString(), GUI.skin.GetStyle("HiScoreLabel")); // получаем очки
            }
            GUI.EndScrollView();
        }
        else
        {
            GUI.Label(new Rect(200, 200, 200, 30), "LOADING...", GUI.skin.GetStyle("HiScoreLabelCenter"));
        }

        if (GUI.Button(new Rect(210, 300, 180, 30), "Back", GUI.skin.GetStyle("ReplayButton"))) 
        {
            StartCoroutine(ChangePage(Page.HISCOREMENU));
        }
        GUI.EndGroup();
    }

    IEnumerator ChangePage(Page pageNum)
    {
        switch (pageNum)
        {
            case Page.HOWTOPLAY:
                page = Page.EMPTY;
                while ((transform.position - new Vector3(178f, transform.position.y, transform.position.z)).magnitude > 0.5f)
                {
                    transform.position = Vector3.Lerp(transform.position, new Vector3(178f, transform.position.y, transform.position.z), Time.deltaTime*3.0f);
                    yield return new WaitForSeconds(0.01f);
                }
                page = Page.HOWTOPLAY;
                break;
            case Page.MENU:
                page = Page.EMPTY;
                while ((transform.position - new Vector3(0f, transform.position.y, transform.position.z)).magnitude > 0.5f)
                {
                    transform.position = Vector3.Lerp(transform.position, new Vector3(0f, transform.position.y, transform.position.z), Time.deltaTime*3.0f);
                    yield return new WaitForSeconds(0.01f);
                }
                page = Page.MENU;
                break;
            case Page.HISCORE:
                page = Page.EMPTY;
                while (cloud.transform.position.y > 0.1f)
                {  
                    cloud.transform.position = Vector3.Lerp(cloud.transform.position, new Vector3(cloud.transform.position.x, 0f, cloud.transform.position.z), Time.deltaTime*3f);
                    yield return new WaitForSeconds(0.01f);
                }
                page = Page.HISCORE;
                break;
            case Page.HISCOREMENU:
                page = Page.EMPTY;
                while (cloud.transform.position.y < 87.9f)
                {
                    cloud.transform.position = Vector3.Lerp(cloud.transform.position, new Vector3(cloud.transform.position.x, 88f, cloud.transform.position.z), Time.deltaTime * 3.0f);
                    yield return new WaitForSeconds(0.01f);
                }
                page = Page.MENU;
                break;
        };
    }
}

     Если же мне нужно сохранить данные на сервер, то я делаю следующим образом:
private void SubmitDialog() 
    {
        GUI.BeginGroup(new Rect(Screen.width / 2 - 150, Screen.height / 2 - 140, 300, 280));
        GUI.Box(new Rect(0, 0, 300, 280), "", GUI.skin.GetStyle("PauseBox"));
        GUI.Label(new Rect(25, 10, 250, 50), "Submit Results", GUI.skin.GetStyle("LevelCompleteLabel"));
        GUI.Label(new Rect(25, 60, 250, 50), "Your score: " + totalScore.ToString(), GUI.skin.GetStyle("LabelMidCenter"));
        GUI.Label(new Rect(25, 90, 250, 50), "Enter your name:");
        userName = GUI.TextField(new Rect(25, 140, 250, 45), userName, 20);
        if (GUI.Button(new Rect(105, 200, 80, 24), "Submit", GUI.skin.GetStyle("NextLevelButton")))
        {
            submitResult = true;
            //сохранение результата на сервер
            objServerHighScore.SendScore(totalScore, userName);
            dialog = previousDialog;

        }
        if (GUI.Button(new Rect(105, 235, 80, 24), "Back", GUI.skin.GetStyle("NextLevelButton")))
        {
            dialog = previousDialog;
        }
        GUI.EndGroup();
    }




8 комментариев:

  1. А можно приложить проект юнити рабочий потому что половина скриптов и ошибками как и PHP файл.. наверно сайт подпортил все :(

    ОтветитьУдалить
  2. я тоже поипался с этим кодом, в итоге придется писать свое...
    ошибки...
    зря потраченое время

    ОтветитьУдалить
  3. или удали эту тему или исправь ошибки, заебал

    ОтветитьУдалить
  4. Этот комментарий был удален автором.

    ОтветитьУдалить
  5. в этом скрипте ошибка




    using UnityEngine;
    using System.Collections;

    public class ServerHiScore : MonoBehaviour
    {
    //Set the PHP url here
    public string PHPUrl = "http://yourDomainName.byethost15.com/HiScore.php"; // адрес скрипта
    //Set the hash key id
    public string hashKey = "BRIGHTWORLDGAMES"; // ключ для шифрования данных

    private WWWForm obj_WWW;
    private bool b_loaded;

    private delegate void LoadXmlDel(string str);

    // Use this for initialization
    void Start ()
    {

    }

    // Update is called once per frame
    void Update ()
    {

    }



    //Отправка данных
    public void SendScore( int score, string name)
    {
    WWWForm w_form = new WWWForm();
    //Telling PHP that the user is submiting the data
    w_form.AddField("action", "PostScore");
    //Sending hash code key to prevent unwanted user
    w_form.AddField("hash", MD5.Md5Sum(name + "-" + score.ToString() + "-" + hashKey)); //Encrypt with MD5
    //Sending the user score
    w_form.AddField("score", score);
    //Sending the user name
    w_form.AddField("name", name);
    //Start waiting for the response back from the server
    StartCoroutine(WaitingForResponse(new WWW(PHPUrl, w_form), null));
    }

    //
    public IEnumerator WaitingForResponse(WWW www, System.Func callback)
    {
    yield return www; // ожидаем пока получим с сервера данные

    if (www.error == null)
    {
    // Debug.Log("Successful.");
    }
    else
    {
    // Debug.Log("Failed.");
    }

    if (callback != null) {
    callback(www.text);
    callback = null;
    }

    //Очищаем данные
    www.Dispose();
    }

    //Получение данных
    public void GetScores()
    {
    b_loaded = false;
    WWWForm w_form = new WWWForm();
    //Telling PHP that the user is loading the data
    w_form.AddField("action", "GetScore");
    //Start waiting for the response back from the server
    StartCoroutine(WaitingForResponse(new WWW(PHPUrl, w_form), LoadXMLData));
    }


    //Parse the XML data from the server
    public bool LoadXMLData(string str)
    {
    XmlParser.Parse(str);
    b_loaded = true;
    return true;
    }

    //Getting User length
    public int GetUserLength()
    {
    return XmlParser.UserLength();
    }
    //Getting User Name by index
    public string GetNameData(int index)
    {
    return XmlParser.Name(index);
    }
    //Getting User Score by index
    public int GetScoreData(int index)
    {
    return XmlParser.Score(index);
    }
    //Loaded XML
    public bool IsLoaded()
    {
    return b_loaded;
    }

    }

    ОтветитьУдалить
  6. хотя бы рабочие скрипты залили...

    ОтветитьУдалить
  7. Исправте ктонить скрипты и на 5 юньку

    ОтветитьУдалить
  8. Большое спасибо за статью!
    Наконец нашел интересный пример шифрования при передаче данных на сервер.

    ОтветитьУдалить