Using CALayers as HUD elements – Part II.

In my last post for iDevBlogADAy, I showed you how one can build on a simple CALayer subclass to create a resource-friendly, custom looking number display (eg. for showing scores) for your game.

In this post, I will use the same base class in a useful example on how you can implement a healthbar / speedbar. The base class I am talking about is MysteryCoconut’s MCSpriteLayer which was introduced on his great blog here.

Our speedbar can change between a zero and a max value. The graphical representation could look something like this when the speed is maxed out:

The effect we want to achieve is filling up the bar according to our player’s speed.

So first, let’s create a CALayer and load up the speedbar image onto it:


- (void) createSpeedbar {

CGImageRef speedImage = [UIImage imageWithContentsOfFile: [[NSBundle mainBundle] pathForResource:@"speedbar" ofType:@"png"]].CGImage;

fixedSize = CGSizeMake(CGImageGetWidth(speedImage), CGImageGetHeight(speedImage));
speedBar = [MCSpriteLayer layerWithImage:speedImage sampleSize:fixedSize];
[speedBar setFrame: CGRectMake(0, 0, 80.0f, 80.0f * speedBar.frame.size.height / speedBar.frame.size.width)];
speedBar.position = CGPointMake(12.0, 198.0f);
[self.layer addSublayer: speedBar];
}

You can notice a few things in the code above:

  • I specify an 80 pixel width to the speedbar regardless of the image file’s size. It’s handy to resize the bar according to your needs directly from the code, quicker to finalize the aesthetics. The height of the layer is resized according to the 80 pixel width and the original aspect ratio
  • I originally set the frame origin to 0,0 and then in the next line I set the position, this is just a matter of preference really

The CALayer we have now simply displays a static image, so now lets create a method that changes the image according to our needs. Our method’s input paramater is going to be a float called speedRatio, this is the percentage of the current speed compared to the max speed. We use this input parameter to decide how much of the speedbar we have to display.


- (void) showSpeed:(float) speedRatio {

	float prevHeight = speedBar.frame.size.height;
	CGImageRef speedImg = (CGImageRef) speedBar.contents;

	[speedBar updateContentWithFixedWidthSize: CGSizeMake( 80.0f, 80.0f * speedRatio * CGImageGetHeight(speedImg) / CGImageGetWidth(speedImg) )];
	speedBar.position = CGPointMake(speedBar.position.x, speedBar.position.y + (prevHeight - speedBar.frame.size.height) / 2.0f );
}

We update the underlying CALayer with the actual portion of the image we have to show (note that we only change the height of the layer, the width stays at a fixed 80-pixel size). But now the layer’s size changes, and as the layer is anchored by its middle point, the result is going to be: according to the changed height, both ends of the speedbar are going to move! It might suit you in a couple of situations but in the majority of the cases a speedbar has a fixed end from where the rest of the bar grows. To achieve a fixed point, we have to move the layer according to how much we changed the layer’s size. We compare the previous height with the new, updated height, divide it by two (remember, the layer used to grow in both directions, we need to eliminate one direction), and now we can offset our layer with the exact amount we need.

After implementing this method, we will have a speedbar with a fixed bottom, and a dynamically growing top part according to the speedratio we feed into the method.

All you have to do is create your UIView you want to use as your HUD, call the createSpeedbar method on it to attach the CALayer to its sublayers, then every time your player’s speed changes, call the showSpeed method with the currentSpeed / maxPlayerspeed ratio.

There you have it, a dynamic speedbar / healthbar with your own custom image!

This entry was posted in idevblogaday. Bookmark the permalink.

Comments are closed.