What Caught Our Eye This Week – August 16 to August 22, 2009

Aug 23, 2009   //   by Derek van Vliet   //   Weekly Recap  //  No Comments

Here are some of the stories that we shared with each other this week:

  • Step by step instructions for handset makers to leverage the Android platform to overtake Apple’s iPhone juggernaut.
  • The iPhone overtook the Canon Rebel XTi as the #1 camera on Flickr, according to their own stats. As an avid Flickr user who owns both of these products, it is no surprise to me, as an increasing number of pictures in my own feed has come from my iPhone.
  • TomTom released their long awaited GPS app for the iPhone and broke the $0.99 pricing model that most users are used to in the process, asking $100 for it.
  • The uproar over the Google Voice app rejection continued. The WSJ had an oped explaining why AT&T was behind the rejection, or why they would at least not object to it. Apple, AT&T and Google all filed responses to the FCC’s questions about the matter late in the day on Friday. Michael Arrington tried to make sense of it all and concluded that we will see Google Voice on the iPhone before all is said and done.
  • Jeff Atwood explained why Microsoft Bob is the only truly failed project.

Join our FriendFeed group to discuss these stories and more.

Cocos2D and UIScrollView

Aug 21, 2009   //   by Rob Segal   //   Development  //  127 Comments

Learning now to work with a new framework is usually a challenge.  You often delve into a new API or SDK expecting things to work in a certain manner.  When those expectations fail you need to delve deeper to find your answer in several different ways.  It often starts with visiting the website of the package you are working with.  This often leads to..

All of these approaches have their pros and cons and I have often found its some combination of these sources that will lead you to your answer.  In my case I have been trying over the past couple of weeks to get Cocos2D working with a UIScrollView container.  This is a requirement for the game we are working on to allow players to get the familiar scrolling movement you see in many iPhone apps.

We tried a number of approaches to get this to work properly…

  1. Cocos window embedded inside a UIScrollView instance
  2. Changing the size of the OpenGL rendering window to render a larger space.  Works but there is a serious performance hit.
  3. A UIScrollView instance overlaid on top of a Cocos window.  If the scroll movements were caught they could be passed onto the Cocos window manually.

Ultimately we chose option #3 and this is the option I will cover details for in this article as it is ultimately the only solution which worked for us properly.

Modifications to the Cocos source code

The first step to get this to work properly is a small modification to the Cocos source code.  The reason being that Cocos will freeze while working with a UIScrollView by default.  Luckily the location to you need to modify is noticeably documented in the Cocos source code.  In Director.m look for this block of comment with code…

//
// If you want to attach the opengl view into UIScrollView
// uncomment this line to prevent 'freezing'. It doesn't work on
// with the Fast Director
//
// [[NSRunLoop currentRunLoop] addTimer:animationTimerforMode:NSRunLoopCommonModes];

Simply uncomment this line of code to get freeze free scrolling.  Leave the lines commented out to see the difference in movement as my description may not make it readily apparent.

EDIT: If you are using Cocos 0.99.4 or later use the following directions provided by one of our helpful community members “drootang” for solving the freezing issue…


I’m using 0.99.4 and it took me a while to figure out that the new hello world uses the following line in applicationDidFinishLaunching:

CC_DIRECTOR_INIT();

This macro (among other things) does the following:

if( ! [CCDirector setDirectorType:kCCDirectorTypeDisplayLink] )
[CCDirector setDirectorType:kCCDirectorTypeNSTimer];

You need to force it to set the director type to kCCDirectorTypeNSTimer to prevent cocos2d from “freezing” while scrolling. Either change
the line in the macro, or copy the macro contents to your appDidFinishLaunching method, and change the line above to simply:

[CCDirector setDirectorType:kCCDirectorTypeNSTimer] ;


Using a view controller and a UIScrollView together

Detecting touches in UIScrollView

One of the necessary steps to getting this to work properly would be to detect both touches and swipes in the UIScrollView instance and pass those messages along to the Cocos window.  Researching this in Google revealed the following forum posting which was a huge help.  The solution there is to use a UIViewController where the view in that controller is an instance of UIScrollView.  Here’s what that setup looks like in our codebase…

CocosOverlayViewController.h

#import 
 
@interface CocosOverlayViewController : UIViewController
{
}
@end

CocosOverlayViewController.m

