Unity GameCenter Support

I’ve spend the last couple of days implementing GameCenter support in a Unity game and found more than a few pitfalls all of which goes back to a seriously broken Unity API.

So, if you are planning on using the Social API in Unity3D, here’s a couple of landmines you may not want to step on (I’m sure there are others, but these are the ones that I wasted time on) – oh, and just for the record

1) I love Unity – it’s an awesome platform. It’s just their GameCenter implementation of the Social API that could do with a bit of love 🙂

2) These issues were found in Unity 4.5 (but Google suggests they have been there forever, so I would not count on them being fixed anytime soon)

Loading scores

The first thing that bit me is that ILeaderBoard.LoadScores only supports a single request at a time. If you try to call it multiple times, only the last invocation will receive a callback (several times, whatever good that will do you). I implemented the following helper to work around the issue:

  struct LoadScoreRequest
  {
    public string            _id;
    public string[]          _userIds;
    public BoardLoadedEvent  _callback;
  }

  public delegate void BoardLoadedEvent(ILeaderboard board, HighScore[] highScore);
  
  private bool _friendsLoaded;
  private bool _loadScoreRequestPending;
  private List<LoadScoreRequest> _loadScoreRequests = new List<LoadScoreRequest>();

  public void CreateAndLoadLeaderBoard(string id, string[]userids, BoardLoadedEvent ondone)
  {
    lock(_loadScoreRequests)
    {
      _loadScoreRequests.Add( new LoadScoreRequest() { _id=id, _userIds = userids, _callback = ondone }  );
      SendNextScoreRequest();
    }
  }

  private void SendNextScoreRequest()
  {
    LoadScoreRequest req;
    lock(_loadScoreRequests)
    {
      if(!_friendsLoaded)
        return;

      if(_loadScoreRequestPending)
        return;

      if(_loadScoreRequests.Count==0)
        return;

      _loadScoreRequestPending = true;
      req = _loadScoreRequests[0];
      _loadScoreRequests.RemoveAt(0);
    }

    if(req._userIds==null)
    {
      req._userIds = new string[Social.localUser.friends.Length];
      int i=0;
      foreach(IUserProfile friend in Social.localUser.friends)
      {
        req._userIds[i++] = friend.id;
      }
    }
    
    ILeaderboard board = Social.CreateLeaderboard();
    board.id = req._id;
    board.SetUserFilter(req._userIds);

    board.LoadScores( (bool scoresloaded) =>
    {
      req._callback(board);
      lock(_loadScoreRequests)
      {
        _loadScoreRequestPending = false;
        SendNextScoreRequest();
      }
    });
  }

Basically, it queues up all requests until the GameCenter authentication has been completed and the friends list has been loaded. It then starts loading the leaderboards one at a time, making sure each board gets returned to the proper callback.

The authentication code is not shown above, but it’s straight forward and simply sets the _friendsLoaded member to “true” after loading the friends list and then calls SendNextScoreRequest in case any requests got queued up while authenticating.

Saving scores

IScore.ReportScore does not work.

At least, I could not get it to work. Use Social.ReportScore() instead as it seems to work as advertised, and has the added advantage that you don’t need to carry around the ILeaderboard reference. Why the broken method is even in the public API I can only guess at.

Error handling

Basically, there isn’t any.

At least, you can forget about checking the boolean “success” parameter provided in the various callbacks. I’ve never seen this have a value other than “true” despite a myriad of issues, none of which I’d personally characterise as a “success”.

Instead, check that the data you expected is available – for example, that the user- or highscore-lists are not empty.

UPDATE & WARNING:

Be really careful with Social.LoadAchievementDescriptions – it will load the associated images for all achievements and keep them in memory. They will not be released again.

In my case it amounted to 12 images of 1024×1024 pixels which, with some overhead of unknown origin, amounted to 96MB each time the game refreshed the achievements list.

There’s a similar problem with loading user profiles – their profile images are also kept in memory and are not released when you let go of the profile itself.

12 thoughts on “Unity GameCenter Support

    • On iOS, yapp. But you need to create several test users and make them friends of each other to get it to work in the sandbox – pain in the neck to set up.

      • For LoadUsers()? You’re just supposed to pass a list of IDs to it. What does that have to do with friends? Is there good documentation about how to do what you’re talking about (creating test users and friending them)?

      • No, sorry, I didn’t read your question properly – I was thinking of “LoadFriends” which is what I have working.

        I think the only way to create “friends” in the sandbox is to log in with one test-profile, send a friend request, log out and back in with another profile and then accept the request (and this is iOS – nothing works in the editor AFAIK, but then again, I have made no effort to make it work either 🙂 ).

      • LoadFriends is very close to LoadUsers. Why won’t LoadUsers work? (I always get 0 profiles returned to the callback.) 😦

        Have you been able to use the .image from the profiles as a Texture2D and display it? I’m getting a 76x76px image, but it’s not displaying.

      • How are you obtaining the ID’s you use for LoadUsers? Are you sure they exist in the sandbox? Other than that, I don’t know – as noted above, I found the implementation of the Social API somewhat buggy, so it would not surprise me if it simply didn’t work 😉

        Yes, I’ve been able to get images – they do not work in the sandbox though. I seem to recall something about only getting the image of the logged in user, and that this image was reset as soon as the player logged out, but the details escapes me. We eventually decided not to use the picture since very few profiles has one.

  1. I obtain the ids from the other users of the app and store them in a database, so I know they exist.

    So maybe the images will work once the app is in production?? That wouldn’t be terrible, just untestable.

    • Are those user IDs test-users (sandbox) or real users? You cannot look up test-users in the live GameCenter nor real users in the sandbox.

      Images should work once you go live *if* they work for the logged in user in sandbox mode (To test, you have to set a profile picture on the local test device after logging in to GameCenter (sandbox). Note that the image will be lost when the sandbox user is logged out again).

      And yes, testing this sucks 🙂

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s