Solving a Z-Ordering Issue
The square containing our hero sprite covers the rock above…
…A possible solution causes the background to bleed through…
…Ahhh. Fixed!
Cocos2D is quite a complete 2D game engine and it is always growing. However, what do you do when it just can’t do something you are intending? Well, thankfully it is written in a fairly high-level language like Objective C. You can expand the Cocos2D engine as you see fit.
In this article we will be talking about two expansions to the Cocos2D engine that were necessary to get main characters, enemies, and special effects to draw correctly in the game QuexlorLite. These two extensions are called CCZSprite
and HudLevelSprite
.
CCZSprite: A Cocos2D Extension Included in the iPhone Game Kit
Getting our barbarian (his name is Quexlor) to draw correctly in the multiple layers of ground, rocks, and trees of the QuexlorLite world was a tad tricky. The challenge was that our hero sprite unnaturally appeared on top of trees. Thanks to Ricardo and the Cocos2D community, a solution was developed. For your reference, here’s the 2nd and 3rd pages of the forum topic that led to the eventual solution.
The original challenge was one of hierarchy. All the layers of the map (a subclass of CCTMXTiledMap
) were drawn with successive z orders. However, the main character and enemy sprites were drawn as children of one of the layers. No matter what was done to change the z order of the hero, it didn’t matter because the z order was only relative to other children of that particular layer, not all the layers.
The solution started with using a 2D OpenGL projection with a decent depth buffer. This allowed all the nodes of our game to be given a more accurate z order using CCNode
‘s vertexZ
property. A range of -1000 to 0 was chosen, -1000 being the lowest vertexZ
in the stack. Entire background layers, like the ground, were given successive, automatic vertexZ
values (-999, -998, etc…). Foreground layers, like the trees, were given more detailed vertexZ
values, assigned to individual tiles of the map (trees, in this case) based on the y position in the tile map (-997.4 for a tree to the north, -997.2 for a tree to the south). These were automatically assigned detailed vertexZ
values by setting the layer property cc_vertexz
to automatic
for that particular foreground layer. Finally, all the player, enemy, and item sprites (essentially, any sprite that could possibly move) were assigned dynamic vertexZ
values based on their current y position on the map (for example, -997.3 if the hero was standing in the middle of the two previously mentioned trees). This solution finally enabled Quexlor to look right! Trees were drawn on top of him when appropriate.
The last thing that was necessary to cement this vertexZ
solution was to make our hero, enemy, and item sprites use a bit of different drawing code. Hence, the creation of CCZSprite
, a subclass of CCSprite
that simply overrides the draw
method with the vertexZ
-friendly drawing code. Here’s that draw
method:
@implementation CCZSprite
-(void) draw
{
glEnable(GL_ALPHA_TEST);
glAlphaFunc(GL_GREATER, 0.0f);
[super draw];
glDisable(GL_ALPHA_TEST);
}
@end
Pretty simple, eh?
HudLevelSprite: Another Cocos2D Addition

HudLevelSprite
in actionBorn out of the same challenge of accurate z ordering, the HudLevelSprite
is a solution for drawing sprites that must appear on top of everything else (like fog, auras, etc…). Because of the way we slide the camera around to view the level map from different points, we also needed these HUD sprites to be attached to certain level objects, hence the name “HUD Level Sprite.” (By the way, HUD stands for Heads Up Display.)
A good example of a HudLevelSprite
is the lifebar attached to the top of our hero and all the enemies. It is always drawn on top and always follows the sprite that it is attached to.
Coding a HudLevelSprite
is as simple as a method to attach to a level-based object, and another method to move when the camera has changed.
// a subclass for sprites attached to level objects
@interface HudLevelSprite : CCSprite
{
CGPoint offset;
LevelObject* attachee;
}
@property CGPoint offset;
-(void) attach:(LevelObject*)newAttachee;
-(void) move:(CGPoint)cameraPos;
@end
@implementation HudLevelSprite
@synthesize offset;
-(void) attach:(LevelObject*)newAttachee
{
// check so that we only add child once
if(attachee != newAttachee)
{
// save attachee
attachee = newAttachee;
// add to hud
[[attachee getHud] addChild:self];
}
}
-(void) move:(CGPoint)cameraPos
{
// self pos = attachee pos - camera pos + offset
CGPoint newPos = ccpSub(attachee.position, cameraPos);
newPos = ccpAdd(newPos, offset);
self.position = newPos;
}
@end
When the camera is moved, the HUD must update all the HudLevelSprite
objects. It does this using fast enumeration (the for..in
loop) in the moveSprites
method. Note the use of the isKindOf
method which is a handy extension to Cocoa’s NSObject
, part of Extensions.h/.m
in the iPhone Game Kit’s source code:
// in our HUD class...
-(void) moveSprites:(CGPoint)cameraPos
{
// move all hud level sprites
for(HudLevelSprite* s in self.children)
if([s isKindOf:@"HudLevelSprite"])
[s move:cameraPos];
}
Keeping track of sprites like this can be handy in other ways. For example, what if you want to make a type of sprite that is “touchable?” You could code something similar to a HudLevelSprite
and use a for..in
loop like the one above to query your list of sprites when your game receives a touch event. That’s just one idea…
Remember, you can extend Cocos2D and even Cocoa to your liking. Objective C is a really nice language to extend because of its easy to use class categories and lack of multiple inheritance. So, when you find yourself challenged with something that goes beyond the game engine, just create a way! Make it happen.
Enjoy creating.
Hi,
I need some help…
Is possible to solve this: http://www.cocos2d-iphone.org/forum/topic/35521
What do you think?