#import "CocosOverlayViewController.h"
#import "CocosOverlayScrollView.h"
 
@implementation CocosOverlayViewController
 
// Implement loadView to create a view hierarchy programmatically, without using a nib.
- (void)loadView
{
  CocosOverlayScrollView *scrollView = [[CocosOverlayScrollView alloc] initWithFrame:[UIScreen mainScreen].applicationFrame];
 
  // NOTE - I have hardcoded the size to 1024x1024 as that is the size of the levels in
  // our game.  Ideally this value would be parameterized or configurable.
  //
  scrollView.contentSize = CGSizeMake(1024, 1024);
 
  scrollView.delegate = scrollView;
  [scrollView setUserInteractionEnabled:TRUE];
  [scrollView setScrollEnabled:TRUE];
 
  self.view = scrollView;
 
  [scrollView release];
}
@end

CocosOverlayScrollView.h

#import 
 
@interface CocosOverlayScrollView : UIScrollView
{
}
@end

CocosOverlayScrollView.m

#import "CocosOverlayScrollView.h"
#import "cocos2d.h"
 
@implementation CocosOverlayScrollView
 
-(void) touchesBegan: (NSSet *) touches withEvent: (UIEvent *) event
{
  if (!self.dragging)
  {
    UITouch* touch = [[touches allObjects] objectAtIndex:0];
    CGPoint location = [touch locationInView: [touch view]];
 
    [self.nextResponder touchesBegan: touches withEvent:event];
    [[[Director sharedDirector] openGLView] touchesBegan:touches withEvent:event];
  }
 
  [super touchesBegan: touches withEvent: event];
}
 
-(void) touchesEnded: (NSSet *) touches withEvent: (UIEvent *) event
{
  if (!self.dragging)
  {
    [self.nextResponder touchesEnded: touches withEvent:event];
    [[[Director sharedDirector] openGLView] touchesEnded:touches withEvent:event];
  }
 
  [super touchesEnded: touches withEvent: event];
}
 
- (void)scrollViewWillBeginDecelerating:(UIScrollView *)scrollView
{
  // TODO - Custom code for handling deceleration of the scroll view
}
 
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
  CGPoint dragPt = [scrollView contentOffset];
 
  Scene* currentScene = [[Director sharedDirector] runningScene];
 
  // Only take the top layer to modify but other layers could be retrieved as well
  //
  Layer* topLayer = (Layer *)[currentScene.children objectAtIndex:0];
 
  dragPt = [[Director sharedDirector] convertCoordinate:dragPt];
 
  dragPt.y = dragPt.y * -1;
  dragPt.x = dragPt.x * -1;
 
  CGPoint newLayerPosition = CGPointMake(dragPt.x + (scrollView.contentSize.height * 0.5f), dragPt.y + (scrollView.contentSize.width * 0.5f));
 
  [topLayer setPosition:newLayerPosition];
}
 
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
{
  CGPoint dragPt = [scrollView contentOffset];
 
}
@end

This is by no means a perfect solution yet as I still have some values hardcoded such as the size of the content for the scroll view window. I will certainly be working to improve the finer points but hopefully the core issues I’ve put together will be useful to some of you.  Our next dev video (when we get it posted) will show the scroll view in action.

Web-Enabled Resource Manager for cocos2d

Aug 19, 2009   //   by Derek van Vliet   //   Development  //  5 Comments

You might have noticed in our recent videos that our game features the ability to load assets from either the device or the web depending on which menu item you select from the main menu.

Early on in the development of our game, we realized how valuable it would be to be able to load image assets from the web instead of from the app bundle. This led us to creating a Resource Manager class that we use to load all of our image assets.

ResourceManager is a singleton class that can be toggled between web mode and local mode. Since it’s a singleton, it is intended to stick around in memory for the duration of the app’s runtime, so we added a cleanup method which can be called to dump everything it is responsible for allocating from time to time. We run the cleanup method after every level.

So far it supports getting images in 3 ways: as a UIImage, as a CGImageRef, or as a cocos2d Texture2D. It is by no means a finished product. We are thinking of adding support for sounds as well. And if you see anything glaringly obvious that can be added or improved, I would love to hear it.

Enough talk, let’s see the code

ResourceManager.h

#import <Foundation/Foundation.h>
#import "cocos2d.h"
 
