Tag Archives: objective-c

A More Robust Game Center Availability Check

I just wanted to share the small addition I made to the isGameCenterAvailable() function Apple provides for checking to see if Game Center is available/supported by a particular device.

The reason this is necessary is because the iPhone 3G supports iOS 4.1+ however it does not support Game Center, therefore the Apple-provided isGameCenterAvailable() will say GC is supported, when it is in fact, not.

Enjoy!

Edit: Some people have questioned why I should do this, especially when Apple themselves recommend against it. This is because the current Game Center login dialog as presented in-game when calling authenticateWithCompletionHandler has absolutely no information as to the purpose of the login. While most people will rightfully assume it’s for Game Center, there are still a number of reports of users getting angry at developers thinking that they are trying to steal their login information. So the idea here is to use this code in conjunction with a friendly dialog explaining the impending Game Center login and how awesome it is, thus reducing the potential for user confusion. The reason you want to check for the 3G is so you don’t present your “I’m about to log in to Game Center” dialog and then… not log in to Game Center.

If the situation with the dialog improves, or the 3G is ever graced with Game Center support, it is easy enough to update your game to take out the check for the device.

#import <GameKit/GameKit.h>
#import <sys/utsname.h>
 
bool isGameCenterAvailable()
{
	// Check for presence of GKLocalPlayer API.
	Class gcClass = (NSClassFromString(@"GKLocalPlayer"));
	// The device must be running running iOS 4.1 or later.
	NSString *reqSysVer = @"4.1";
	NSString *currSysVer = [[UIDevice currentDevice] systemVersion];
	bool osVersionSupported = ([currSysVer compare:reqSysVer options:NSNumericSearch] != NSOrderedAscending);
 
	if(gcClass && osVersionSupported)
	{
		struct utsname systemInfo;
		uname(&systemInfo);
		//iPhone 3G can run iOS 4.1 but does not support Game Center
		if(strcmp(systemInfo.machine, "iPhone1,2") == 0)
		{
			return false;
		}else{
			return true;
		}
	}else{
		return false;
	}
}

Retina Display, Open GL, and You!

Over the long weekend, I took some time to code support for the iPad and iPhone 4′s Retina Display into Red Nova. It was a fairly painless process, after Paul Pridham pointed me in the right direction.

If you are using OpenGL for your app on the iPhone and are using an Orthographic projection for your 2D bits, you shouldn’t have to change much of your code to get it “Retina Ready”.

Setting the View Scale Factor

First, your Open GL View class if you add the following in your init code it will tell the OS that you want to display your graphics at 960×640:

if([[UIScreen mainScreen] respondsToSelector: NSSelectorFromString(@"scale")])
{
	if([self respondsToSelector: NSSelectorFromString(@"contentScaleFactor")])
	{
		self.contentScaleFactor = [[UIScreen mainScreen] scale];
	}
}

If you run your app at this point you should see the content running in the corner of the screen because your GL viewport is still running at 480×320. So that brings us to…

Setting up the Open GL Viewport

Note: If you haven’t already you may want to make a wrapper function that returns the current device scale factor. I did and it’s very useful in code that needs to properly deal with displaying your graphics at the right size.

Anyway, this is the next and pretty much final step.

Find your call to glViewport, and modify it thusly:

glViewport(0,0,usefulGetScreenWidthHelper()*usefulGetScreenScaleHelper(),
	usefulGetScreenHeightHelper()*usefulGetScreenScaleHelper());

Obviously change the width and the height to correspond to your app’s layout.

If the rest of your code sets up your projections (2D and 3D) based on 480×320, when you compile and run your app you should get glorious retina display goodness! You will of course have to adjust your 2D bitmapped assets (fonts and images) to reflect the higher resolution of the display, but the end result is that your app thinks in 480×320 (this is how Apple manages it with Cocoa Touch, as far as I can tell, amusingly enough) but displays at 960×640!

Spinning Wheels

Much progress was to be had over the weekend, also much frustration… but my game code pretty much works on my iPod now! I’m so excited :D

First of all, I’ve decided to create a new law. Let’s call this law Colin’s First Law of New Platform Development. It is thus: “When developing on a new platform and your code fails in a spectacular fashion, don’t assume it is the platform’s fault for being new and strange. It is in fact because you’re an idiot and didn’t initialize a variable properly.” I’ll just go ahead and leave this here.

In other news, I will now tell you how to make a fairly decent method to shuttle input from the main thread to your game thread when working on the iPhone OS. It took me a while to hunt it down, so I’m sure if this ever shows up in Teh Googles someone might find it useful.

First of all, I created a class which is basically a FIFO of “event” objects. It’s pretty basic, but it will do the trick.

