- Initialize the scale of the background with respect to the current screen size and the resources dimensions and save this ratio.
- Scale the background and images with the previously calculated ratio. As the iPhone has not the same dimensions of the iPad, it uses scaleX and scaleY, and the images get a bit stretched.
- Never use points for positions, but use logic of dynamic positions with respect to the screen: top-left corner of the screen is (size.width*0, size.height*1) and the center-right point is (size.width*1, size.height*0.5).
One of the problems I found in my way was the memory constraint, the iPad 1 couldn't load big files of images. I had to change the resources target from iPad retina to iPad non-retina screen. Enabling retina display but without the -hd resources makes the graphics on iPad retina not blurred nor with pixels, so it was a good solution and saved a lot of mega bytes.
Here you have the framework and an example of the usage:
Here you have the framework and an example of the usage:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// ResourcesManagerStretched.h | |
// | |
// Created by Elena Vielva on 31/04/12. | |
// Copyright (c) 2012 Elena Vielva. All rights reserved. | |
// | |
#import <Foundation/Foundation.h> | |
#import "cocos2d.h" | |
@interface ResourcesManagerStretched : NSObject { | |
float scaleX; | |
float scaleY; | |
CGSize size; | |
CCSpriteFrameCache *frameCache; | |
} | |
@property (readonly) float scaleX; | |
@property (readonly) float scaleY; | |
@property (readonly) CGSize size; | |
+ (ResourcesManagerStretched *) sharedResourcesManager; | |
- (CCSprite *) addBackground:(NSString*)bg To:(CCLayer*)layer; | |
- (CCSprite *) addSpriteWithName:(NSString*)name Position:(CGPoint)pos Layer:(int)z To:(CCNode*)layer Tag:(int) tag; | |
- (CCAnimate*) generateAnimationWithName:(NSString*)name NumberOfImages:(int)n Delay:(float) delay Repeticion:(BOOL) rep Restore:(BOOL)restore; | |
@end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// ResourcesManagerStretched.m | |
// | |
// Created by Elena Vielva on 31/04/12. | |
// Copyright (c) 2012 Elena Vielva. All rights reserved. | |
// | |
#import "ResourcesManagerStretched.h" | |
static ResourcesManagerStretched *shared; | |
@implementation ResourcesManagerStretched | |
@synthesize scaleX = scaleX; | |
@synthesize scaleY = scaleY; | |
@synthesize size = size; | |
+ (ResourcesManagerStretched *) sharedResourcesManager { | |
if (shared) { | |
return shared; | |
} | |
shared = [[ResourcesManagerStretched alloc] init]; | |
return shared; | |
} | |
+ (id)alloc { | |
NSAssert(shared == nil, @"Attempted to allocate a second instance of a singleton."); | |
return [super alloc]; | |
} | |
-(id) init { | |
self = [super init]; | |
if (self) { | |
size = [[CCDirector sharedDirector] winSize]; | |
frameCache = [CCSpriteFrameCache sharedSpriteFrameCache]; | |
} | |
return self; | |
} | |
/** ADDBACKGROUND:TO | |
* Adds a background image to the scene. For this: | |
* - Gets the file from the name | |
* - Scales it | |
* - Places it in the center of the screen | |
* - Adds it to the z-height -1 (to make sure its in the background) | |
* Input: NSString -> name of the background image | |
* CCLayer -> parent layer to add the background to | |
* Output: CCSprite -> the sprite | |
*/ | |
- (CCSprite *) addBackground:(NSString *)bg To:(CCLayer *)layer { | |
CCSprite *background = [CCSprite spriteWithFile:bg]; | |
if ((scaleX==0) || (scaleY==0)) { | |
scaleX = size.width / background.contentSize.width; | |
scaleY = size.height / background.contentSize.height; | |
} | |
background.scaleX = scaleX; | |
background.scaleY = scaleY; | |
NSAssert(background!=nil,@"Error couldn't load background"); | |
[background setPosition:ccp(size.width/2, size.height/2)]; | |
[layer addChild:background z:-1]; | |
return background; | |
} | |
/** ADDSPRITEWITHFRAME:POSITION:LAYER:TO:TAG | |
* Adds the sprite to the scene. For this: | |
* - Gets the frame from the name | |
* - Gets the sprite from the frame | |
* - Scales the sprite according with the current device | |
* - Places it in the given position | |
* - Adds it to the parent node | |
* Input: NSString -> name of the file | |
* CGPoint -> position where the sprite is placed | |
* int -> z-layer (height) where the sprite is placed | |
* CCNode -> parent node where the sprite is added to | |
* Int -> tag | |
* Output: CCSprite -> the sprite | |
*/ | |
- (CCSprite *) addSpriteWithName:(NSString*)name Position:(CGPoint)pos Layer:(int)z To:(CCNode *)layer Tag:(int) tag{ | |
CCSpriteFrame * frame = [frameCache spriteFrameByName:name]; | |
NSAssert(frame!=nil,@"Frame es nil"); | |
CCSprite *sprite = [CCSprite spriteWithSpriteFrame:frame]; | |
if ((scaleX==0) || (scaleY==0)) { | |
scaleX = size.width / sprite.contentSize.width; | |
scaleY = size.height / sprite.contentSize.height; | |
} | |
if (sprite!=nil) { | |
sprite.scaleX = scaleX; | |
sprite.scaleY = scaleY; | |
[sprite setPosition:pos]; | |
[layer addChild:sprite z:z tag:tag]; | |
} | |
return sprite; | |
} | |
/** GENERATEANIMATIONWITHNAME:NUMBEROFIMAGES:DELAY:REPETITION:TO {:RESTORE} | |
* The name of the sprites range from name0001.png to name0023.png for example | |
* Generates a cartoon-style animation. For this: | |
* - For each image of the animation: "calculates its name" and adds it to an array | |
* - When all the images are added to the array, creates the animation | |
* - It the animation should be a loop, makes the animation repeat forever | |
* Input: NSString -> Base name of the animations (before 0001.png) | |
* int -> Number of images of the animation | |
* float -> Delay between the animation frames | |
* BOOL -> Whether the animation is a loop or not | |
* BOOL -> Whether the animation should restore to the first frame when finished or not | |
* Output: CCAnimate -> The animation | |
*/ | |
- (CCAnimate*) generateAnimationWithName:(NSString*)name NumberOfImages:(int)n Delay:(float) delay Repeticion:(BOOL) rep Restore:(BOOL)restore { | |
NSMutableArray *animFrames = [NSMutableArray array]; | |
for (int i=1; i<=n; i++) { | |
NSString * spriteName; | |
if (i<10) { | |
spriteName = [NSString stringWithFormat:@"%@000%d.png",name, i]; | |
} else if (i<100) { | |
spriteName = [NSString stringWithFormat:@"%@00%d.png",name, i]; | |
} else if (i<1000) { | |
spriteName = [NSString stringWithFormat:@"%@0%d.png",name, i]; | |
} else if (i<10000) { | |
spriteName = [NSString stringWithFormat:@"%@%d.png",name, i]; | |
} | |
CCSpriteFrame * frame = [frameCache spriteFrameByName: spriteName]; | |
[animFrames addObject: frame]; | |
} | |
CCAnimation *animation = [CCAnimation animationWithFrames:animFrames delay:delay]; | |
CCAnimate *action = [CCAnimate actionWithAnimation:animation restoreOriginalFrame:restore]; | |
if (rep) { | |
action = [CCRepeatForever actionWithAction:action]; | |
} | |
return action; | |
} | |
@end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
+(CCScene *) scene { | |
CCScene *scene = [CCScene node]; | |
TestLayer *layer = [TestLayer node]; | |
[scene addChild: layer]; | |
return scene; | |
} | |
- (id) init { | |
self = [super init]; | |
if (self) { | |
ResourcesManagerStretched *helper = [ResourcesManagerStretched sharedResourcesManager]; | |
[helper addBackground:@"background.jpg" To:self]; | |
size = helper.size; | |
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:@"resources.plist" | |
textureFile:@"resources.pvr.ccz"]; | |
sp1 = [helper addSpriteWithName:@"spName.png" Position:ccp(size.width*0.5,size.height*0.18) Layer:5 To:self]; | |
sp2 = [helper addSpriteWithName:@"otherSprite.png" Position:ccp(size.width*0.1,size.height*0.1) Layer:5 To:self]; | |
} | |
return self; | |
} |
When the iPhone 5 ready apps were mandatory for new submissions (and updates), it was also possible to keep stretching the backgrounds and sprites, but now our characters have got too much fat. I had to think a new way of using only a pack of images and taking advance of all screen sizes. Pen and paper and a calculator for operations, I draw something like this:
The small rectangle should have all the important information for the app (buttons, information, ...) but the background have to fill all the space, in order to take advance of the iPhone screens. The reference size is the dimension of this rectangle in the different devices: in the iPad is the same as its size, while in the iphone will fulfill that 4*iphoneHeight = 3*iphoneWidth. Thus, the left reference point is the (screen size - reference size) / 2 and the point most on the right would be refLeft+refWidht*1.0.
Here there is the new framework and an example of how to use it:
The small rectangle should have all the important information for the app (buttons, information, ...) but the background have to fill all the space, in order to take advance of the iPhone screens. The reference size is the dimension of this rectangle in the different devices: in the iPad is the same as its size, while in the iphone will fulfill that 4*iphoneHeight = 3*iphoneWidth. Thus, the left reference point is the (screen size - reference size) / 2 and the point most on the right would be refLeft+refWidht*1.0.
Here there is the new framework and an example of how to use it:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// ResourcesManager.h | |
// | |
// Created by Elena Vielva on 15/04/13. | |
// Copyright (c) 2012 Elena Vielva. All rights reserved. | |
// | |
#import <Foundation/Foundation.h> | |
#import "cocos2d.h" | |
#import "Constants.h" | |
@interface ResourcesManager : NSObject { | |
float _scale; | |
CGSize _size; | |
CGSize _refSize; | |
CCSpriteFrameCache *frameCache; | |
} | |
@property (readonly) float scale; | |
@property (readonly) CGSize size; | |
@property (readonly) CGSize refSize; | |
+ (ResourcesManager *) sharedResourcesManager; | |
- (CCSprite *) addBackground:(NSString*)bg To:(CCLayer*)layer; | |
- (CCSprite *) addSpriteWithName:(NSString*)name Position:(CGPoint)pos Layer:(int)z To:(CCNode*)layer Tag:(int) tag; | |
- (CCAnimate*) generateAnimationWithName:(NSString*)name NumberOfImages:(int)n Delay:(float) delay Repeticion:(BOOL) rep Restore:(BOOL)restore; | |
@end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// ResourcesManager.m | |
// | |
// Created by Elena Vielva on 15/04/13. | |
// Copyright (c) 2012 Elena Vielva. All rights reserved. | |
// | |
#import "ResourcesManager.h" | |
static ResourcesManager *shared; | |
@implementation ResourcesManager | |
@synthesize scale = _scale; | |
@synthesize size = _size; | |
@synthesize refSize = _refSize; | |
+ (ResourcesManager *) sharedResourcesManager { | |
if (shared) { | |
return shared; | |
} | |
shared = [[ResourcesManager alloc] init]; | |
return shared; | |
} | |
+ (id)alloc { | |
NSAssert(shared == nil, @"Attempted to allocate a second instance of a singleton."); | |
return [super alloc]; | |
} | |
-(id) init { | |
self = [super init]; | |
if (self) { | |
_size = [[CCDirector sharedDirector] winSize]; | |
frameCache = [CCSpriteFrameCache sharedSpriteFrameCache]; | |
[self initRefSizes]; | |
} | |
return self; | |
} | |
/** INITREFSIZES | |
* Initializes the reference sizes (dimensions) for the current device | |
* Reference size is the dimensions of the screen in which will be the information and important elements of the scene | |
* - iPads have as reference size the iPad non-retina dimension | |
* - iPhone reference size: keep the height; the width is calculated from its height with the ratio of iPad screen dimensions | |
*/ | |
- (void) initRefSizes { | |
CGSize pixelSize = [[CCDirector sharedDirector] winSizeInPixels]; | |
if (pixelSize.width == 1024) { | |
// iPad non-retina | |
_refSize = CGSizeMake(1024, 768); | |
}else if (pixelSize.width == 2048) { | |
// iPad retina | |
_refSize = CGSizeMake(1024, 768); | |
}else if (pixelSize.width == 1136) { | |
// iPhone 5 | |
_refSize = CGSizeMake(427, 320); | |
}else if (pixelSize.width == 960) { | |
// iPhone retina | |
_refSize = CGSizeMake(427, 320); | |
}else if (pixelSize.width == 480) { | |
// iPhone non-retina | |
_refSize = CGSizeMake(427, 320); | |
} | |
_scale = pixelSize.height/768; | |
} | |
/** ADDBACKGROUND:TO | |
* Adds a background image to the scene. For this: | |
* - Gets the file from the name | |
* - Scales it | |
* - Places it in the center of the screen | |
* - Adds it to the z-height -1 (to make sure its in the background) | |
* Input: NSString -> name of the background image | |
* CCLayer -> parent layer to add the background to | |
* Output: CCSprite -> the sprite | |
*/ | |
- (CCSprite *) addBackground:(NSString *)bg To:(CCLayer *)layer { | |
CCSprite *background = [CCSprite spriteWithFile:bg]; | |
background.scale = _scale; | |
NSAssert(background!=nil,@"Error al cargar el fondo"); | |
[background setPosition:ccp(_size.width/2, _size.height/2)]; | |
[layer addChild:background z:-1]; | |
return background; | |
} | |
/** ADDSPRITEWITHFRAME:POSITION:LAYER:TO:TAG | |
* Adds the sprite to the scene. For this: | |
* - Gets the frame from the name | |
* - Gets the sprite from the frame | |
* - Scales the sprite according with the current device | |
* - Places it in the given position | |
* - Adds it to the parent node | |
* Input: NSString -> name of the file | |
* CGPoint -> position where the sprite is placed | |
* int -> z-layer (height) where the sprite is placed | |
* CCNode -> parent node where the sprite is added to | |
* Int -> tag | |
* Output: CCSprite -> the sprite | |
*/ | |
- (CCSprite *) addSpriteWithName:(NSString*)name Position:(CGPoint)pos Layer:(int)z To:(CCNode *)layer Tag:(int) tag{ | |
CCSpriteFrame * frame = [frameCache spriteFrameByName:name]; | |
NSAssert(frame!=nil,@"Frame es nil"); | |
CCSprite *sprite = [CCSprite spriteWithSpriteFrame:frame]; | |
if (sprite!=nil) { | |
sprite.scale = _scale; | |
[sprite setPosition:pos]; | |
[layer addChild:sprite z:z tag:tag]; | |
} | |
return sprite; | |
} | |
/** GENERATEANIMATIONWITHNAME:NUMBEROFIMAGES:DELAY:REPETITION:TO {:RESTORE} | |
* The name of the sprites range from name0001.png to name0023.png for example | |
* Generates a cartoon-style animation. For this: | |
* - For each image of the animation: "calculates its name" and adds it to an array | |
* - When all the images are added to the array, creates the animation | |
* - It the animation should be a loop, makes the animation repeat forever | |
* Recibe: NSString -> Base name of the animations (before 0001.png) | |
* int -> Number of images of the animation | |
* float -> Delay between the animation frames | |
* BOOL -> Whether the animation is a loop or not | |
* BOOL -> Whether the animation should restore to the first frame when finished or not | |
* Devuelve: CCAnimate -> The animation | |
*/ | |
- (CCAnimate*) generateAnimationWithName:(NSString*)name NumberOfImages:(int)n Delay:(float) delay Repeticion:(BOOL) rep Restore:(BOOL)restore { | |
NSMutableArray *animFrames = [NSMutableArray array]; | |
for (int i=1; i<=n; i++) { | |
NSString * spriteName; | |
if (i<10) { | |
spriteName = [NSString stringWithFormat:@"%@000%d.png",name, i]; | |
} else if (i<100) { | |
spriteName = [NSString stringWithFormat:@"%@00%d.png",name, i]; | |
} else if (i<1000) { | |
spriteName = [NSString stringWithFormat:@"%@0%d.png",name, i]; | |
} else if (i<10000) { | |
spriteName = [NSString stringWithFormat:@"%@%d.png",name, i]; | |
} | |
CCSpriteFrame * frame = [frameCache spriteFrameByName: spriteName]; | |
[animFrames addObject: frame]; | |
} | |
CCAnimation *animation = [CCAnimation animationWithSpriteFrames:animFrames delay:delay]; | |
CCAnimate *action = [CCAnimate actionWithAnimation:animation restoreOriginalFrame:restore]; | |
if (rep) { | |
action = [CCRepeatForever actionWithAction:action]; | |
} | |
return action; | |
} | |
- (void) dealloc { | |
NSLog(@"Deallocating %@",self); | |
[super dealloc]; | |
} | |
@end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
+ (CCScene *) scene{ | |
CCScene *scene = [CCScene node]; | |
SomeLayer *layer = [SomeLayer node]; | |
[scene addChild:layer]; | |
return scene; | |
} | |
- (id) init{ | |
self = [super init]; | |
if (self) { | |
// ask director for the window size | |
CGSize size = [[CCDirector sharedDirector] winSize]; | |
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:@"sceneResources.plist"]; | |
ResourcesManager *helper = [ResourcesManager sharedResourcesManager]; | |
float refWidth = helper.refSize.width; | |
float refLeft = size.width/2-refWidth/2; | |
[helper addBackground:@"sceneBackground.jpg" To:self]; | |
sp1 = [helper addSpriteWithName:@"spriteName.png" Position:ccp(refLeft+refWidth*0.5, size.height*0.18) Layer:2 To:self]; | |
sp2 = [helper addSpriteWithName:@"otherSprite.png" Position:ccp(refLeft+refWidth*0.1, size.height*0.1) Layer:2 To:self]; | |
} | |
return self; | |
} |
Some useful links: