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.

5 Ways to Take Screenshots of Your iPhone App

Jul 30, 2009   //   by Derek van Vliet   //   Development  //  16 Comments

Screenshots are an integral tool in application development, not to mention marketing. You may find you need a screenshot in any number of situations. Here are 5 ways you can capture a screenshot of your iPhone app. Each one is useful in a different way.

Do It With OS X

The first method is good old screen capture that is built into OS X. This is, of course, not exclusive to iPhone apps. You can hold down Command-Shift-3 to save a screenshot to your desktop in PNG format. This is useful for if you want to take a screenshot while you are running your app in the emulator.

Even more useful, in my opinion, is the Command-Shift-4 function. This turns your cursor into cross-hairs and lets you select a region of the screen to save as your screenshot, again as a PNG. This is great if you just wanted to grab a portion of your app.

You can also add Ctrl to both of the above keyboard shortcuts to save the screenshot to your clipboard instead of to disk. Then you can go paste the screenshot into Photoshop or some other image editor.

Do It With Grab

Grab is an application that comes with OS X which you can find in your Applications/Utilities folder. It features the unique ability to take a screenshot of a window:

grab-menu

This means if you run your app in the iPhone Simulator and then use Grab, you can take screenshots of your app looking all hot in the actual device with just a couple of clicks. This is a great way to get shots of your app in the iPhone if you don’t own one or aren’t a paid developer. Here’s the result:

grab

Screenshots that come out of Grab are in TIFF format.

Do It With Xcode

Xcode’s Organizer tool provides a simple interface for taking screenshots of whatever is currently displaying on an iPhone that is connected to your computer. Open it by going to Xcode’s Window menu and selecting Organizer or just hit Ctrl-Command-O.

organizer

Once it is opened, select your iPhone from the Devices section on the left. Then go to the Screenshot tab in the main section. Now use your application on the iPhone until it is in a state where you want to take a screenshot. Finally, press the capture button in Organizer and boom: you’re done.

Xcode saves screenshots as PNG files in the following folder on your machine:

~/Library/Application Support/Developer/Shared/Xcode/Screenshots/

Do It With the iPhone

Did you know you can take a screenshot of anything that the iPhone is doing at any time? It’s true. Just press and hold the home button. While you’re holding it, press and release the Sleep/Wake button. The iPhone’s screen will flash and the camera shutter sound will play. The screenshot will have been saved to your camera roll, which you can access in the Camera app or the Photos app.

iphone

Do It With Code

Finally, you can capture screenshots at precise moments in your application by using the following pair of objective-c methods. The first one, captureView, returns a UIImage that contains a render of any UIView (except EAGLView, see below). If you pass your app’s UIWindow into this method, it will return you a screenshot of the app.

The second method, saveScreenshotToPhotosAlbum, takes it a step further and saves an image that contains a render of any UIView to your iPhone’s photo album.

#import 
 
- (UIImage*)captureView:(UIView *)view {
	CGRect rect = [[UIScreen mainScreen] bounds];
	UIGraphicsBeginImageContext(rect.size);
	CGContextRef context = UIGraphicsGetCurrentContext();
	[view.layer renderInContext:context];
	UIImage *img = UIGraphicsGetImageFromCurrentImageContext();
	UIGraphicsEndImageContext();
	return img;
}
 
- (void)saveScreenshotToPhotosAlbum:(UIView *)view {
	UIImageWriteToSavedPhotosAlbum([self captureView:view], nil, nil, nil);
}

Since the above 2 methods don’t work for EAGLView, we need another solution. Below are a pair of methods that do the equivalent of the above 2 for apps using EAGLView, courtesy iPhone Dev SDK Forum.

- (UIImage*) getGLScreenshot {
    NSInteger myDataLength = 320 * 480 * 4;
 
    // allocate array and read pixels into it.
    GLubyte *buffer = (GLubyte *) malloc(myDataLength);
    glReadPixels(0, 0, 320, 480, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
 
    // gl renders "upside down" so swap top to bottom into new array.
    // there's gotta be a better way, but this works.
    GLubyte *buffer2 = (GLubyte *) malloc(myDataLength);
    for(int y = 0; y &lt;480; y++)
    {
        for(int x = 0; x &lt;320 * 4; x++)
        {
            buffer2[(479 - y) * 320 * 4 + x] = buffer[y * 4 * 320 + x];
        }
    }
 
    // make data provider with data.
    CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, buffer2, myDataLength, NULL);
 
    // prep the ingredients
    int bitsPerComponent = 8;
    int bitsPerPixel = 32;
    int bytesPerRow = 4 * 320;
    CGColorSpaceRef colorSpaceRef = CGColorSpaceCreateDeviceRGB();
    CGBitmapInfo bitmapInfo = kCGBitmapByteOrderDefault;
    CGColorRenderingIntent renderingIntent = kCGRenderingIntentDefault;
 
    // make the cgimage
    CGImageRef imageRef = CGImageCreate(320, 480, bitsPerComponent, bitsPerPixel, bytesPerRow, colorSpaceRef, bitmapInfo, provider, NULL, NO, renderingIntent);
 
    // then make the uiimage from that
    UIImage *myImage = [UIImage imageWithCGImage:imageRef];
    return myImage;
}
 
- (void)saveGLScreenshotToPhotosAlbum {
	UIImageWriteToSavedPhotosAlbum([self getGLScreenshot], nil, nil, nil);
}

Our Games

Latest Tweets