Initially, I figured all I needed was a boolean variable in the FIFO to let the system know if the FIFO was busy. Boy howdy was I wrong. You see, I discovered that the variable itself was being accessed simultaneously by the view controller and the game code causing it to crash horribly. So I’m like… clearly people do this sort of thing all the time, I just need to keep searching until I find a mechanism that allows this sort of thing. After some searching I learned about atomic operations, or operations that are guaranteed to happen within one processor cycle. This guarantees that only one thread will be operating on the variable at one time, thus preventing Michael Bay levels of explosive action in your code.

So, where do we find these atomic operations? Thankfully Apple has thoughtfully included some in the OS. I’m sure there are more elsewhere, but for my code I decided to use the functions in the scarily located <libkern/OSAtomic.h>. Really though, it’s not so scary! What we’re going to do is use a spinlock to resolve resource contention (the resource being our FIFO). Basically we give our game code and the view controller a pointer to an OSSpinLock variable and they both use OSSpinLockLock(OSSpinLock *); to fight it out for who gets first crack at the buffer. The reason I chose OSAtomic functions is because they are relatively lightweight, and this sort of resource sharing is fairly simple.

Anyway! To get this to work, you need to declare an OSSpinLock variable (it’s basically an integer) somewhere and pass a pointer to it to your view controller and your game code. Also you have to do the same with your event queue object. Because your view controller will probably be trying to send touch events before your game code has fully initialized, it is best to have the view controller call the functions to init the spinlock and the event queue.

A word of warning, most of my game code is in C++, as I’m really not very comfortable with Obj-C atm, but C++ code will happily co-exist with your Obj-C code. The only major caveat is if you’re calling C++ inside your Obj-C objects, the implementation files have to be .mm, not just .m so they are compiled as Objective-C++. Otherwise the compiler will just sit there and stare at you funny. This spinlock method should work with straight Obj-C too though!

Okay so I’ve created a few simple functions (in C!) to initialize and pass around the spinlock and the event queue. They live in eventsync.cpp:

#include "eventsync.h" //This includes OSAtomic.h and the header for the event queue
 
static OSSpinLock *lock; //static so it never goes away
static CGEventQueue *uiEventQueue; //same here
 
CGEventQueue *getEventQueue()
{
  return uiEventQueue;
}
CGEventQueue *getEventQueueAndInit()
{
   uiEventQueue = new CGEventQueue;
   return uiEventQueue;
}
OSSpinLock *getSpinLockAndInit()
{
   lock = OS_SPINLOCK_INIT;
   return &lock;
}
OSSpinLock *getSpinLock()
{
   return &lock;
}

Now that that’s sorted out it’s pretty easy to get started. In your view’s interface add a pointer for an OSSpinLock and your queue object. Then in the view’s init code (in my case initWithCoder), call getSpinLockAndInit() and getEventQueueAndInit() to set up your view’s pointers (in this case, lock and eventQueue). In your game code you’ll want to call getSpinLock() and getEventQueue().

WARNING: Stargate references abound. Now that you’ve spanned your two threads with the code equivalent of a wormhole, just like in Stargate, they can only work in one direction. So your spinlock is going to act like a DHD and will activate the wormhole and send SG-1 in the right direction, hopefully avoiding killing some hapless team member in the process.

Basically what we will do is use OSSpinLockLock() to try and set the spinlock variable. If the spinlock is already locked, it is in use somewhere else, so OSSpinLockLock() will loop until it is no longer locked. It is our responsibility to unlock the spinlock in each thread when we’re done with it or everything will stop working.

So before this post gets even longer, here’s the upshot. When we overload touchesBegan, touchesMoved, and touchesEnded (or other input methods) we’re going to do something like this:

-(void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
   UITouch *touch = [[event allTouches] anyObject];
   CGPoint touchLocation = [touch locationInView:touch.view];
 
   OSSpinLockLock(lock);
   //when the lock is free we can safely push events into the queue
   TouchEvent *ev = new TouchEvent;
   ev->type = CGE_TOUCHDOWN;
   ev->x = touchLocation.x;
   ev->y = touchLocation.y;
   eventQueue->pushEvent(ev);
   OSSpinLockUnlock(lock);//important to release the lock!
}

In our game code we’d have something like:

OSSpinLockLock(lock);
TouchEvent *ev;
while(eventQueue->getNumEvents())
{
   switch(eventQueue->topEvent()->type)
   {
      case CGE_TOUCHDOWN:
         ev = static_cast<TouchEvent *>(eventQueue->topEvent());
         //do stuff with the event
         break;
      }
      //Handle other events here
      eventQueue->delTopEvent();
   }
}
OSSpinLockUnlock(lock);

It’s as simple as that!

Til next time :)