@interface ResourceManager : NSObject {
	BOOL web;
	NSString * urlPrefix;
	NSMutableArray * images;
	NSMutableArray * textures;
}
 
@property (readwrite) BOOL web;
@property (readwrite,assign) NSString * urlPrefix;
 
+(ResourceManager*)sharedResourceManager;
 
-(void)cleanup;
 
-(CGImageRef)getCGImageRef:(NSString*)fileName;
-(UIImage*)getUIImage:(NSString*)fileName;
-(Texture2D*)getTexture2D:(NSString*)fileName;
 
-(CGImageRef)getCGImageRefFromWeb:(NSString*)url;
-(UIImage*)getUIImageFromWeb:(NSString*)url;
-(Texture2D*)getTexture2DFromWeb:(NSString*)url;
 
-(CGImageRef)getCGImageRefFromAppBundle:(NSString*)fileName;
-(UIImage*)getUIImageFromAppBundle:(NSString*)fileName;
-(Texture2D*)getTexture2DFromAppBundle:(NSString*)fileName;
@end

ResourceManager.m

#import "ResourceManager.h"
 
 
@implementation ResourceManager
 
@synthesize web;
@synthesize urlPrefix;
 
static ResourceManager* _sharedResourceManager = nil;
 
+(ResourceManager*)sharedResourceManager
{
	@synchronized([ResourceManager class])
	{
		if (!_sharedResourceManager)
			[[self alloc] init];
 
		return _sharedResourceManager;
	}
 
	return nil;
}
 
+(id)alloc
{
	@synchronized([ResourceManager class])
	{
		NSAssert(_sharedResourceManager == nil, @"Attempted to allocate a second instance of a singleton.");
		_sharedResourceManager = [super alloc];
		return _sharedResourceManager;
	}
 
	return nil;
}
 
-(id)autorelease {
    return self;
}
 
-(id)init {
	images = [[NSMutableArray alloc] init];
	textures = [[NSMutableArray alloc] init];
	web = NO;
 
	return [super init];
}
 
-(void)cleanup {
	[images removeAllObjects];	
	[textures removeAllObjects];
}
 
-(CGImageRef)getCGImageRef:(NSString*)fileName {
	UIImage * img = [self getUIImage:fileName];
	if (img)
		return [img CGImage];
 
	return nil;
}
 
-(UIImage*)getUIImage:(NSString*)fileName {
	if (web) {
		return [self getUIImageFromWeb:[NSString stringWithFormat:@"%@%@", urlPrefix, fileName]];
	}
	else {
		return [self getUIImageFromAppBundle:fileName];
	}
 
	return nil;
}
 
-(Texture2D*)getTexture2D:(NSString*)fileName {
	UIImage * img = [self getUIImage:fileName];
	if (img)
		return [[Texture2D alloc] initWithImage:img];
 
	return nil;
}
 
-(UIImage*)getUIImageFromWeb:(NSString*)url {
	UIImage * img = [[UIImage alloc] initWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:url]]];
	[images addObject:img];
	return img;
}
 
-(CGImageRef)getCGImageRefFromWeb:(NSString*)url {
	UIImage * img = [self getUIImageFromWeb:url];
	if (img)
		return [img CGImage];
 
	return nil;
}
 
-(Texture2D*)getTexture2DFromWeb:(NSString*)url {
	UIImage * img = [self getUIImageFromWeb:url];
	if (img) {
		Texture2D * tex = [[Texture2D alloc] initWithImage:img];
		[textures addObject:tex];
		return tex;
	}
 
	return nil;
}
 
-(UIImage*)getUIImageFromAppBundle:(NSString*)fileName {
	UIImage * img = [UIImage imageNamed:fileName];
	[images addObject:img];
	return img;
}
 
-(CGImageRef)getCGImageRefFromAppBundle:(NSString*)fileName {
	UIImage * img = [self getUIImageFromAppBundle:fileName];
	if (img)
		return [img CGImage];
 
	return nil;
}
 
-(Texture2D*)getTexture2DFromAppBundle:(NSString*)fileName {
	UIImage * img = [self getUIImageFromAppBundle:fileName];
	if (img) {
		Texture2D * tex = [[Texture2D alloc] initWithImage:img];
		[textures addObject:tex];
		return tex;
	}
 
	return nil;
}
 
@end

Example Usage

Since it’s a singleton, there’s no need to instantiate it. Just include the header file and call its sharedResourceManager method. By default, it is set to load assets from the application bundle.

