Archive for the ‘Wizardry’ Category

Make Your Own iPhone Game, Day 19?

Saturday, February 20th, 2010

NSLog(@"size of unsigned long long long is %u", sizeof(unsigned long long long));
error: 'long long long' is too long for GCC

ahahahahafhffhahhahhhghgyayha!

Make Your Own iPhone Game, Day 11

Monday, 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

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

Make Your Own iPhone Game, Day 8

Wednesday, 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

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

Make Your Own iPhone Game: Days 4 & 5

Wednesday, 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

Monday, 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

Wednesday, 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 to Open a Coconut

Tuesday, December 8th, 2009


How to open a coconut without the aid of a very large knife..

The Ultimate Key to Life

Thursday, August 13th, 2009

You know it. You’ve always known it. You remember it again and again. Friends, books, epiphanies: these things keep pointing the way.

The ultimate key to life is right here, right now.. inside you.

It’s not there! It’s not right over there!

It’s right here.

The Ultimate Key to Life

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