How to batch create SD images from HD in 1,2,3 steps :)

With iPhone 4 and all, now we need two sets of art assets, one for SD and one for HD (for retina display). So my artist gives me the assets in HD, and I:

  1. Make a copy of the assets.
  2. Use A Better Finder Rename 8 to rename the copied assets’ file names (append the suffix @2x or -hd).
  3. Select all the original assets, right click, and open it in Preview. Once in Preview make sure all the assets are selected and then click Tools > Adjust Size (oh, the sizes of the assets should be uniform), and then you change then width and height to SD. And then Save All. (Source: http://www.usingmac.com/2008/6/10/batch-resize-images)
  4. And then, what step 4? We are done 🙂

EDIT:

Or, if you happen to be using Cocos2D, you can use TexturePacker, to create a SD spritesheet from a HD spritesheet, with only 1 click!

Open your TexturePacker file with HD images (texture and data file name should end with @2x or -hd), and then click on AutoSD,

and when you publish, you will automatically have an SD version.

There we go, only one step 🙂

Advertisements

Hey guys, our company’s first app just got released on the appstore. It’s called Visual Travel Checklist, it’s basically a travel checklist app, what makes it different from other travel checklist apps, is that our is well, visual (hence the name, lol). And being visual and all, we have more than 250 tasks and items icons that you can add to your checklist, and if that’s still not enough for you, you can also add your own tasks and items, using your own pictures from your iPhone gallery. One way that I would use the custom items is, take a picture of all my shirts and shoes and add them to my list, so I can make sure that I pack my favorite tshirt.

Spiel from iTunes page:

Create your own reusable travel checklists and check off icons as you pack! All visually!

Mobili Studio presents Visual Track Checklist (VTC), a travel essential application that helps you pack and unpack for your trips. VTC provides an intuitive user interface with vibrant visuals. Who says packing has to be a bothersome and wordiness procedure? VTC redefines the packing process by functioning visually. With VTC, you will never forget anything for your time away from home!

Features
•Intuitive and easy-to-navigate user interface!
•Over 250+ tasks and items, all represented by its own unique icons!
•Check and uncheck tasks and items in the easy-to-view packing area!
•Customize up to 100 tasks and items that are not in the default database!
•Choose from 6 different themes that best suit your personal style!
•Create up to 5 different checklists that cater to your needs and reuse them over and over again!
•Create your own list from scratch or from the 4 pre-defined sample lists!
•Manage travel checklists for multiple trips!
•Sort items by categories, alphabetical orders, or by items that are unchecked/checked!
•Built-in visual tutorial!
•Supports Retina Display with high resolution graphics!
•iOS 4 compatible!

iTunes Links:

http://itunes.apple.com/us/app/visual-travel-checklist/id425870850?mt=8

http://itunes.apple.com/us/app/visual-travel-checklist-lite/id431203459?mt=8

So check it out 🙂 We also have a lite version that you guys can try out 🙂 Thanks 🙂 Although I’d really appreciate it if you guys buy our app 🙂

How to optimize your PNG images?

There are a lot of free and online softwares for optimizing PNG images, here is a short list of them:

  • PunyPNG (talked about in this blog post) :

photo: Heckyeah my PunyPNG now allows the max of 500 KB and uploads up to 75 files :) The default is 150 KB and 15 files, but if you donate you get the increase. PunyPNG, for me is one of the most efficient PNG compressors out there, first of all it’s online, so you can access it anywhere, and second, it’s really is pretty good at compressing compared to other compressors out there. See comparison chart on their website: http://punypng.com/about/comparison PunyPNG website: http://punypng.com/

The default is 150 KB and 15 files, but if you donate you get the increase of 500 KB and uploads up to 75 files 🙂

PunyPNG, for me is one of the most efficient PNG compressors out there, first of all it’s online, so you can access it anywhere, and second, it’s really is pretty good at compressing compared to other compressors out there.

See comparison chart on their website: http://punypng.com/about/comparison

PunyPNG website: http://punypng.com/

  • Smush.it™

Smush.it is by Yahoo! and it is also available online.

From the website:

Smush.it uses optimization techniques specific to image format to remove unnecessary bytes from image files. It is a “lossless” tool, which means it optimizes the images without changing their look or visual quality. After Smush.it runs on a web page it reports how many bytes would be saved by optimizing the page’s images and provides a downloadable zip file with the minimized image files.

Smush.it website: http://www.smushit.com/ysmush.it/

  • ImageOptim

ImageOptim is free and open source! And its for Mac users.

From the website:

ImageOptim optimizes images — so they take up less disk space and load faster — by finding best compression parameters and by removing unnecessary comments and color profiles. It handles PNG, JPEG and GIF animations.