How to load a file into a UIImage:

UIImage * img = [[ResourceManager sharedResourceManager] getUIImage:@"menu.png"];

How to load a file into a cocos2d Sprite:

Sprite * bg = [Sprite spriteWithCGImage:[[ResourceManager sharedResourceManager] getCGImageRef:@"background.jpg"]];

How to load a file into a cocos2d AtlasSpriteManager:

AtlasSpriteManager * mgr = [AtlasSpriteManager spriteManagerWithTexture:[[ResourceManager sharedResourceManager] getTexture2D:@"character.png"] capacity:1];

As I mentioned above, we like to be able to switch between loading the same assets from the device and from the web at run-time. We do this by setting the ResourceManager to be in web mode and giving it the URL of a folder where our assets are stored online:

[ResourceManager sharedResourceManager].web = YES;
[ResourceManager sharedResourceManager].urlPrefix = @"http://example.com/assets/";

If you run those 2 lines before running any of the previous examples, the ResourceManager will look online for the asset you asked for instead of on the device.

As well, there are methods included that force the class to load from the web or device, regardless of which mode it is in.

How to force it to load a UIImage from the web:

UIImage * img = [[ResourceManager sharedResourceManager] getUIImageFromWeb:@"http://example.com/assets/menu.png"];

Finally, to clean up any resources already loaded:

[[ResourceManager sharedResourceManager] cleanup];

IGDA meetup on iPhone development

Aug 17, 2009   //   by Rob Segal   //   Events  //  No Comments

I was able to attend an IGDA Toronto chapter meeting a few days ago.  This months meeting highlighted what a few local developers were up to with iPhone development.

First up was Cerebral Vortex Games.  They reviewed some of the trials and tribulations they had to go through for their upcoming iPhone game “Ghost Breaker” which you can read a bit about on their site,  Pretty cool concept.  The game works in conjunction with the iPhone camera to turn any environment into a game against the undead.

Dave Wood from Cerebral Gardens was up next.  Most of his presentation highlighted various ways that people can hack your app/game on the iPhone.  It was an interested presentation overall as Dave went through various code snippets you can dump into your app to help prevent this from happening.  The ultimate message he was stressing though was that people WILL crack your app.  That is a given but it shouldn’t stop you from following through with a good idea if you have one and I definitely agree.  Although people may be pirating your app in some ways that is a good thing as it means people want to play your game and they will tell their friends about it.

Last up was Harry Mack.  An audio designer work on various iPhone projects.  He went through some of the finer points of what the best compression choices are when deploying a game to the iPhone as well as what file formats work best.  His presentation is available for download off of the main page of his site or you can click here to get it.

Overall it was worthwhile attending and revealed a few of the finer points we will have to address in our own upcoming game.

What Caught Our Eye This Week – August 9 to August 15, 2009

Aug 16, 2009   //   by Derek van Vliet   //   Weekly Recap  //  No Comments

Here are some of the stories we kept track of this week:

  • A bank has come up with an innovative new way to use the iPhone (or why not any phone with a camera?). They’re enabling their customers to deposit checks using an iPhone app.
  • Facebook acquired FriendFeed. Be still our hearts.
  • How the iPhone App Store is changing the perception of what software should cost and how publishers are pricing their product. On the flip side, here’s a look at the 10 most expensive iPhone apps.
  • It’s time to play Duke Nukem on the iPhone and chew bubble gum… and we’re all out of gum.
  • Apple’s Phil Schiller is taking the PR reigns for the iPhone App Store, acknowledging that there have been problems and that people at Apple are listening to the feedback.

Join us in our FriendFeed group to catch these stories as they break and share ones that you find.

Dev Video Diary – August 12, 2009

Aug 12, 2009   //   by Derek van Vliet   //   Development  //  6 Comments

We added a whole bunch of new features to the game since last week, including:

  • Exploding bomb mushrooms (with hilarious chain reactions)
  • Force fields
  • Matching icons to win the level
  • Poison mushrooms
  • Mushroom respawning
  • Golden mushrooms
  • Race against the clock
  • Fun effects for popping/exploding

Also be sure to listen for some familiar sounds.

What Caught Our Eye This Week – August 2 to August 8, 2009

Aug 9, 2009   //   by Derek van Vliet   //   Weekly Recap  //  No Comments

