Creating Slideshows with Cocos2d iPhone
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]];
}
<span class="c">/*</span>
* 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]);
<span class="c">// Add the background and desc to the layer.</span>
<span class="p">[</span><span class="n">self</span> <span class="nl">setBackground:</span><span class="n">bg</span><span class="p">];</span>
<span class="p">[</span><span class="n">self</span> <span class="nl">setDescription:</span><span class="n">desc</span><span class="p">];</span>
<span class="p">[</span><span class="n">self</span> <span class="nl">add:</span><span class="n">bg</span><span class="p">];</span>
<span class="p">[</span><span class="n">self</span> <span class="nl">add:</span><span class="n">desc</span><span class="p">];</span>
}
-(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.