ImageOptim website: http://imageoptim.pornel.net/

  • PNGShrink PNG file compressor for Mac

Website: http://www.macupdate.com/app/mac/33222/pngshrink

  • Ping! PNG file compressor for Mac

Website: http://www.macupdate.com/app/mac/23055/ping!

Other sources:

I don’t really know which one is the best, so what I do is I drop everything in ImageOptim first and then, I upload those that are less than 500KB to PunyPNG to see if it can compress a bit more. And then dump those that are more than 500KB to Smush.it, Smush.it doesn’t have a file size limit you see (at least it doesn’t say it does, but it couldn’t upload my 2MB PNG file). And until all those tell me that are are no more savings, that prolly means that I have compressed my PNGs until they can no longer be compressed.

Tutorial: The step two to making a ‘Talking’ iPhone app, when to record and when to stop recording

This post is related to the following posts:

The ‘Talking’ app, you say something and an animal repeats what you say in a cute voice.

Well, we can’t really ask the player to tap the animal to make it record, we want the animal to simply record something when the player say something, and then stop recording when the player stopped talking, and then play it. So how do we detect if the player stopped talking?

How to start recording when detecting sound, and stop recording when detect silence?

From Stack Overflow:

Perhaps you could use the AVAudioRecorder’s support for audio level metering to keep track of the audio levels and enable recording when the levels are above a given threshold. You’d need to enable metering with:

[anAVAudioRecorder setMeteringEnabled:YES];

and then you could periodically call:

[anAVAudioRecorder updateMeters];
power
= [anAVAudioRecorder averagePowerForChannel:0];
if (power > threshold && anAVAudioRecorder.recording==NO) {
   
[anAVAudioRecorder record];
} else if (power < threshold && anAVAudioRecorder.recording==YES) {
   
[anAVAudioRecorder stop];
}

Or something like that.

Source: http://stackoverflow.com/questions/3855919/ios-avaudiorecorder-how-to-record-only-when-audio-input-present-non-silence

According to the API, averagePowerForChannel returns the average power of the sound being recorded. If it returns 0 that means that recording is at its full scale, the maximum power (like when someone shouts really really loudly into the mic?), while -160 is the minimum power or near silence (which is what we want right, near silence?).

Another tutorial (Tutorial: Detecting When a User Blows into the Mic by Dan Grigsby), you can also use peakPowerForChannel. He made an algorithm to get the lowPassResults of the audio input:

From the tutorial:

Each time the timer’s callback method is triggered the lowPassResults level variable is recalculated. As a convenience, it’s converted to a 0-1 scale, where zero is complete quiet and one is full volume.

We’ll recognize someone as having blown into the mic when the low pass filtered level crosses a threshold. Choosing the threshold number is somewhat of an art. Set it too low and it’s easily triggered; set it too high and the person has to breath into the mic at gale force and at length. For my app’s need, 0.95 works.

- (void)listenForBlow:(NSTimer *)timer {
[recorder updateMeters];

const double ALPHA = 0.05;
double peakPowerForChannel = pow(10, (0.05 * [recorder peakPowerForChannel:0]));
lowPassResults = ALPHA * peakPowerForChannel + (1.0 - ALPHA) * lowPassResults;

if (lowPassResults > 0.95)
NSLog(@"Mic blow detected");

}

Source: http://mobileorchard.com/tutorial-detecting-when-a-user-blows-into-the-mic/

So I am using this Dan’s algorithm, except the the threshold number, I’m still testing it out, it really is somewhat of an art.

Okay, now we know when the player STOPS talking, what about when the user starts talking? We wouldn’t be able to know that since we stopped recording after the player stops talking, right? We won’t be able to get the power for the channel with a stopped recorder.

And StackOverflow comes to the rescue again, I read somewhere that you should have TWO AVAudioRecorders, instead of ONE. One AVAudioRecorder to monitor the power for channel at all times and one to actually record your player’s voice.

So we have:

NSURL *monitorTmpFile;
NSURL *recordedTmpFile;
AVAudioRecorder *recorder;
AVAudioRecorder *audioMonitor;

And some booleans to keep track of when it is recording or playing:

BOOL isRecording;
BOOL isPlaying;

We have to initialize both controllers, somewhere in your init add:

[self initAudioMonitor];
[self initRecorder];

The functions:

-(void) initAudioMonitor
{    NSMutableDictionary* recordSetting = [[NSMutableDictionary alloc] init];
    [recordSetting setValue :[NSNumber numberWithInt:kAudioFormatAppleIMA4] forKey:AVFormatIDKey];
    [recordSetting setValue:[NSNumber numberWithFloat:44100.0] forKey:AVSampleRateKey];
    [recordSetting setValue:[NSNumber numberWithInt: 1] forKey:AVNumberOfChannelsKey];
   
    NSArray* documentPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString* fullFilePath = [[documentPaths objectAtIndex:0] stringByAppendingPathComponent: @”monitor.caf”];
    monitorTmpFile = [NSURL fileURLWithPath:fullFilePath];
   
    audioMonitor = [[ AVAudioRecorder alloc] initWithURL: monitorTmpFile settings:recordSetting error:&error];
   
    [audioMonitor setMeteringEnabled:YES];
   
    [audioMonitor setDelegate:self];
   
    [audioMonitor record];
}

-(void) initRecorder
{    NSMutableDictionary* recordSetting = [[NSMutableDictionary alloc] init];
    [recordSetting setValue :[NSNumber numberWithInt:kAudioFormatAppleIMA4] forKey:AVFormatIDKey];
    [recordSetting setValue:[NSNumber numberWithFloat:44100.0] forKey:AVSampleRateKey];
    [recordSetting setValue:[NSNumber numberWithInt: 1] forKey:AVNumberOfChannelsKey];
   
    NSArray* documentPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString* fullFilePath = [[documentPaths objectAtIndex:0] stringByAppendingPathComponent: @”in.caf”];
    recordedTmpFile = [NSURL fileURLWithPath:fullFilePath];
   
    recorder = [[ AVAudioRecorder alloc] initWithURL: recordedTmpFile settings:recordSetting error:&error];
   
    [recorder setMeteringEnabled:YES];
   
    [recorder setDelegate:self];
   
    [recorder prepareToRecord];
}

And then we have a function that will be called all the time, to monitor your AVAudioRecorders, call it somewhere in your update:

-(void) monitorAudioController: (ccTime) dt
{  
    if(!isPlaying)
    {   [audioMonitor updateMeters];
   
        // a convenience, it’s converted to a 0-1 scale, where zero is complete quiet and one is full volume
        const double ALPHA = 0.05;
        double peakPowerForChannel = pow(10, (0.05 * [audioMonitor peakPowerForChannel:0]));
        double audioMonitorResults = ALPHA * peakPowerForChannel + (1.0 – ALPHA) * audioMonitorResults;
   
        NSLog(@”audioMonitorResults: %f”, audioMonitorResults);
       
        if (audioMonitorResults > AUDIOMONITOR_THRESHOLD)
        {    NSLog(@”Sound detected”);
       
            if(!isRecording)
            {   [audioMonitor stop];
                [self startRecording];
            }
        }   else
        {   NSLog(@”Silence detected”);
            if(isRecording)
            {   if(silenceTime > MAX_SILENCETIME)
                {   NSLog(@”Next silence detected”);
                    [audioMonitor stop];
                     [self stopRecordingAndPlay];
                    silenceTime = 0;
                }   else
                {   silenceTime += dt;
                }
            }
        }
       
        if([audioMonitor currentTime] > MAX_MONITORTIME)
        {   [audioMonitor stop];
            [audioMonitor record];
        }
    }
}

Okay, lemme explain…

You have to call [audioMonitor updateMeters], because (according to AVAudioRecorder class reference):

Refreshes the average and peak power values for all channels of an audio recorder.

And then, do you see Dan’s algorithm?

const double ALPHA = 0.05;
double peakPowerForChannel = pow(10, (0.05 * [audioMonitor peakPowerForChannel:0]));
 double audioMonitorResults = ALPHA * peakPowerForChannel + (1.0 – ALPHA) * audioMonitorResults;

NSLog(@”audioMonitorResults: %f”, audioMonitorResults);

If audioMonitorResults is greater than our threshold AUDIOMONITOR_THRESHOLD (to get this value, requires many hours of testing and monitoring, that’s why I have a NSLog there), that means we have detected sound. And we start recording!

if(!isRecording)
{   [audioMonitor stop];
    [self startRecording];
}

If it isn’t already recording, we stop the audio monitor and start recording:

-(void) startRecording
{   NSLog(@”startRecording”);
   
    isRecording = YES;
    [recorder record];
}

Okay then, if the audioMonitorResults is less than the AUDIOMONITOR_THRESHOLD and we are recording, it means that silence has been detected, but but but, we do not stop the recording at once. Why…? Because when people are speaking, we speak like this: “Hello, how are you?” instead of “Hellohowareyou”, you see the spaces between each word are also detected as silences, which is why:

