Monday 17 February 2014

Technical Post 1: Integrating Facebook into unity C# Part 3

<< Part 2

Please Note: This shows the high scores system that I am currently using, it works where it will show the top 10 players if the player who is signed in is within the top 10, if the player is outside the top 10 it shows the top 5 players, there is then a break in the table and it shows the 2 players above you, you and then the 2 players below you. This is in C#.

I do not show the entirety of the script for all 10 positions on our table, I am using 3D Text, so all text assigned is using the TextMesh object, and Textures are assigned to OTSprites (Orthello).
Where the below code has "....." it means that the code continues to fill in the rest of the fields in similar fashion to that shown

//List used for FBResult from HTTP GET Request for scores
private static List<object> scoresFacebook = null; 
//Is the player in the top 10
bool PlayerTopTen = false; //used to align the table once
bool DoOnce = false; //used to remove any unused entries once
bool OneTime = false; //used to get profile pictures once
bool getProfilePics = false; 
//variable to take the number of entries to indicate the last position taken up //in the table, used to remove empty entries to our table
int lastPos; 

//Update function, where our methods are called
void Update()
{
    if (!DoOnce){
        friendsScore ();
        DoOnce = true; //make it so that the method is only called once
    }
    if (PlayerTopTen) {
        Divider.SetActive(false); 

        //the player is in the top 10, no break in table needed
        if (!OneTime) 

        {
            removeNames(lastPos); 

            //remove any names on table that are empty entries
            OneTime = true;
        }
    }

}

