NSLog(@"size of unsigned long long long is %u", sizeof(unsigned long long long));
error: 'long long long' is too long for GCC
ahahahahafhffhahhahhhghgyayha!
NSLog(@"size of unsigned long long long is %u", sizeof(unsigned long long long));
error: 'long long long' is too long for GCC
ahahahahafhffhahhahhhghgyayha!
Conan makes me laugh with his “heeya!” noise
Here’s a simple bit of code which creates a cocos2d denshion sound asynchronous engine as a super-easy-to-use singleton:
static Sound* soundSingleton = nil;
@interface Sound : NSObject
{
BOOL soundReady;
}
+(Sound*) getSingleton;
+(void) purgeSingleton;
-(void) loadSound;
-(void) loadSoundFx:(NSObject*)data;
-(void) updateSound;
-(void) playSound:(int)fx;
-(void) playSound:(id)sender data:(void*)data;
-(void) playSound:(int)fx volume:(CGFloat)volume pan:(CGFloat)pan pitch:(CGFloat)pitch loop:(BOOL)loop;
@end
@implementation Sound
+(Sound*) getSingleton
{
@synchronized([Sound class])
{
if (!soundSingleton)
soundSingleton = [[self alloc] init];
}
return soundSingleton;
}
+(id) alloc
{
@synchronized([Sound class])
{
NSAssert(soundSingleton == nil, @"Tried to allocate a second instance of Sound singleton");
return [super alloc];
}
return nil;
}
+(void) purgeSingleton
{
@synchronized(self)
{
[soundSingleton release];
}
}
-(id) init
{
self = [super init];
if(self != nil)
{
// setup a few groups for sound effect playback
int groups[kSoundGroupTotal];
groups[kSoundGroupSingle] = 1; // only one at a time
groups[kSoundGroupMultiple] = 16; // 16 sounds at a time
// initialize audio manager asynchronously as it can take a few seconds
[CDAudioManager initAsynchronously:kAudioManagerFxPlusMusicIfNoOtherAudio
channelGroupDefinitions:groups channelGroupTotal:kSoundGroupTotal];
}
return self;
}
- (void) dealloc
{
soundSingleton = nil;
[super dealloc];
}
-(void) loadSound
{
// we will check this later
soundReady = NO;
// sound engine is already ready
if ([CDAudioManager sharedManagerState] == kAMStateInitialised)
{
[self loadSoundFx:nil];
}
// load sound fx when engine is done
else
{
// don't ask me how this works
NSInvocationOperation* op = [[[NSInvocationOperation alloc]
initWithTarget:self selector:@selector(loadSoundFx:) object:nil] autorelease];
NSOperationQueue *opQ = [[[NSOperationQueue alloc] init] autorelease];
[opQ addOperation:op];
}
}
-(void) loadSoundFx:(NSObject*)data
{
// wait until engine is ready...
while([CDAudioManager sharedManagerState] != kAMStateInitialised)
[NSThread sleepForTimeInterval:0.1];
// create requests to load sound fx
NSMutableArray *loadRequests = [[[NSMutableArray alloc] init] autorelease];
[loadRequests addObject:[[[CDBufferLoadRequest alloc] init:kFxKarate filePath:@"karate.wav"] autorelease]];
[loadRequests addObject:[[[CDBufferLoadRequest alloc] init:kFxBelch filePath:@"belch.wav"] autorelease]];
[loadRequests addObject:[[[CDBufferLoadRequest alloc] init:kFxChomp filePath:@"chomp.aiff"] autorelease]];
[loadRequests addObject:[[[CDBufferLoadRequest alloc] init:kFxStab filePath:@"stab.caf"] autorelease]];
// load fx in the background
CDSoundEngine* engine = [CDAudioManager sharedManager].soundEngine;
[engine loadBuffersAsynchronously:loadRequests];
}
-(void) playSound:(int)fx
{
[self playSound:fx volume:1.0f pan:1.0f pitch:1.0f loop:NO];
}
-(void) playSound:(int)fx volume:(CGFloat)volume pan:(CGFloat)pan pitch:(CGFloat)pitch loop:(BOOL)loop
{
[self updateSound];
if(soundReady)
{
CDSoundEngine* engine = [CDAudioManager sharedManager].soundEngine;
[engine playSound:fx channelGroupId:kSoundGroupMultiple pitch:pitch pan:pitch gain:volume loop:loop];
}
}
-(void) updateSound
{
// check if sound buffers have completed loading
if([CDAudioManager sharedManager].soundEngine.asynchLoadProgress >= 1.0f)
{
// done
soundReady = YES;
//[[CDAudioManager sharedManager] setBackgroundMusicCompletionListener:self selector:@selector(backgroundMusicFinished)];
//[[CDAudioManager sharedManager].soundEngine setChannelGroupNonInterruptible:CGROUP_NON_INTERRUPTIBLE isNonInterruptible:TRUE];
[[CDAudioManager sharedManager] setResignBehavior:kAMRBStopPlay autoHandle:TRUE];
}
//NSLog(@"Sound buffers loading %0.1f", [CDAudioManager sharedManager].soundEngine.asynchLoadProgress);
}
@end
Conan finally kicks Skeletor’s ass!
A bit of the code to that determine’s if a character can attack another character (for example, if the player can attack an enemy):
-(BOOL) canAttack:(Character*)character
{
CGFloat xDist = fabs(self.position.x - character.position.x);
CGFloat yDist = fabs(self.position.y - character.position.y);
return (((xDist * xDist) + (yDist * yDist)) < (attackDistance * attackDistance));
}
This is the song “Abaddon Threads” by Bullets in a Burning Box. It is a ten-year-old song next year.
Jammin’ out a drum beat. This one is dedicated to my long-time friend and amazingly inspirational drummer Matthew T Wells.
Skeletor Burnin’ the Rug
Tiled has a beautiful object layer where you can put characters, treasure chests, warp points to other levels and whatever else you can imagine. You just create an object layer then click on the object layer to create an object. Give it a distinct type so you can instantiate the object in your code.
For example, in my first level, I have a “player” object and a “skeleton” object. Here’s some of the code that instantiates the characters:
// explore object layer
NSMutableArray* ra = [objects objects];
for(i = 0; i < [ra count]; i++)
{
// get this object
CCTMXObject* o = [ra objectAtIndex:i];
// the tmx map will instantiate objects of known classes
// in trying this out it was too tricky and less control
// so we only mess with objects of type CCTMXObject
if([o class] == NSClassFromString(@"CCTMXObject"))
{
// loop over the object's properties
NSMutableDictionary* props = [o properties];
id type = [props valueForKey:@"type"];
// setup the player
if([type isEqualToString:@"player"])
{
Player* player = [[[Player alloc] init] autorelease];
player.position = [self propsXYToPoint:props];
[self addChild:player z:1 tag:kTagPlayer];
}
// setup a skeleton
if([type isEqualToString:@"skeleton"])
{
Skeleton* skeleton = [[Skeleton alloc] init];
skeleton.position = [self propsXYToPoint:props];
[self addChild:skeleton];
}
}
}
Conan gets real, real sexy.
Look how smooth his flow is flowing!
I spent most of day 7 researching Cocos2D iPhone’s sprites, sprite caches, animations and atlases. The thorn in my mind was trying to figure out a way to erase the ugly “transparent” brown color from the bitmaps using purely code. I was searching in earnest for this way because I don’t want to have to update thousands of bitmaps and erase ugly brown colors.
At the end of the day I gave up and just decided to pack the textures with Zwoptex and then remove the ugly brown color in Photoshop after it packed a nice PNG for me. Before Zwoptex would accept the bitmaps I had to convert them to PNG first. After an hour of frustratingly trying to do this with the horribly stupid Automator, I found the following command line utility which worked like a charm:
sips -s format png *.bmp --out pngs
I also had a bit of fun re-learning how circles work. Yay. Trigonometry is fun. Woooo. Here’s how the joystick button position is converted into player movement:
joybtnRadians = atan2f(newpos.x - joypad.position.x, newpos.y - joypad.position.y);
if(joybtnRadians < -(M_PI * 0.875) || joybtnRadians > (M_PI * 0.875))
{move.y -= step;}
else if(joybtnRadians < -(M_PI * 0.625))
{move.x -= step; move.y -= step;}
else if(joybtnRadians < -(M_PI * 0.375))
{move.x -= step;}
else if(joybtnRadians < -(M_PI * 0.125))
{move.x -= step; move.y += step;}
else if(joybtnRadians > (M_PI * 0.625))
{move.x += step; move.y -= step;}
else if(joybtnRadians > (M_PI * 0.375))
{move.x += step;}
else if(joybtnRadians > (M_PI * 0.125))
{move.x += step; move.y += step;}
else
{move.y += step;}
And the joystick button position is “clamped” to the edge of the joypad with the following code. Don’t ask me why we have to subtract half a PI for x and add half a PI for y… It just works that way.
if( joybtnDist > kMaxJoybtnDist)
{
newpos.x = joypad.position.x + (23.45f * cosf(joybtnRadians - (M_PI / 2)));
newpos.y = joypad.position.y + (23.45f * sinf(joybtnRadians + (M_PI / 2)));
joybtnDist = kMaxJoybtnDist;
}
[joybtn setPosition:newpos];
Ahh! To exit the world of freeways, cops, registration, road rage, repairs, rush hour, tires, maintenance, driving, smog, and gas: what a relief!
Thinking back on all the times in my life without a car, I realize they have truly been the best, most memorable, most fun, challenging, physically rewarding times of my life! Growing up I walked to school and have the fondest memories. I traveled the world without a car for a year and it was beyond blissful. I lived in my home town with just a bicycle recently and completely loved it.
Its time. This car has a new owner
Day 5. 0:15. He floats as graceful as Tinkerbell in the night.
Conan. He doesn’t cry. He doesn’t even walk. He just floats everywhere but on top of rocks. Here’s a little bit of the code to implement the joystick-style movement pad:
if( isMovingJoybtn && joybtnDist > kMovementThreshold )
{
CGPoint p = [joybtn position];
CGPoint move = ccp(0,0);
CGFloat speed = (500.0f * (joybtnDist / kMaxJoybtnDist));
CGFloat step = delta * speed;
NSLog(@"Delta=%.2f, speed=%.2f, step=%.2f",delta,speed,step);
// test movement thresholds
if( p.x < joypad.position.x )
move.x -= step;
else if( p.x > joypad.position.x )
move.x += step;
if( p.y < joypad.position.y )
move.y -= step;
else if( p.y > joypad.position.y )
move.y += step;
// move the player
if(![self movePlayerBy:move])
{
if(![self movePlayerBy:ccp(move.x, 0)])
{
[self movePlayerBy:ccp(0, move.y)];
}
}
}
Day 4. 0:46. Conan remembers to walk.. Though he gets a bit dizzy when the world starts to shake.
Day 2 of Writing a Book About Writing an iPhone Game
0:47. A little barbarian moves across the screen when I click in the corners. Though he floats over rocks and that’s just not natural.
Day 3 of Writing a Book About Writing an iPhone Game
0:50. Conan gets frisky on the rocks… Still not crying.
Guess this book ain’t just gonna write itself, so I took one heavy first step and got a tiled background to load on the iPhone simulator using cocos2d’s TMXTiledMap class and a sweet map editor program called Tiled.

Here’s a bit of the code to load the tilemap file and move the map to the center of the screen:
ColorLayer *color = [ColorLayer layerWithColor:ccc4(64,64,64,255)];
[self addChild:color z:-1];
TMXTiledMap *map = [TMXTiledMap tiledMapWithTMXFile:@"myfirsttile.tmx"];
[self addChild:map z:0 tag:kTagTileMap];
// move map to the center of the screen
CGSize ms = [map mapSize];
CGSize ts = [map tileSize];
[map runAction:[MoveTo actionWithDuration:1.0f position:ccp( -ms.width * ts.width/2, -ms.height * ts.height/2 ) ]];
Storyteller .:. Musician .:. Vagabond .:. Wizard