if(isRecording)
{   if(silenceTime > MAX_SILENCETIME)
     {   NSLog(@”Next silence detected”);
         [audioMonitor stop];
         [self stopRecordingAndPlay];
         silenceTime = 0;
     }   else
     {   silenceTime += dt;
}

MAX_SILENCETIME is threshold for the silence time between words.

And then to make sure the size of our audioMonitor output will not explode:

if([audioMonitor currentTime] > MAX_MONITORTIME)
{   [audioMonitor stop];
    [audioMonitor record];
}

It saves the file after MAX_MONITORTIME.

And then stopRecordingAndPlay:

-(void) stopRecordingAndPlay
{    NSLog(@”stopRecording Record time: %f”, [recorder currentTime]);
   
    if([recorder currentTime] > MIN_RECORDTIME)
    {   isRecording = NO;
        [recorder stop];
       
        isPlaying = YES;
        // insert code for playing the audio here
    }   else
    {   [audioMonitor record];
    }
}

After the audio is played, call:

-(void) stopPlaying
{   isPlaying = NO;
    [audioMonitor record];
}

And there we go! 🙂

To summarize:

  • 1 AVAudioRecorder to monitor when the player starts talking and stops talking
  • 1 AVAudioRecorder to record the player’s voice
  • Use peakPowerForChannel to detect talking or silence

And that’s about it!

How to make animated sprites in Cocos2D? (using 3rd party sprites and TexturePacker)

First of all, thank you Andreas Loew for the TexturePacker and Physics Editor licenses.

This is not a tutorial per se, because a lot of information that I will include in this post are referenced from other people’s works, particularly Andreas’ (http://www.texturepacker.com/blog/).

So the question: How do we make animated sprites in Cocos2D using 3rd party sprites. I mentioned before TurboSquid as a good source for free 3D game assets, now I am telling you guys, that aside from free 3D game assets, a lot of 2D game assets are also available for free online, such as Reiner’s Tileset (all the assets that I’ll be using for is from Reiner’s Tileset).

So hop on over to Reiner’s Tileset, and find a character that you’d like, I picked this skeleton with a bow and arrow (he looks kind of cute).

Meet:

(no, I haven’t named him)

There are a lot of animations included for this character, there’s idle, talking, kicking… animations.

I will be using Andreas’ code (on the TexturePacker website) to load my sprites into the game (check out the blog post: Improved Sprite Loader). You can download the code from that blog post. The CCAnimate+SequenceLoader code loads a sequence of sprites with given format, the start index of sprite must begin with 1 (for example, sprite_0001.png sprite_0002.png, ….the format for this sequence would be “sprite_%04d.png”).

But, but the one from Reiner’s Tileset starts with 0, such as kicking_s0000… One idea is simply ignore the first frame (delete it). Okay, kidding, or rename every file. But what if the animation has like 60 frames?

Good thing, there’s an app called Better Finder Renamer, just drag the files you want to rename and then specifiy the prefix and the Starts with value. So, for our case, I specified “skeleton_kicking_00”, and starts with 1.

Next step, use TexturePacker to create our spritesheet. Click on Add Sprites and select the animation frame files. If you notice the sprite frames have a brown background (which we don’t actually need), Andreas talked about this in another blog post (Using TexturePacker with 3rd party assets).

So just remember to check:

Also check Trim sprite names, so the resulting plist will remove the file extensions of the sprite frames.

Okay, now that we have the the spritesheets ready, some code bits. Add to your project:

  • CCAnimate+SequenceLoader.h
  • CCAnimate+SequenceLoader.m
  • CCAnimation+SequenceLoader.h
  • CCAnimation+SequenceLoader.m

And then in your code, #import “CCAnimate+SequenceLoader.h”” (I placed this in the init of my scene), and then:

[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile: @”skeleton.plist”];
skeleton = [CCSprite spriteWithSpriteFrameName: @”skeleton_idle_s0001”];

CGSize size = [[CCDirector sharedDirector] winSize];        skeleton.position = ccp(size.width/2, size.height/2-10);
CCAnimate *animation = [CCAnimate actionWithSpriteSequence: @”skeleton_kicking_s%04d” delay: 0.1f restoreOriginalFrame: NO];
[skeleton runAction: [CCRepeatForever actionWithAction: animation]];
[self addChild: skeleton];

And then you are done! 🙂

You now have an animated skeleton in your game 🙂

Sources:

http://www.publicspace.net/ABetterFinderRename/

http://reinerstileset.4players.de/monstersE.html

http://www.texturepacker.com

http://www.texturepacker.com/2010/12/11/using-texturepacker-with-3rd-party-tile-sets/

http://www.texturepacker.com/2010/12/16/improved-sprite-loader/

http://www.code-and-web.de/downloads/AnimationSequenceLoader/1.0/AnimationSequenceLoader.zip