Here’s what earned our attention this week:

These are just a few of the stories that we followed in our Friendfeed room this week. Subscribe to our room to join in the discussion.

This Week in Shroom Boom – Aug. 7, 2009

Aug 7, 2009   //   by Derek van Vliet   //   Development  //  1 Comment

We got a lot done this week. From web-based asset loading into cocos2d to bitmap-based object placement. Not to mention a snazzy app icon, splash screen, title screen and jiggly animating mushrooms.

Improving the Performance of Animating Sprites in cocos2d

Aug 5, 2009   //   by Derek van Vliet   //   Development  //  11 Comments

cocos2d has proven to be invaluable in enabling us to get our iPhone game up and running fast. But when we started playing with animated sprites, we noticed some performance problems. If you are like us and have many sprites that have to have repeating animations, you may have noticed the same thing.

Conventional wisdom (aka the sample code) says that you would create a RepeatForever action using an Animate action to animate an AtlasSprite. When we tried this, however, we noticed that at the end of each animation cycle, the action would deallocate an AtlasSpriteFrame. This is a by-product of the Animate action being able to restore the original frame of animation once the animation has finished playing. This is also what was causing the performance issues.

To resolve this, we created a cut-down version of the Animate action called FastAnimate. It is very similar to the Animate action with the exception that it does not support restoring the original frame of animation. From what we’ve seen, this simple change greatly increases the performance of animating sprites in cocos2d.

FastAnimate.h:

#import "cocos2d.h"
 
@interface FastAnimate : IntervalAction <NSCopying>
{
	Animation *animation;
}
+(id) actionWithAnimation:(id<CocosAnimation>) a;
-(id) initWithAnimation:(id<CocosAnimation>) a;
@end

FastAnimate.m:

#import "FastAnimate.h"
 
@implementation FastAnimate
 
+(id) actionWithAnimation: (id<CocosAnimation>)anim {
	return [[[self alloc] initWithAnimation:anim] autorelease];
}
 
-(id) initWithAnimation: (id<CocosAnimation>)anim {
	NSAssert( anim!=nil, @"Animate: argument Animation must be non-nil");
 
	if( (self=[super initWithDuration: [[anim frames] count] * [anim delay]]) ) {
 
		animation = [anim retain];
	}
	return self;
}
 
-(id) copyWithZone: (NSZone*) zone {
	return [[[self class] allocWithZone: zone] initWithAnimation: animation];
}
 
-(void) dealloc {
	[animation release];
	[super dealloc];
}
 
-(void) startWithTarget:(id)aTarget {
	[super startWithTarget:aTarget];
}
 
-(void) stop {
	[super stop];
}
 
-(void) update: (ccTime) t {
	NSUInteger idx=0;
 
	ccTime slice = 1.0f / [[animation frames] count];
 
	if(t !=0 )
		idx = t/ slice;
 
	if( idx >= [[animation frames] count] ) {
		idx = [[animation frames] count] -1;
	}
	id<CocosNodeFrames> sprite = (id<CocosNodeFrames>) target;
	if (! [sprite isFrameDisplayed: [[animation frames] objectAtIndex: idx]] ) {
		[sprite setDisplayFrame: [[animation frames] objectAtIndex:idx]];
	}
}
@end

What Caught Our Eye This Week – July 26 to August 1, 2009

Aug 2, 2009   //   by Derek van Vliet   //   Weekly Recap  //  No Comments

Here’s a look at just some of the things that we kept our eye on this week:

  • Why nobody hates software more than software developers. Hint: it’s because we know the volumes of bad code that get poured into it.
  • Google Voice applications got rejected from the iPhone’s App Store. This is causing a lot of concern among both developers and users of the iPhone platform. Michael Arrington even declared that he’s “quitting” the iPhone.
  • Two researchers announced they had discovered a way to virally infect and hijack the functions of every iPhone on earth. Thankfully, none of Get Set Games’ devices were affected once they unveiled the exploit on Thursday. Apple was quick to release a patch.
  • In a bid to keep iPhone jailbreaking illegal, Apple claimed in a filing that jailbreaking would turn the iPhone into a weapon against cell-phone towers.
  • We watched in amazement as Yahoo! committed seppuku this week, making it a historic week in online search.

If you’d like to keep track of what we read and join in on the discussion, join our FriendFeed room.

Our Games

Latest Tweets