JohnnyWorks

Programming projects and sample code

Browsing Posts published by Eric

Refer to my forums for the latest updates. You can also get a sneak peek of the features that will be added in 1.3. Long story short, I just graduated college and have been busy with all the moving around. However, I’ll finish up 1.3 over the weekend and send it to Apple by Monday. Hopefully they’ll approve it by Friday, in time for the weekend.

It fixes all the bugs people have found, and makes the game less laggy in general, as well as adding tons of new fun features. I can’t wait until people get to try it out :) .

The funny part is I didn’t think Apple would approve both the Apps this quickly, so my changelog currently says “Updated version number”. I also don’t have new screenshots ready yet. I guess I’m going to have to put off working on version 1.3 tonight :( .

I’ve decided to try a slightly higher price of $1.99 for the paid version, as the game is actually more polished now and the price should reflect that. Still, I’ll keep an eye on sales and make adjustments if needed. Another reason doing this is I’m tired of getting 1 star reviews from people who have no idea what the game is before they buy it. If you don’t like throwing stick figures in the air to go splat on the ground, then you shouldn’t buy StickWars. I hate that so many people bought it and left terrible reviews when they realized that the game didn’t appeal to them at all.

Hopefully now that the Lite version is out there, and the price is slightly higher making people think more about their purchase, the people who buy StickWars are those who actually will enjoy it and leave good reviews.

After 12 days in the top spot, StickWars has dropped to #2 in the overall paid app rankings. However, the 1.2 update was sent to Apple last Wednesday and should be live this week, and I’m expecting a great user response. Hopefully it will get bumped back up to #1!

In any case, it is still the #1 game, and I’m still working around the clock on bug fixes and enhancements. I just completed a massive graphics overhaul that ended up with the graphics look exactly the same (with maybe a few compression artifacts), but the game now runs around 20% faster. I went from using 160 separate png files for all the animations to 8 atlas sheets in the compressed pvr format. Now I can work on adding some cool fire particle-effects, since I can spare the processor usage needed.

I just finished my finals at college and am graduating in a few days, so StickWars is now getting all of the attention it deserves. There are some really cool new features coming, some ideas that are new to the genre but I think will really make it fun. I’ll give a hint: if you like the idea behind ‘Bowman’, you’ll love the new wizard spell :D .

Also, after realizing that like 80% of the google searches for StickWars that land on my website contain the terms “cheat codes” I’m going to satisfy those people who don’t have the patience to put in the time to get to level 30 before trying out all the cool powerups. This will be a surprise, with hints posted on my forums.

All these new improvements will be included in version 1.3.


After having a lot of trouble dealing with the issue myself and seeing others having trouble on the cocos2d discussions, I’m going to go ahead and post my solution which so far is working pretty well for me. Please take into account I’m new with Objective C when looking this over.

The problem is each AtlastSpriteManager can 1) refer only to a single sheet image, and 2) each AtlasSprite can only be a child of at AtlasSpriteManager. So what happens if you a few game characters that you want to share certain animations, but not all?

I took the super-class of all my game Sprites, changed its type from a Sprite (or AtlasSpriteManager, which I tried but failed to well) to a generic CocosNode. I added in this to the header. Note that I only needed my Sprites to switch between two different sprite sheets as most.

@interface Touchable : CocosNode <Collideable> {
	AtlasSprite *activeSprite;
	AtlasSprite *sprite1;
	AtlasSprite *sprite2;
...
}
- (id)init:(CGSize)sizeOne two:(CGSize)sizeTwo;
- (AtlasSpriteManager *)spriteManager1;
- (AtlasSpriteManager *)spriteManager2;
- (void)setSprite:(NSUInteger)newstate;
- (void)setActiveSpriteOne;
- (void)setActiveSpriteTwo;
...

To the implementation file…

- (AtlasSpriteManager *)spriteManager1 {
	return nil;
}
 
- (AtlasSpriteManager *)spriteManager2 {
	return nil;
}
 
- (id)init:(CGSize)sizeOne two:(CGSize)sizeTwo {
	self = [super init];
	if(self) {
		sprite1 = [[AtlasSprite spriteWithRect:CGRectMake(sizeOne.width, sizeOne.height, sizeOne.width, sizeOne.height) 
														spriteManager: [self spriteManager1]] retain];
		[[self spriteManager1] addChild:sprite1];
		if(![[self spriteManager1] parent])
			[[[LevelController sharedSingleton] gameLayer] addChild:[self spriteManager1] z:1];
		[sprite1 setVisible:NO];
 
		sprite2 = [[AtlasSprite spriteWithRect:CGRectMake(sizeTwo.width, sizeTwo.height, sizeTwo.width, sizeTwo.height) 
														spriteManager: [self spriteManager2]] retain];
		[[self spriteManager2] addChild:sprite2];
		if(![[self spriteManager2] parent])
			[[[LevelControler sharedSingleton] gameLayer] addChild:[self spriteManager2] z:1];
		[sprite2 setVisible:NO];
 
 
	}
	return self;
}
 