public void friendsScore()
{
    FB.API("/app/scoresfields=score", 

            Facebook.HttpMethod.GET, CallbackFriendScores);
    //HTTP GET request for the scores
}
public void CallbackFriendScores(FBResult result)
{
    scoresFacebook = new List<object>();

    //Our lists of objects and assigned list of object using the Util.cs script
    List<object> scoresList = Util.DeserializeScores(result.Text);

    int i = 0; //count the number of objects as we go through the foreach
    foreach(object score in scoresList)
    {
        var entry = (Dictionary<string,object>) score;
        var user = (Dictionary<string,object>) entry["user"];
        string name = (string)user["name"];
        string userId = (string)user["id"];

        //extract relevant data from objects to assign to text in game
        if (string.Equals(userId,FB.UserId)) 

        {
            playerScoreFound = true;
            //we have found the player
            myScore = "" + entry["score"];
        }
        //set the top 10 names regardless of player position
        if (i<10)
        {
            if (i==0)
            {
                firstRank.text = "#1";
                firstNameFB.text = name;
                firstScoreFB.text = "" + entry["score"];
                idPos1 = userId; //take a reference for the players id
                pic1.SetActive(true); //make sure their profile picture is active
            }
            else if (i==1) //repeat for first 10 objects in the list
            {
                secondRank.text = "#2";
                secondNameFB.text = name;
                secondScoreFB.text = "" + entry["score"];
                idPos2 = userId;
                pic2.SetActive(true);
            }
            else if (i==2)
            {
                thirdRank.text = "#3";
                thirdNameFB.text = name;
                thirdScoreFB.text = "" + entry["score"];
                idPos3 = userId;
                pic3.SetActive(true);
            }

.....

//now check if the player was found within the first 10 entries, if so only show //top 10 players from friends list
if (playerScoreFound && i<10)
{
    PlayerTopTen = true;
    tableFormed = true; //the table has been formed
    
//set the ranks for position 6-10 to their corresponding numbers
    if(i>=6)
        sixthRank.text = "#6";
    if(i>=7)
        seventhRank.text = "#7";

    if(i>=8)
        eighthRank.text = "#8";

    if(i>=9)
        ninethRank.text = "#9";

    if(i>=10)
        tenthRank.text = "#10";

}
//our player has not been found yet, so we need to keep looking through the //objects and move up the positions to make space for the new object data to be //added to position 10
else if (!tableFormed)
{
    //keep sorting for new places as player has not been found yet
    
//get the data at position 7 for storing in position 6
    tempName = seventhNameFB.text;
    tempScore = seventhScoreFB.text;
    rank = seventhRank.text;
    sixthRank.text = rank;
    sixthNameFB.text = tempName;
    sixthScoreFB.text = tempScore;
    idPos6 = idPos7; //change id position references for profile pictures
    //repeat for position 8 and 7

    tempName = eighthNameFB.text;         
    tempScore = eighthScoreFB.text;
    rank = eighthRank.text;
    seventhRank.text = rank;
    seventhNameFB.text = tempName;
    seventhScoreFB.text = tempScore; 
    idPos7 = idPos8; //change id position references
    //repeat for position 9 and 8
    tempName = ninethNameFB.text;          
    tempScore = ninethScoreFB.text;
    rank = ninethRank.text;
    eighthRank.text = rank;
    eighthNameFB.text = tempName;
    eighthScoreFB.text = tempScore; 
    idPos8 = idPos9; //change id position references
    //repeat for position 10 and 9
    tempName = tenthNameFB.text;          
    tempScore = tenthScoreFB.text;
    rank = tenthRank.text;
    ninethRank.text = rank;
    ninethNameFB.text = tempName;
    ninethScoreFB.text = tempScore;
    idPos9 = idPos10; //change id position references

    //update the tenth name with the current object
    tenthNameFB.text = name;
    tenthScoreFB.text = "" + entry["score"];
    tenthRank.text = "#" + (i+1);
    idPos10 = userId; //take the id for reference from the object
}


//if the player was not in the top 10 we need to see that their data is stored at //position 8 in the table to stop any further changes, 2 players above and 2 //below. If there are not 2 players below, the table will stop updating due to //there not being anymore objects in our foreach() loop
if (i>9) {
    if (myScore == eighthScoreFB.text)
    {

        //break needed as player is outside top 10
        Divider.SetActive(true);
        //sorting is complete
        tableFormed = true;
    }
}



//table sorting is complete, now to get profile pictures
if (tableFormed)
{
    if (!getProfilePics)
    {
        StartCoroutine(getFacebookProfile(0));
        StartCoroutine(getFacebookProfile(1));
        StartCoroutine(getFacebookProfile(2));

.....

IEnumerator getFacebookProfile(int key)
{
    if (key==0) //use the key to see which position to assign the texture

    {
        string url = "http://graph.facebook.com/" + idPos1 + "/picture";
        WWW www = new WWW(url);
        yield return www;
        Pic1Script.image = www.texture;
    }
    if (key==1) 

    {
        string url = "http://graph.facebook.com/" + idPos2 + "/picture";
        WWW www = new WWW(url);
        yield return www;
        Pic2Script.image = www.texture;
    }
    if (key==2) 

    {
        string url = "http://graph.facebook.com/" + idPos3 + "/picture";
        WWW www = new WWW(url);
        yield return www;
        Pic3Script.image = www.texture;

.....

//we need to check if we need to remove any names if the player is in the top 10
//do this by sending the lastPos value which should take the value of i+1 at the //end of each foreach() loop
public void removeNames (int j)
{

    //if j is 1, we want to remove the rest of the entries as well so check if       //doTheRest is true
    if (j == 1 || doTheRest) 
    {
        //remove all text and set objects renders to false
        firstRank.text = "";
        firstNameFB.text = "";
        firstScoreFB.text = "";
        pic1.SetActive(false);
        Pic1Script.image = null;
        doTheRest = true; 

        //it was true, so do the others so we don't need a new key value
    }
    if (j == 2 || doTheRest) //repeat process for all the positions possible

    {
        secondRank.text = "";
        secondNameFB.text = "";
        secondScoreFB.text = "";
        pic2.SetActive(false);

        Pic2Script.image = null;
        doTheRest = true;

    }
    if (j == 3 || doTheRest)
    {
        thirdRank.text = "";
        thirdNameFB.text = "";
        thirdScoreFB.text = "";
        pic3.SetActive(false);
        Pic3Script.image = null;
        doTheRest = true;
    }

....

I am aware that this may not be the best way to get high scores and put them in a table, I am open to any improvements that you may offer to my code, as I said I am relatively new to programming

7 comments:

  1. Hi Felix,

    Thank you for posting this technical post.
    I am working through posting my Unity game's high score to Facebook.

    I encounter a problem with your code sample (below) if Facebook does not yet have a score stored, the code will not post a score.
    Did you create a solution for the first time a score is posted to Facebook? Something like if(Facebook scores api score does not exist) {post score}. I don't know how to do this.

    Thank you again for this post, it seems to be the only focused documentation for this process I could find.

    Steve


    //Your code sample

    if (currentScore > playerHighScore)
    {
    setMyScore();
    }

    ReplyDelete
    Replies
    1. Hi Steve,
      You are very welcome, I myself struggled for example code.
      You make a good point, when I was implementing my code, I was using the Graph API explorer and posting a score using a HTTP POST of 'score' to me/scores. This meant there was already a score present.

      In your case i think it should return that your current score will be set to 0 if none is set so the code above should work. but if not you can check to see if the players' score has been post yet using something like;

      bool playerScoreFound = false;
      ...
      foreach(object score in scoresList)
      {
      [*Do some code*]
      if (currentScore > playerHighScore)
      {
      setMyScore();
      playerScoreFound = true;
      }//end if
      [*Other code*]
      }//end foreach
      if (!playerScoreFound)
      setMyScore();

      I believe that should do the job.

      Delete
    2. Firstly it may be worth looking at the graph api for a new player who has not got a score set, and see if it does indeed return 0 as their score.

      Delete
    3. Thanks for the reply Felix,
      I am looking into your suggestions now. I appreciate your time and effort in responding.

      Delete
    4. Hi Felix,

      The answer to your question 'will the Facebook Score API return 0, if no score exists'. Here are the results.



      //If no score exists
      {
      "data": [
      ]
      }




      //If a score exists
      {
      "data": [
      {
      "user": {
      "id": "8888888888",
      "name": "XXXXX XXXXXX"
      },
      "score": 48,
      "application": {
      "name": "XXXX XXXXXX XXXXXX",
      "namespace": "xxx",
      "id": "888888888888888888"
      }
      }
      ]
      }

      Delete
    5. Felix, your suggestion works.
      If no game score exists, the Score API returns:

      {
      "data": [
      ]
      }


      Therefore I placed your bool suggestion after:

      if (string.Equals(userId,FB.UserId))
      {
      fbScoreExists = true;
      ...

      It works.
      Thanks Felix!

      Delete