A Brief Scoreloop Integration Tutorial
by Craig Fortune · in iTorque 2D · 10/18/2009 (12:57 pm) · 43 replies
Quite a few people seem to be struggling with integrating Scoreloop in to their projects, which is a shame as it is a great little system. Here is a quick little tutorial on how to do it. This is barebones and simply gets you “on your feet” with working with Scoreloop.
DISCLAIMER: This was very quickly typed up (had to strip some of my own bits out) so I have probably missed some bits etc, if you spot them please let me know so I can update these instructions for other people. Thanks. (Also, you'll probably see some weird/odd comments - ignore those, this is just ripped out from my working build for my game :D)
First off, I’m assuming you have followed the instructions as part of the Scoreloop download , if not, go do those steps first!
TGBAppDelegate.h needs to be changed, first up is to add the include - I have my Scooreloop directory inside my XCode_iPhone directory alongside my project file)
the @interface part needs the protocols it abides to changing too, like thus:
Also, in the @interface part add the following to add the score loop client object in.
and add a property below like thus:
Now in TGBAppDelegate.mm make the following changes and additions:
Add scoreloopClient to our synthesize list
In - (void)applicationDidFinishLaunching:(UIApplication *)application
Add the following two lines: (these are features in Scoreloop, read the docs for more info)
- (id) init
Should be changed to look like the following: (this setups up scoreloop - NOTE THE BITS YOU NEED TO CHANGE!)
Add the following: (The Con::executef stuff is because I like to handle changes with Scoreloop status in script – they are just global script functions – always handy to have callbacks etc :D)
Now create ScoreloopHandling.h inside the same folder as TGBAppDelegate with the following code:
Here is the matching .mm file…
The above files are your glue between your scoreloop and your game, lets add the consolemethods to actually utilize them now. In ConsoleFunctions.cc add the following right at the bottom.
You need to add this to the top of ConsoleFunctions.cc too...
You can now call the functions from script. For instance you could have a button that calls showScoreLoopGUI(); to load up scoreloop, or when it is game over you can call endGameWithScore(%playerScore);
DISCLAIMER: This was very quickly typed up (had to strip some of my own bits out) so I have probably missed some bits etc, if you spot them please let me know so I can update these instructions for other people. Thanks. (Also, you'll probably see some weird/odd comments - ignore those, this is just ripped out from my working build for my game :D)
First off, I’m assuming you have followed the instructions as part of the Scoreloop download , if not, go do those steps first!
TGBAppDelegate.h needs to be changed, first up is to add the include - I have my Scooreloop directory inside my XCode_iPhone directory alongside my project file)
#import <Scoreloop/Scoreloop.h>
the @interface part needs the protocols it abides to changing too, like thus:
@interface TGBAppDelegate : NSObject <UIApplicationDelegate, SLClientDelegate>
Also, in the @interface part add the following to add the score loop client object in.
id<SLClient> scoreloopClient;
and add a property below like thus:
@property (nonatomic, retain) id<SLClient> scoreloopClient;
Now in TGBAppDelegate.mm make the following changes and additions:
Add scoreloopClient to our synthesize list
@synthesize window, scoreloopClient;
In - (void)applicationDidFinishLaunching:(UIApplication *)application
Add the following two lines: (these are features in Scoreloop, read the docs for more info)
[scoreloopClient setFeature:kSLFeatureTeaserStrategy enabled: NO]; [scoreloopClient setFeature:kSLFeatureAutorotateInterface enabled: YES];
- (id) init
Should be changed to look like the following: (this setups up scoreloop - NOTE THE BITS YOU NEED TO CHANGE!)
- (id) init
{
self = [super init];
if (self != nil)
{
// Create the scoreloop client.
// The init method of the Application Delegate is
// the recommended place
scoreloopClient = SLClientCreateWithGameAndDelegate(
@" GAME ID FROM SCORELOOP GOES HERE!!!!! ",
@" SECRET FROM SCORELOOP GOES HERE!!!!! ",
self);
}
return self;
}Add the following: (The Con::executef stuff is because I like to handle changes with Scoreloop status in script – they are just global script functions – always handy to have callbacks etc :D)
- (BOOL) application:(UIApplication *)application handleOpenURL:(NSURL *)url
{
// let Scoreloop know that another app started the game.
// If Scoreloop can't handle the URL, it will return NO
// This is important to implement as this enables starting
// challenges from other applications
return [scoreloopClient handleOpenURL:url];
}
- (void) scoreloopDidShow:(id<SLClient>) aClient
{
// Scoreloop UI Active and showing
Con::executef(1, "onScoreloopDidShow");
}
- (void) userDidFinishScoreloop:(id<SLClient>) aClient usingContext:(NSDictionary*) aContext
{
// User successfully finished with Scoreloop UI
// WITH CHALLENGE
Con::executef(2, "onUserDidFinishScoreloop", "");
}
- (void) userDidCancelScoreloop:(id<SLClient>) aClient
{
// User unsuccessfully finished with Scoreloop UI
// NO CHALLENGE
Con::executef(1, "onUserDidCancelScoreloop");
}
- (BOOL)client:(id) aClient shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return YES;
}Now create ScoreloopHandling.h inside the same folder as TGBAppDelegate with the following code:
//
// ScoreLoopHandling.h
// Torque2D
//
// Created by Craig Fortune on 07/08/2009.
// Copyright 2009 __MyCompanyName__. All rights reserved.
//
#ifndef SCORELOOPHANDLING_INCLUDED
#define SCORELOOPHANDLING_INCLUDED
namespace ScoreLoopHandling
{
void submitScore(int score);
void showScoreLoop();
}
#endifHere is the matching .mm file…
#include "platformiPhone/ScoreLoopHandling.h"
#import <Scoreloop/Scoreloop.h>
void ScoreLoopHandling::submitScore(int score)
{
id<SLClient> scoreloopClient = [(id)[UIApplication sharedApplication].delegate scoreloopClient];
NSNumber* scoreResult;
scoreResult = [NSNumber numberWithInt: score];
// for advanced game play (levels...) you can pass a context
NSDictionary* context = nil;
[scoreloopClient gameDidEndWithScoreResult: scoreResult usingContext: context];
}
void ScoreLoopHandling::showScoreLoop()
{
id<SLClient> scoreloopClient = [(id)[UIApplication sharedApplication].delegate scoreloopClient];
[scoreloopClient show: kSLTabOverview];
}The above files are your glue between your scoreloop and your game, lets add the consolemethods to actually utilize them now. In ConsoleFunctions.cc add the following right at the bottom.
ConsoleFunctionGroupBegin(Scoreloop, "Functions for dealing with Scoreloop");
ConsoleFunction( endGameWithScore, void, 2, 2, "")
{
argc; argv;
int score = dAtoi(argv[1]);
ScoreLoopHandling::submitScore(score);
}
ConsoleFunction( showScoreLoopGUI, void, 1, 1, "")
{
ScoreLoopHandling::showScoreLoop();
}
ConsoleFunctionGroupEnd(Scoreloop);You need to add this to the top of ConsoleFunctions.cc too...
#include "platformiPhone/ScoreLoopHandling.h"
You can now call the functions from script. For instance you could have a button that calls showScoreLoopGUI(); to load up scoreloop, or when it is game over you can call endGameWithScore(%playerScore);
#22
"DefaultFormat" = "%R%r %S %m %M";
"ModeOnlyFormat" = "%m %M";
"mode:0" = "Beginner";
"mode:1" = "Advanced";
"mode:2" = "Expert";
This way when you are looking at the scoreboard. there is a button that's called "game modes" and when you click it, it gives you options to all the different game modes. these buttons were blank before I edit this file.
10/19/2009 (6:00 pm)
I ended up changing my SLScoreFormatter.strings to:"DefaultFormat" = "%R%r %S %m %M";
"ModeOnlyFormat" = "%m %M";
"mode:0" = "Beginner";
"mode:1" = "Advanced";
"mode:2" = "Expert";
This way when you are looking at the scoreboard. there is a button that's called "game modes" and when you click it, it gives you options to all the different game modes. these buttons were blank before I edit this file.
#23
10/19/2009 (6:19 pm)
Where is the button called "game modes" I don't see it anywhere? :/
#24
I did forget to mention that I've also added the following line to appDelegate. this sets the range of game modes. and also in admin.scoreloop.com there is a place in advanced options to set the mode Count. maybe the button doesn't show up until you set it.
10/19/2009 (6:37 pm)
when you click on High Score you should be able to see the button on the bottom of the screen, between the Location icon and the two pages icon (black on white).I did forget to mention that I've also added the following line to appDelegate. this sets the range of game modes. and also in admin.scoreloop.com there is a place in advanced options to set the mode Count. maybe the button doesn't show up until you set it.
- (id) init
{
self = [super init];
if (self != nil)
{
// Create the scoreloop client.
// The init method of the Application Delegate is
// the recommended place
scoreloopClient = SLClientCreateWithGameAndDelegate(
@"",
@"w",
self);
scoreloopClient.gameModes = NSMakeRange(0,3);
}
return self;
}
#25
10/19/2009 (6:53 pm)
Figured it out right after posting my last comment! (Hate it when that happens) Thanks anyway, you were correct. :)
#26
the bundle is only 1.1Mb , but you need to add the additional frameworks that adds up.
There's definitely a price for this cool gadget. but I'm keeping it anyway.
10/19/2009 (8:12 pm)
just an FYI. my app bundle size before I zip it went up from 12.8Mb to 15.7Mb . the zipped app went up from 8.3Mb to 9.5Mb which will put me over the 10Mb wifi limit , since our zip is better than the one Apple is using in the appStore.the bundle is only 1.1Mb , but you need to add the additional frameworks that adds up.
There's definitely a price for this cool gadget. but I'm keeping it anyway.
#27
Thats right :), it does feel a little dirty but it worked, and stopped my crashes from happening!
10/20/2009 (4:36 am)
@CraigQuote:The if statement you mention I assume just keeps hold of a bool you set when displaying an advert right? A neat and straight forward little way of handling that situation, heh - even if it just feel a bit nasty :)
Thats right :), it does feel a little dirty but it worked, and stopped my crashes from happening!
#28
@Eval Erez:
You can further reduce the size by removing unneeded(wanted) language translations from the Scoreloop.bundle. Just delete es.lproj or/and de.lproj.
Greetings
Nikwest
10/20/2009 (7:03 am)
@Craig: thanks for the great write up!@Eval Erez:
You can further reduce the size by removing unneeded(wanted) language translations from the Scoreloop.bundle. Just delete es.lproj or/and de.lproj.
Greetings
Nikwest
#29
No, my secret and id is fine in delegate file, but game still cant load.
Please, can you send me edited .mm and .h files to flymyxa@gmail.com.
10/20/2009 (7:13 am)
@Eyal:No, my secret and id is fine in delegate file, but game still cant load.
Please, can you send me edited .mm and .h files to flymyxa@gmail.com.
#30
Can you provide a little snippet to show us how you did it?
10/20/2009 (12:31 pm)
John, it sounds like passing on the main loop would be a good idea for scoreloop too. I had it crashed once and I suspect it's the game running in the background.Can you provide a little snippet to show us how you did it?
#31
isBigAd() is a function which checks if a Greystripe Ad is currently displayed.
Not the best method of stoping rendering calls, not even sure if it does stop them, it stopped crashes though. :D.
10/20/2009 (2:37 pm)
Its quite simple really, just compare this function and your own in iPhoneMain.mm:void _iPhoneGameInnerLoop()
{
if(Game->isRunning()){
S32 start = Platform::getRealMilliseconds();
if( isBigAd() == false )
{
Game->mainLoop();
}
S32 time = sgTimeManagerProcessInterval - (start - gLastStart);
gLastStart = start;
iPhoneRunEventLoopTimer(time);
}
else
{
Game->mainShutdown();
}
}isBigAd() is a function which checks if a Greystripe Ad is currently displayed.
Not the best method of stoping rendering calls, not even sure if it does stop them, it stopped crashes though. :D.
#32
Basically I've added too functions to my script that will change the global variable.
function onScoreloopDidShow()
{
$pref::iPhone::isSL = true;
}
function onUserDidCancelScoreloop()
{
$pref::iPhone::isSL = false;
}
Then I'm trying to read the global variable using Con::getVariable("$pref::iPhone::isSL"); and based on that determine if I should run Game->mainLoop();
However, when I run the program , I get a blank screen. probably the program never goes into the main loop. Any ideas how to do it?
10/20/2009 (4:53 pm)
So I'm trying to do similar thing for scoreloop but having problems since _iPhoneGameInnerLoop() is one of the first functions to run. Basically I've added too functions to my script that will change the global variable.
function onScoreloopDidShow()
{
$pref::iPhone::isSL = true;
}
function onUserDidCancelScoreloop()
{
$pref::iPhone::isSL = false;
}
Then I'm trying to read the global variable using Con::getVariable("$pref::iPhone::isSL"); and based on that determine if I should run Game->mainLoop();
However, when I run the program , I get a blank screen. probably the program never goes into the main loop. Any ideas how to do it?
#33
10/20/2009 (4:59 pm)
What is the default value you are setting on that var? I'm guessing it isn't "true" :)
#34
With the lack of a better idea I tried that, but same result.
bool isScoreloop()
{
bool isSL = false;
bool isSLVar = false;
isSLVar = Con::getVariable("$pref::iPhone::isSL");
if(isSLVar == true) isSL=true;
return isSL;
}
S32 gLastStart = 0;
void _iPhoneGameInnerLoop()
{
if(Game->isRunning()){
S32 start = Platform::getRealMilliseconds();
if( isScoreloop() == false)
{
Game->mainLoop();
}
S32 time = sgTimeManagerProcessInterval - (start - gLastStart);
gLastStart = start;
iPhoneRunEventLoopTimer(time);
}
else
{
Game->mainShutdown();
}
}
10/20/2009 (5:22 pm)
yes, it is false. but I'm not sure if at that point it is even set.With the lack of a better idea I tried that, but same result.
bool isScoreloop()
{
bool isSL = false;
bool isSLVar = false;
isSLVar = Con::getVariable("$pref::iPhone::isSL");
if(isSLVar == true) isSL=true;
return isSL;
}
S32 gLastStart = 0;
void _iPhoneGameInnerLoop()
{
if(Game->isRunning()){
S32 start = Platform::getRealMilliseconds();
if( isScoreloop() == false)
{
Game->mainLoop();
}
S32 time = sgTimeManagerProcessInterval - (start - gLastStart);
gLastStart = start;
iPhoneRunEventLoopTimer(time);
}
else
{
Game->mainShutdown();
}
}
#35
10/20/2009 (6:28 pm)
On a different issue. since the GAME ID and SECRET are hardcoded into the source. I basically need to make a copy of the whole project in for each game (even just a lite version). What would be an easy way to reference this strings from an external file? can it be done using the info.plist?
#36
10/20/2009 (6:48 pm)
#Ifdef would work just fine no?
#37
#ifdef for the Game->mainLoop(); issue or for the separate game id/secret?
Can you expand?
10/20/2009 (8:11 pm)
sorry, not sure I follow.#ifdef for the Game->mainLoop(); issue or for the separate game id/secret?
Can you expand?
#38
Then start it up like this:
Just have a different secrets.h for each game. Regular C stuff.
10/20/2009 (8:41 pm)
A couple of defines in, say, secrets.h, maybe?#ifdef LITE_VERSION
#define SCORELOOP_ID @"id for the lite version"
#define SCORELOOP_SECRET *"secret here"
#else
#define SCORELOOP_ID @"id for the full version"
#define SCORELOOP_SECRET *"secret here"
#endifThen start it up like this:
scoreloopClient = SLClientCreateWithGameAndDelegate(SCORELOOP_ID, SCORELOOP_SECRET, self);
Just have a different secrets.h for each game. Regular C stuff.
#39
I've just used a console function which gets called from script and returns the C variable when I've needed the variable in script.
Thats all I can really tell you since its all I've done. I'm guessing its probably the worst way to fix the problem but it worked fine for me.
10/21/2009 (4:59 am)
@Eyal: I've used a variable in the C code rather than getting the variable from the Scripts. I'm sure thats the issue, as you say, since the variable probably won't be created yet.I've just used a console function which gets called from script and returns the C variable when I've needed the variable in script.
Thats all I can really tell you since its all I've done. I'm guessing its probably the worst way to fix the problem but it worked fine for me.
#40
Your trick is actually working great. it is a lot faster now to load and reload the scoreloop gui's. Thanks.
10/21/2009 (12:01 pm)
John, that worked. I've changed it to c variable.Your trick is actually working great. it is a lot faster now to load and reload the scoreloop gui's. Thanks.
Associate Craig Fortune