- (void)setSprite:(NSUInteger)newstate {
}
 
- (void)setActiveSpriteOne {
	activeSprite = sprite1;
	[sprite2 setVisible:NO];
	[sprite1 setVisible:YES];
}
 
- (void)setActiveSpriteTwo{ 
	activeSprite = sprite2;
	[sprite2 setVisible:YES];
	[sprite1 setVisible:NO];
}


A few methods of my superclass ‘Sprite’ object that I modify slightly to take changes into account. You can no longer want to run the actions (which include animations) on yourself, but on the active sprite.

 
- (void)setState:(enum ActionFigureState)newstate{
	[activeSprite stopAction:[stateActions objectAtIndex:state]];
	[self setSprite:newstate];
	[activeSprite runAction:[stateActions objectAtIndex:newstate]];
	state = newstate;
}
- (void) setPosition:(CGPoint)pos {
	[super setPosition:pos];
	[activeSprite setPosition:pos];
}

So what we have now is a superclass that is calling a instance method [self spriteManager1] in order to manage attach its two different sprites. Notice that no animation specific code is here in the superclass, except for the fact that a single ‘Sprite’ can work with 2 AtlasSheetManagers at most. You could easily take away this restriction, but I had no need to complicate things.

This gets helpful when I get into a child class that actually knows how to draw itself. Here is my “SmallStick.m” which has an empty header file except for being a child of Touchable. This implementation code also contains all the this object’s interaction with the game, such as collision detection, how to handle if it is touched, and how to add itself to the Chipmunk space.

#define width1 79
#define height1 68
#define columns1 7
 
#define width2 79
#define height2 68
#define columns2 7
 
@implementation SmallStick
 
static AtlasSpriteManager *spriteManager1 = nil;
static AtlasSpriteManager *spriteManager2 = nil;
 
+ (AtlasSpriteManager *)spriteManager1 {
	@synchronized(spriteManager1) { // I dont actually know if synchronized is needed here
		if (!spriteManager1) {
			[Texture2D saveTexParameters];
			[Texture2D setAliasTexParameters];
			spriteManager1 = [[AtlasSpriteManager alloc] initWithFile:@"smallstick_common.png" capacity:50];	
			[Texture2D restoreTexParameters];
		}
		return spriteManager1;
	}
	return nil;
}
 
+ (AtlasSpriteManager *)spriteManager2 {
	@synchronized(spriteManager2) {
		if (!spriteManager2) {
			[Texture2D saveTexParameters];
			[Texture2D setAliasTexParameters];
			spriteManager2 = [[AtlasSpriteManager alloc] initWithFile:@"smallstick_weaponless.png" capacity:50];	
			[Texture2D restoreTexParameters];
		}
		return spriteManager2;
	}
	return nil;
}
 
- (AtlasSpriteManager *)spriteManager1 {
	return [SmallStick spriteManager1];
}
 
- (AtlasSpriteManager *)spriteManager2 {
	return [SmallStick spriteManager2];
}
 
-(id)init {
	self = [super init:CGSizeMake(width1, height1) two:CGSizeMake(width2, height2)];
	if(self) {
          ...
	}
	return self;
}
 
- (void) setSprite:(NSInteger)newstate {
	switch	(newstate) {
		case fState_charging:
		case fState_attacking:
		case fState_recovering:
			[self setActiveSpriteTwo];
			break;
 
		default:
			[self setActiveSpriteOne];
			break;
	}
}
 
