I've been slowly growing my Cocos2d iPhone related entries, and in this entry I want to display some real usable code to solve a general problem for iPhone game developers: how can I create a how-to guide that teaches new users to play my game?
The code below implements a simple slideshow player, which will cycle through an arbitrary number of images and text labels, and then pop itself off the stack of scenes. Along the way we'll implement a full example of handling touch detection in Cocos2d, and see how quick and downright pleasant Cocos2d iPhone development can be.
Usage of the SlideShowLayer class looks like this:
// Initialize scene and slide show layer.
Scene * s = [Scene node];
SlideShowLayer * ssl = [SlideShowLayer node];
// Set positions for the background and labels.
// Generally you'll be using full screen backgrounds
// (screenshots), and positioning them at the center.
[ssl setBackgroundXPosition:100];
[ssl setBackgroundYPosition:100];
[ssl setDescriptionXPosition: 200];
[ssl setDescriptionYPosition: 200];
[ssl setDescriptionWidth:100];
[ssl setDescriptionHeight:300];
[ssl setFontName:@"Helvetica"]; // optional
[ssl setFontSize:24]; // optional
// Add slides in order they appear.
[ssl addSlideWithBackground:@"pic1.png" andDescription:@"Yada yada one."];
[ssl addSlideWithBackground:@"pic2.png" andDescription:@"Yada yada two."];
[ssl addSlideWithBackground:@"pic3.png" andDescription:@"Yada yada 3."];
[ssl addSlideWithBackground:@"pic4.png" andDescription:@"Yada yada 4."];
[ssl addSlideWithBackground:@"pic5.png" andDescription:@"Yada yada 5."];
[ssl addSlideWithBackground:@"pic6.png" andDescription:@"Yada yada six."];
// Start and display the slide show.
[ssl displayFirstSlide];
[scene add:ssl];
[[Director sharedDirector] pushScene:scene];
Currently there are some limitations (all labels and images must be at the same location and all labels must be of the same height and width), but improving upon the code in that regard shouldn't be terriably difficult. Beyond that, it should be a fairly reusable and flexible tool for displaying the simplest of simple slide shows.
I'll be posting the code in its entirety as large chunks (one chunk
for SlideShowLayer.h and one chunk for SlideShowLayer.m), to
promote copy and pastability. Commentary will be inlined in the code
as Objective-C comments.
First SlideShowLayer.h.
/*
* SlideShowLayer by Will Larson and Luke Hatcher. 10/29/2008.
* Released under MIT License.
* Please check out from repository for full license detail.
*
* Nothing too exiting happening here.
* Apologies that it is not in Obj2.0
* syntax, but I'm still ambivalent about
* the ambiguousness of properties.
*/
#import <UIKit/UIKit.h>
#import "CocosNode.h"
#import "Layer.h"
#import "Label.h"
#import "Sprite.h"
@interface SlideShowLayer : Layer {
int slidePosition;
int backgroundXPosition;
int backgroundYPosition;
int descriptionXPosition;
int descriptionYPosition;
int descriptionHeight;
int descriptionWidth;
int fontSize;
NSString * fontName;
NSMutableArray * backgrounds;
NSMutableArray * descriptions;
Sprite * background;
CocosNode * description;
}
/*
* This is the only method for adding slides to the slide show.
*/
-(void)addSlideWithBackground: (NSString *)imageString
andDescription: (NSString *)descString;
/*
* Methods for advancing, retreating, and starting the slideshow.
* There currently isn't a UI mechanism for retreating to already
* seen slides, but you could rig something up if you tried hard
* enough. ;)
*/
-(void)displayFirstSlide;
-(void)displayNextSlide;
-(void)displayPreviousSlide;
-(void)displaySlide: (int)slideNumber;
-(BOOL)hasPreviousSlide;
-(BOOL)hasNextSlide;
/*
* Mutators and Accessors
*/
-(NSMutableArray *)backgrounds;
-(NSMutableArray *)descriptions;
-(Sprite *)background;
-(void)setBackground: (Sprite *)aSprite;
-(CocosNode *)description;
-(void)setDescription: (CocosNode *)aCocosNode;
-(NSString *)fontName;
-(void)setFontName: (NSString *)aFontName;
-(int)fontSize;
-(void)setFontSize: (int)anInt;
-(int)slidePosition;
-(void)setSlidePosition: (int)anInt;
-(int)backgroundXPosition;
-(void)setBackgroundXPosition: (int)anInt;
-(int)backgroundYPosition;
-(void)setBackgroundYPosition: (int)anInt;
-(int)descriptionXPosition;
-(void)setDescriptionXPosition: (int)anInt;
-(int)descriptionYPosition;
-(void)setDescriptionYPosition: (int)anInt;
-(int)descriptionWidth;
-(void)setDescriptionWidth: (int)anInt;
-(int)descriptionHeight;
-(void)setDescriptionHeight: (int)anInt;
@end
And now for SlideShowLayer.m.
/*
* SlideShowLayer by Will Larson and Luke Hatcher. 10/29/2008.
* Released under MIT License.
* Please check out from repository for full license detail.
*/
#import "SlideShowLayer.h"
@implementation SlideShowLayer
-(id)init {
self = [super init];
if (self) {
/*
* setting isTouchEnabled is the magic step that
* allows the layer to be registered for UI events.
* forgetting this step will lead to great confusion :-/
*/
isTouchEnabled = YES;
slidePosition = -1;
backgrounds = [[NSMutableArray alloc] init];
descriptions = [[NSMutableArray alloc] init];
[self setFontName:@"Helvetica"];
[self setFontSize:24];
}
return self;
}
- (void) dealloc {
[backgrounds release];
backgrounds = nil;
[descriptions release];
descriptions = nil;
[background release];
background = nil;
[description release];
description = nil;
[fontSize release];
fontSize = nil;
[super dealloc];
}
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
/*
* We are using pushScene/popScene instead of replaceScene
* (which has superior memory usage characteristics), because
* it makes integrating SlideShowLayer much simpler, and helps
* avoid binding the code to one application without requiring
* us to use the delegate pattern.
*/
if ([self hasNextSlide]) [self displayNextSlide];
else [[Director sharedDirector] popScene];
}
-(void)addSlideWithBackground: (NSString *)imageString
andDescription: (NSString *)descString {
[[self backgrounds] addObject:imageString];
[[self descriptions] addObject:descString];
}
-(void)displayFirstSlide {
if ([[self backgrounds] count] > 0) {
[self setSlidePosition:0];
[self displaySlide:[self slidePosition]];
}
}
-(void)displayNextSlide {
if ([self hasNextSlide]) {
[self setSlidePosition:[self slidePosition]+1];
[self displaySlide:[self slidePosition]];
}
}
-(void)displayPreviousSlide {
if ([self hasPreviousSlide]) {
[self setSlidePosition:[self slidePosition]-1];
[self displaySlide:[self slidePosition]];
}
}
-(void)displaySlide: (int)slideNumber {
/*
* Remove existing slide and description.
*/
if ([self background]) {
[self remove:[self background]];
}
if ([self description]) {
[self remove:[self description]];
}
/*
* Retrieve and generate necessary details for next slide.
*/
NSString * bgString = [[self backgrounds] objectAtIndex:slideNumber];
NSString * descString = [[self descriptions] objectAtIndex:slideNumber];
Sprite * bg = [Sprite spriteWithFile:bgString];
bg.position = cpv([self backgroundXPosition], [self backgroundYPosition]);
CocosNode * desc = [Label labelWithString:descString
dimensions:CGSizeMake([self descriptionWidth],
[self descriptionHeight])
alignment:UITextAlignmentCenter
fontName:[self fontName]
fontSize:[self fontSize]];
desc.position = cpv([self descriptionXPosition], [self descriptionYPosition]);
// Add the background and desc to the layer.
[self setBackground:bg];
[self setDescription:desc];
[self add:bg];
[self add:desc];
}
-(BOOL)hasNextSlide {
return ([self slidePosition]+1 < [[self backgrounds] count]);
}
-(BOOL)hasPreviousSlide {
return ([self slidePosition] > 0);
}
-(Sprite *)background {
return background;
}
-(void)setBackground: (Sprite *)aSprite {
if (background) [background release];
background = [aSprite retain];
}
-(CocosNode *)description {
return description;
}
-(void)setDescription: (CocosNode *)aCocosNode {
if (description) [description release];
description = [aCocosNode retain];
}
-(NSMutableArray *)backgrounds {
return backgrounds;
}
-(NSMutableArray *)descriptions {
return descriptions;
}
-(NSString *)fontName {
return fontName
}
-(int)fontSize {
return fontSize;
}
-(void)setFontSize: (int)anInt {
fontSize = anInt;
}
-(void)setFontName: (NSString *)aFontName {
if (fontName) [fontName release];
fontName = [aFontName retain];
}
-(int)slidePosition {
return slidePosition;
}
-(void)setSlidePosition: (int)anInt {
slidePosition = anInt;
}
-(int)backgroundXPosition {
return backgroundXPosition;
}
-(void)setBackgroundXPosition: (int)anInt {
backgroundXPosition = anInt;
}
-(int)backgroundYPosition {
return backgroundYPosition;
}
-(void)setBackgroundYPosition: (int)anInt {
backgroundYPosition = anInt;
}
-(int)descriptionXPosition {
return descriptionXPosition;
}
-(void)setDescriptionXPosition: (int)anInt {
descriptionXPosition = anInt;
}
-(int)descriptionYPosition {
return descriptionYPosition;
}
-(void)setDescriptionYPosition: (int)anInt {
descriptionYPosition = anInt;
}
-(int)descriptionWidth {
return descriptionWidth;
}
-(void)setDescriptionWidth: (int)anInt {
descriptionWidth = anInt;
}
-(int)descriptionHeight {
return descriptionHeight;
}
-(void)setDescriptionHeight: (int)anInt {
descriptionHeight = anInt;
}
@end
Hopefully this is a starting point that you can work from, and use in your applications. I'll be cleaning this up and putting it into a repository in the near future. I'll post a link here afterwards.
Let me know if you have any questions or comments.
Will, your articles are very nicely done and I for one appreciate it. Could you put up the entire code for this slideshow (or your other articles) as an Xcode project that we could download and build? I am trying to wrap my arms around cocos2d. Thanks!
Hiya!, I've found this blog pretty interesting, and a log of articles very very usefull.
I want to point some things tho.
-(NSString *)fontName { return fontName }
there's a ; missing at the end of the return.
I think, you'll have to import "cocos2d.h" in the SlideShowLayer.h file, as it gives problems because it doesnt know what is the Director
And to finish (and I dont know how to fix this:P) there are 2 warnings on the dealloc function at:
[fontSize release]; //Warning: Invalid receiver type 'int' fontSize = nil;//Warning: assignment makes integer prom pointer without a cast
Even tho, after these little things, all I can say is Great Post!!! keep them like that!
your blog has been helping me a lot, thanks! ;)
Please also, let me know where you fix those warnings, or tell me how can i fix them :S!
regards!
Juan Sebastian
I think i worked to solve the warnigns :P
Just change fontSize to fontName :D ;)
Interessant.Could you send me by mail, the complete XCode project please? Thank you.
Yeah, it would be very nice if you could send me the complete app to :) thank you
you should be careful setting pointers to nil with a reference counting scenario; that is a good way to create leaks. Just release them without dangling them with a nil.
Very nice code: simple and useful. Thanks for sharing.
where are the definitions for cocos2d.h,Layer.h,Sprite.h and so on?. Nobody seemed to be bothered about those. am I missing something?.
Found them. sorry, I was dumb
hey.. Surya !! from where did u find that missing files?
thanks..
If you haven't found them already, please follow the link http://code.google.com/p/cocos2d-iphone/
I'm getting Layer may not respond to '-add' and '-remove' warnings when I try to compile it in cocos2d 0.80. Anyone seen the same?
It seems to be a great example for creating slideshow. I am wanting to build a slideshow using cocos2D for my game too. Found your code giving me a lot of help....Thanks. I will try to see if I can apply this for my game or not.
Could you send me your complete code please? It would help me a lot ....thanks a lot
Reply to this entry