Categories
Tutorials

Playing a secondary theme in Cocos2D

Cocos2D offers CocosDenshion, an easy to use framework to work with audio.

CocosDenshion allows you to play a background music and then some effects on top of that. This is clearly aimed at games and it usually works really well. The difference between background music and effects is summarized as follows:

  1. background music is thought of as potentially of long duration, so it is handled as a long audio stream;
  2. background music is thought of as continuous, so it is played in exclusive mode;
  3. effects are thought of as smaller in size, so they are loaded into memory entirely;
  4. effects are by their definition itself aimed at being played many times, so they are cached into memory for reuse;
  5. effect can be mixed to background music, so they do not “steal” the audio subsystem.

This works well until your effects are pretty small in size, otherwise your memory requirements will quickly grow. On iOS this is a no-no, since your app has very little memory to run with. Another case when CocosDenshion falls short is when you want your effects to be played continuously in a loop.

Say, for example, that you have a main background theme that is played in a loop, and then a secondary theme that you would like to be also played in a loop when your character enters some given state.

Suche scenario led me to create a small category on CocosDenshion SimpleAudioManager which defines four methods:


-(void) playForegroundMusic:(NSString*)filePath loop:(BOOL)loop;
-(void) stopForegroundMusic;
-(void) pauseForegroundMusic;
-(void) resumeForegroundMusic;

playForegroundMusic will simply play your secondary theme on top of your background music without claiming exclusive access to the audio subsystem. You can find it on my github together with the rest of my Cocos2D snippets.

 

Categories
Main

Blur effect for Cocos2D textures on retina displays

I have recently needed to blur a Cocos2d texture to simulate an “out-of-view-field” effect. As it usually happens, it had been already implemented under the name of AWTextureFilter by Manuel Martínez-Almeda, who made it available on github.

Integrating it into my app was really easy, but it turned out that there was a problem with @2x textures on retina displays, both iPhone and the new iPad. So, I just fixed this and published a https://github.com/sdesimone/AWTextureFilter“>fork for anyone who would like to use it.

 Update 27 May:

Due to the lengthy calculations that AWTextureFilter does, it can be pretty slow and block your UI for up to a couple of seconds. Lengthy calculations scream for background execution, but the stock version of AWTextureFilter will not work correctly when executed on a secondary thread due to it eventually calling the Open GL layer.
So I made some changes to blur method and it is now possible to create a blurred texture off the main thread; this will not block the UI. I hope you enjoy it.

Categories
Main Tutorials

cocos2d and the new retina iPad – take 2

I described in a previous post about cocos2d and the new retina iPad a quick solution to the problem of supporting higher-resolution images. The main objective of that work was to make easier re-building your app for submission on the App Store before  you could possibly provide higher-resolution images for the new iPad. I mentioned anyway that that approach was just a quick hack and not  a full answer to the need to provide 4 (four!) different versions for any of your artworks.

Now, I would like to quickly describe a different approach aimed at reducing that number to 2 (if you are willing to discontinue support for older iPhones and iPods, up to the iPhone 3GS/iPod Touch 3G). This is possible by:

  1. providing a “@2x” (aka “-hd”) version;
  2. providing a “@2x-ipad” (aka “-hd-ipad”) version;
  3. making the non-retina iPads (iPad 1 and 2) use the “@2x” version by default.

This is made possible through a new category I wrote that you can add to your project to transparently get the behavior described above.

The code

As you can see, the changes are pretty trivial. The only thing you have to do is inspect the two constants defined at the beginning of the file to check that file suffixes are ok with your conventions.

[sourcecode]

//
// CCFileUtils+SDSDeviceSuffix.m
// MantraPhoneTest
//
// Created by sergio on 3/19/12.
// Copyright 2012 Sergio De Simone, Freescapes Labs. All rights reserved.
//

#import "cocos2d.h"

#define CC_IPAD_DISPLAY_FILENAME_SUFFIX @"~ipad"
#define CC_RETINA_IPAD_DISPLAY_FILENAME_SUFFIX @"-hd~ipad"

#ifdef CC_RETINA_IPAD_DISPLAY_FILENAME_SUFFIX

//////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////
@implementation CCFileUtils (SDSDeviceSuffix)

//////////////////////////////////////////////////////////////////////////////////////////////
+ (NSFileManager*)localFileManager {
static NSFileManager *__localFileManager = nil;

if (!__localFileManager)
__localFileManager = [[NSFileManager alloc] init];
return __localFileManager;
}

//////////////////////////////////////////////////////////////////////////////////////////////
+ (NSString*)getPathForSuffix:(NSString*)path suffix:(NSString*)suffix {

NSString *pathWithoutExtension = [path stringByDeletingPathExtension];
NSString *name = [pathWithoutExtension lastPathComponent];

//– check if path already has the suffix.
if( [name rangeOfString:suffix].location != NSNotFound ) {

CCLOG(@"cocos2d: WARNING Filename(%@) already has the suffix %@. Using it.", name, suffix);
return path;
}

NSString *extension = [path pathExtension];

if( [extension isEqualToString:@"ccz"] || [extension isEqualToString:@"gz"] )
{
// All ccz / gz files should be in the format filename.xxx.ccz
// so we need to pull off the .xxx part of the extension as well
extension = [NSString stringWithFormat:@"%@.%@", [pathWithoutExtension pathExtension], extension];
pathWithoutExtension = [pathWithoutExtension stringByDeletingPathExtension];
}

NSString *retinaName = [pathWithoutExtension stringByAppendingString:suffix];
retinaName = [retinaName stringByAppendingPathExtension:extension];

if( [[self localFileManager] fileExistsAtPath:retinaName] )
return retinaName;

CCLOG(@"cocos2d: CCFileUtils: Warning HD file not found (%@): %@", suffix, [retinaName lastPathComponent] );

return nil;
}

//////////////////////////////////////////////////////////////////////////////////////////////
+ (NSString*)getDoubleResolutionImage:(NSString*)path {

#if CC_IS_RETINA_DISPLAY_SUPPORTED

NSString * retinaPath;

if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {

if (CC_CONTENT_SCALE_FACTOR() == 2) {
if ((retinaPath = [self getPathForSuffix:path suffix:CC_RETINA_IPAD_DISPLAY_FILENAME_SUFFIX])) {
return retinaPath;
} else if ((retinaPath = [self getPathForSuffix:path suffix:CC_IPAD_DISPLAY_FILENAME_SUFFIX])) {
return retinaPath;
} else if ((retinaPath = [self getPathForSuffix:path suffix:CC_RETINA_DISPLAY_FILENAME_SUFFIX])) {
return retinaPath;
}
} else {
if ((retinaPath = [self getPathForSuffix:path suffix:CC_IPAD_DISPLAY_FILENAME_SUFFIX])) {
return retinaPath;
} else if ((retinaPath = [self getPathForSuffix:path suffix:CC_RETINA_DISPLAY_FILENAME_SUFFIX])) {
return retinaPath;
}
}

} else {

if (CC_CONTENT_SCALE_FACTOR() == 2) {
if ((retinaPath = [self getPathForSuffix:path suffix:CC_RETINA_DISPLAY_FILENAME_SUFFIX])) {
return retinaPath;
}
}
}

#endif // CC_IS_RETINA_DISPLAY_SUPPORTED

return path;
}

@end

#endif

[/sourcecode]

 

It is available on github and free for your use.