- (void)loadActions {
	AtlasAnimation *chargeA = [AtlasAnimation animationWithName:@"weaponless_charge" delay:0.05f];
	for(int i=0;i<18;i++) {
		int x= i % columns2;
		int y= i / columns2;
		[chargeA addFrameWithRect: CGRectMake(x*width2, y*height2, width2, height2) ];
	}
	Action *charge = [RepeatForever actionWithAction:[Animate actionWithAnimation: chargeA]];
 
        ...
 
	stateActions = [[NSArray alloc] initWithObjects:
									charge, dying, ..., nil];

The part I like about this is when I want to reuse the ’smallstick_common.png’ atlas sheet I can just not override the class method +(AtlasSpriteManager *)spriteManager2 in my child classes.

Don’t forget to remove the sprite from the AtlasManager when you’re done.

	[[self spriteManager1] removeChild:sprite1 cleanup:YES];
	[[self spriteManager2] removeChild:sprite2 cleanup:YES];

First of all, I hope this doesn’t have some hidden downside. As I said, I’m new to the platform. But I hope somebody finds this useful.

Thanks to everyone for being so patient! After working very hard for the past couple weeks, the 1.2 update is completely ready. The changelog is enormous, as I put in a lot of time adding every feature asked for that was feasible in the time frame. We also added in tons of new graphics, which really add a lot of polish and fun to the game. In addition, we now have background music tracks composed just for StickWars! I hope everyone enjoys them.

Don’t worry though, I added more options, such as the ability to disable background music, so you can listen to your own music on your iPod. For a complete list of the changes, check out the post on my forums.

The 1.2 and 1.1 Lite are actually the same game, but Apple won’t let me skip the version 1.1 for whatever reason. I’m hoping the Lite version finally gets through so people have the chance to try my game to see if it fits them before they buy. I’d like to get more than a 3.5 star rating, but I blame Apple’s rejection of the Lite version for a lot of this.

I’ll be putting up some new screenshots on the StickWars page shortly.

Exactly one week after release, StickWars has hit the top spot in the App Store. For something that started out a little over a month ago as a project to help me learn Objective C,  I can say that I never expected this.

I want to thank all my friends who put so much of their time into playing my test versions back when it wasn’t really fun to play :) . I also owe a lot to my other testers who provided me a lot of valueable feedback.

I’m still hard at work for future updates, so enjoy playing and look forward to the game improving over time.

After a few days of intense labor, the graphics and tweaks are finished. The game visually is improved, but behind the scenes I did a lot of work to patch small issues and make the gameplay smoother. Of course, nobody will notice this, but I expect that the game will feel a lot more polished and people will enjoy.

I didn’t have time to add in new enemies or game modes as planned, but we have some pretty neat ideas for those in the upcoming weeks.

A few teaser screenshots are here.

Also, just noting for the record, StickWars is currently #10 in paid apps on the App Store :D .

At last! Wizards, Archers, difficulty levels, global high scores, and more!

Enjoy.

Welcome!

15 comments

Welcome to my site. I initially created it with the purpose of showcasing my development experience while creating my first iPhone game, StickWars. However, after the release and huge market response to the game, this site mainly serves to provide news and support for StickWars. You can find answers to many questions in my forums as well as post any other questions you might have.

While a lot of the posts are about updates about StickWars, I try to post code snippets and tips to get through roadblocks that I experienced while building my first iPhone game. If you have questions or would like to see some code to help you along, contact me and I’ll try to throw something up.

When working with Cocos 2d iPhone, a lot of the sample code provided that shows how to hook into the Chipmunk physics engine using static functions outside of any Objective C object. While this is the most straightforward way to get the engine up and running, it restricts what you can do later on.

Instead, you need to create a static function which takes two parameters, the object being updated and the objects container. Here’s some code:

This is the static function which goes outside your class implementation. Notice that we don’t actually do any of the object updates here. This is good, because we can’t access any of the internal state in the container in this method.

static void updateEachShapeCallback(void *ptr, void *parent)
{
	GameController *parentObject = (GameController *)parent;
	[parentObject updateShape: ptr];
}

Note that my container, called GameController, is a CocosNode so when were ready to start the game we can call

[self schedule: @selector(step:)];

to update the shapes.
This goes inside the implementation of your container.

- (void) step: (ccTime) delta
{
    int steps = 1;
    cpFloat dt = delta/(cpFloat)steps;
    for(int i=0; iactiveShapes, &amp;updateEachShapeCallback, self);
    cpSpaceHashEach(space-&gt;staticShapes, &amp;updateEachShapeCallback, self);
}

Add in the updateShape:(void *)ptr method to your GameController interface file and then implement as

- (void) updateShape:(void *)ptr {
	cpShape *shape = (cpShape*) ptr;
	Touchable *obj = shape-&gt;data;
 
	if(obj) {
		cpBody *body = shape-&gt;body;
		[obj setPosition: cpv( body-&gt;p.x, body-&gt;p.y)];
		[obj setRotation: (float) CC_RADIANS_TO_DEGREES( -body-&gt;a )];
 
		// do whatever else you want here! you have all the the state you should need!
		// just avoiding creating objects anywhere from here as that will slow your game down
	}
}

I adopted this solution from instructions I found at Using native Objective-C methods for C Callbacks.