05-07 03:51
Notice
Recent Posts
Recent Comments
관리 메뉴

Scientific Computing & Data Science

Facebook and Unity Tutorial – Part Two 본문

CG & Video Games/Unity 3D

Facebook and Unity Tutorial – Part Two

cinema4dr12 2013. 6. 14. 18:47

Original Web Address: http://www.paladinstudios.com/2011/11/24/facebook-and-unity-tutorial-part-two/

In part two of our series about connecting facebook and unity we’ll learn the following:

  • Sending and storing highscores.
  • Retrieving highscores.
  • Display scores and facebook profile picture.
If you havent already, you should read part one first.

To make a game

In order to get a highscore we’ll need to make a game. For the demonstration we’ll be creating something very simple, but you should be able to use the lessons learned to implement facebook and highscores in your own games. We’ll be creating a dice rolling game, where the user has to click a button to roll a random number.

Create a new project and add the following script:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<pre>public class DiceGame : MonoBehaviour
{
    public static string FacebookId = "1"; //We'll link up with FacebookManager later.
    private const int max = 6; //Max score, the sides on the dice
    private string btnText = "Press to roll";
    void OnGUI()
    {
        if (GUI.Button(new Rect(10, 10, 200, 50), btnText))
        {
            int score = UnityEngine.Random.Range(1, max + 1); //Calculate our score.
            btnText = string.Format("You rolled {0}, \nclick to try again.", score);
 
            StartCoroutine(submitHighscore(score, FacebookId));
        }
 
    }
 
    IEnumerator submitHighscore(int score, string fbid)
    {
        yield return null; //We'll implement this later.
    }
}

Nothing fancy there, just a button that when clicked generates a random number and starts our mysterious submitHighscore coroutine. This method should do a webrequest to insert the highscores, so lets create the webservice first!

Creating the webservices

In order to store our scores we’ll need a webservice with a database. Luckily we already have a webserver running locally, we can use that to test. Start by creating a simple database.

We’ll need to store our users facebook id, score and it would be nice to know the creationdate too. Create your database by going to PHPMyadmin, make a new database called `highscores` and run the following SQL to generate the database:

1
2
3
4
5
6
CREATE TABLE `highscores`.`scores` (
`id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY ,
`fbid` TEXT NOT NULL ,
`score` INT NOT NULL ,
`creation_date` TIMESTAMP NOT NULL
) ENGINE = INNODB;

Now we have our database setup lets create the script that will insert our highscores; create a new file called insert_highscore.php in your WWW directory.  

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php
 
    mysql_connect('localhost','root',''); //Connect to the databsae
    mysql_select_db('highscores');
 
    $score = @$_GET['score'];
    $fbid = @$_GET['fbid'];
 
    if(!$score || !$fbid) //Ensure both parameters are passed
        die("No fbid or score");
 
    mysql_query("INSERT INTO scores (fbid, score) VALUES ('$fbid', $score)"); //Execute the query
?>

The first 2 lines connect to the database server and select the proper database. Next we validate the input, and stop executing if either score or fbid was not supplied. After we’re sure they exist we insert them in the database using SQL.
Great, visit your page at http://localhost/insert_highscore.php?fbid=4&score=1, and verify that there is a new row in the database by going to PhpMyAdmin and clicking on Browse in your scores table.

Now we can insert, we’ll need a way to retrieve the scores too. Create a new page with the name get_highscores.php. We dont want to get all highscores from a user, just the highest one. This is where GROUP BYcomes in handy.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?php
 
    mysql_connect('localhost','root','');
    mysql_select_db('highscores');
 
    $scoresQuery = mysql_query("SELECT
                                    MAX(score) as max_score,
                                    fbid
                                FROM
                                    scores
                                GROUP BY
                                    fbid
                                ORDER BY
                                    max_score DESC");
 
    while($scoresRow = mysql_fetch_assoc($scoresQuery))
        echo $scoresRow['fbid'] . ',' . $scoresRow['max_score'] . "\n";
 
?>

It connects to the database again, and executes the query. It then outputs each row as follows:

fbid,score
fbid,score
etc..

Check it out by going to http://localhost/get_highscores.phpIf required you can add more highscores by going back to insert_highscore.php. Try using different facebook id’s too, you’ll see more rows being displayed by get_highscores.

Inserting data

Now we have the webservices ready we should be able to call them from our Unity game.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
private Dictionary<string, int> scores; //The key is the facebook id, value is his score.
 
void Start()
{
    scores = new Dictionary<string, int>();
    StartCoroutine(retrieveHighscores()); //Start out by getting the current scores.
}
IEnumerator submitHighscore(int score, string fbid)
{
    WWW webRequest = new WWW("http://localhost/insert_highscore.php?score=" + score + "&fbid=" + fbid);
    yield return webRequest;
 
    yield return retrieveHighscores(); //After we submit we'd want an update, perhaps someone else played too!
}
IEnumerator retrieveHighscores()
{
    WWW webRequest = new WWW("http://localhost/get_highscores.php");
    yield return webRequest;
 
    string[] lines = webRequest.text.Split(new string[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries); //Split the response by newlines.
 
    scores = new Dictionary<string, int>(); //Always reset our scores, as we just got new ones.
    foreach (string line in lines) //Parse every line
    {
        string[] parts = line.Split(',');
 
        string id = parts[0];
        int score = int.Parse(parts[1]);
 
        scores.Add(id,score);
    }
}

Great, we call the webpages using Unity’s built in WWW class with the proper GET parameters. Whenever you post highscores it will automaticly retrieve them too. There is some parsing going on again in retrieveHighscores, namely we split them by each line and then by comma.
Our Dictionary contains the scores, with the facebook id as key and their highest score as value.

Drag the script on a gameobject in your scene and try it out in the Editor. Validate everything is working properly by clicking the button a few times and then check if there are new rows added in the database.

Linking Facebook

Our game now stores and retrieves highscores properly , but lets link it up with Facebook. Lets create a FacebookManager to retrieve the ID:

1
2
3
4
5
6
7
8
9
10
11
public class FacebookManager : MonoBehaviour
{
    void Start()
    {
        Application.ExternalCall("GetCurrentUser");
    }
    public void GetCurrentUserComplete(string fbid)
    {
        DiceGame.FacebookId = fbid;
    }
}

Stick it on a GameObject named FacebookManager and make a web build. Modify the WebPlayer.html to implement the Facebook SDK:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
<div id="fb-root"></div>
 
<script type='text/javascript'>
 
    //Fired when the facebook sdk has loaded
    window.fbAsyncInit = function()
    {
        FB.init(
        {
          appId      : '171298766297727',
          status     : true, // check login status
          cookie     : true, // enable cookies to allow the server to access the session
          oauth      : true, // enable OAuth 2.0
          xfbml      : false // dont parse XFBML
        });
 
        //Get the current login status.
        FB.getLoginStatus(function(loginStatusResponse)
        {
            if(!loginStatusResponse.authResponse) //Not logged in, log him in
                FB.login();
        });
 
    };
 
    function GetCurrentUser() //When we are logged in this shows our name.
    {
        FB.api('/me', function(meResponse)  //Do a graph request to /me
        {
            getUnity().SendMessage("FacebookManager", //Game object name, make sure this exists!
                                    "GetCurrentUserComplete", //Method to call
                                    meResponse.id); //Our serialized facebook data
        });
    }
 
    //Load the Facebook JS SDK
    (function(d){
     var js, id = 'facebook-jssdk'; if (d.getElementById(id)) {return;}
     js = d.createElement('script'); js.id = id; js.async = true;
     js.src = "//connect.facebook.net/en_US/all.js";
     d.getElementsByTagName('head')[0].appendChild(js);
    }(document));
 
    function getUnity()
    {
        if (typeof unityObject != "undefined")
        {
            return unityObject.getObjectById("unityPlayer");
        }
        return null;
    }
    if (typeof unityObject != "undefined")
    {
        unityObject.embedUnity("unityPlayer", "WebPlayer.unity3d", 960, 640);
    }
</script>
 
<div id='unityPlayer'></div>

Open the page and press the button a few times, then go to your database and you’ll see your Facebook ID inserted correctly!

The next step is to do something with the facebook id’s we store, lets show the user’s profile image. As per the Facebook Graph API you can get a users image with the following url: http://graph.facebook.com/UID/picture/

Change your retrieve highscores to this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
private Dictionary<string, Texture2D> fbProfileImages = new Dictionary<string, Texture2D>();
    IEnumerator retrieveHighscores()
    {
        WWW webRequest = new WWW("http://localhost/fbblog/p2/get_highscores.php");
        yield return webRequest;
 
        string[] lines = webRequest.text.Split(new string[] { "\n" }, StringSplitOptions.RemoveEmptyEntries);
 
        scores = new Dictionary<string, int>(); //Always reset our scores, as we just got new ones.
        foreach (string line in lines)
        {
            string[] parts = line.Split(',');
 
            string id = parts[0];
            int score = int.Parse(parts[1]);
 
            if (!fbProfileImages.ContainsKey(id)) //Have we already loaded this user before?
            {
                //No, better get our image!
                WWW imageRequest = new WWW("http://graph.facebook.com/" + id + "/picture");
                yield return imageRequest;
                fbProfileImages.Add(id, imageRequest.texture);
            }
 
            scores.Add(id,score);
        }
    }

And change your OnGUI to:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
void OnGUI()
 {
     if (GUI.Button(new Rect(10, 10, 200, 50), btnText))
     {
         int score = UnityEngine.Random.Range(1, max + 1);
         btnText = string.Format("You rolled {0}, \nclick to try again.", score);
 
         StartCoroutine(submitHighscore(score, FacebookId));
     }
 
     float h = 10;
     foreach (var score in scores)
     {
         GUI.Label(new Rect(300, h, 200, 50), score.Value.ToString());
 
         if (fbProfileImages != null && fbProfileImages.ContainsKey(score.Key))
             GUI.DrawTexture(new Rect(230, h, 50, 50), fbProfileImages[score.Key]);
 
         h += 60;
     }
 }

The fbProfileImages dictionary keeps track of the profile Textures, they get loaded whenever a new user is found. Build and run the project again, and be amazed how easy it is to retrieve someone’s Facebook profile picture.

A great result, but there’s something fishy. Mark Zuckerberg has rolled 7 with a 6 sided dice! In the next part we’ll find out how he did it, and protect ourself against several common attacks used by malicious users.


Comments