Make Your Own iPhone Game, Day 11

February 8th, 2010


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

Make Your Own iPhone Game, Day 10

February 6th, 2010


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));
}

Acoustic Jamming

February 6th, 2010


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.

Make Your Own iPhone Game, Day 8

February 3rd, 2010


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];
		}
	}
}

Make Your Own iPhone Game, Day 7

February 1st, 2010

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];

Car Freedom

January 29th, 2010

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

Make Your Own iPhone Game: Days 4 & 5

January 27th, 2010


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 Two and Three

January 25th, 2010

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.

Writing a Book About Writing an iPhone Game, Day One

January 20th, 2010

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.

Screenshot, Day 1

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 ) ]];

How I Un-funked My Flow

January 16th, 2010

George Clinton gets funky

George Clinton gets funky

2009 was the funk. Not “the funk” as in George Clinton, but the funk as in a gunky soul. 2010 couldn’t have come at a better time. It’s a new decade! Time to break out of the shell. Time to bust all the old barriers. Time to be happy. Time to completely let go. Time to stop living in the past. Time to stop staying in. Time to go out! Time to live. Time to thrive!

Thanks to Ron for the mentorship. Thanks to Beaver for the support. Thanks to Rachel for taking me camping. Thanks to Colin for always reminding me of what it is to live.

So easy to resolve to thrive. So difficult to go from funked to flowing. It’s all a trick of the curve of time. We’re either trending up or trending down. What seems like a plateau is just the the outer edge of the parabola, the “curve” of the earth to us ants.

So the first days of my resolve to thrive were sputtered. One minute I was up. The next minute I was down and all seemed unreachable, hopeless. For the first time I looked up the definition of despair in the dictionary.

Then, drop by drop, the water came back into my soul. And for both our benefits, here are the grams that finally tipped the scale:

  • Got a pair of suspenders (wow, I do have an ass)
  • Listened to Jeff Olson’s “The Slight Edge” on audio CD (thanks Glen)
  • Started writing a book about iPhone game programming (to generate cashflow and forget to worry about money)
  • Got fresh sheets, blankets and an awesome memory-foam mattress for my berth
  • Started to eat new foods
  • Stopped writing lists (just began doing what truly felt most important)
  • Tore off the door to my v-berth (doors are not meant for boats)
  • Began lifting weights again (gained 3 pounds already)
  • Started a habit of getting off the boat each morning (used to spend all day in the cave)
  • Went out dancing (and finally learned a few breakdancing basics)
  • Sold a lot of unnecessary equipment
  • Got a new pair of boots (and was given my first pair of Vans — thanks again Colin)
  • Found creative ways to reduce my expenses
  • Tossed the seats out of my dinghy (standing up is way more fun)
  • Put my car up for sale (was really hard to let go of my mental dependency on this luxury)
  • Started to look at fixed-gear bicycles (planning to purchase next week)
  • Cut a hole in my roof for a hatch (no more having to hunchback on the steps to the back deck)
  • Cut away half the kitchen to make room for a drum set
  • Got drums and put them on the boat!!!!!!! (lifelong dream)
  • Began to focus on getting to know new people
  • Started holding auditions for a drummer for Bullets in a Burning Box
  • Finally put my “couch” up for surf on couchsurfing.org (have always wanted to give back to the travel community)
  • Started standing straighter (my left shoulder used to scrunch from playing guitar so much)
  • Started to remember people’s names again
  • Got a new water filter cartridge
  • Drank up all the caffeinated tea on the boat (now using my urge for a kick to get ashore more)
  • Finally went in for an eye exam to get hard contacts (softies never worked for my astigmatism)
  • Remembered that I have an album to finish!!

I’ve noticed a couple things about myself that I really, really like:

  1. I’ve gone to bed and even let whole days go by without smoking any weed.
  2. I’ve noticed that I am befriending people for no particular reason, and enjoying my existing friendships more than ever.

In other words, my funky habits are dissolving and new, strong, good habits are forming.

Hope this blog entry inspires you! Start to make your dreams come true right now!!!!

Storyteller   .:.   Musician   .:.   Vagabond   .:.   Wizard