Circular Health Bar

I wanted to implement a circular health bar around my player and i thought it will be nice to implement it using primitives.
But one thing i observed in primitives is, if i draw a filled rectangle using ccDrawSolidRect and when ever there is a particle effect in game, this filled rectangle is loosing its alpha :frowning: (i’m using cocos2d-x 2.0.1 version)
So i tried another approach by using CCSpriteBatchNode.
I saw many people asked about circular health bar in the forums so i figured if i explain my implementation here it might come in handy for others who wanted to achieve the same result.

Step 1:
I created a sprite sheet like this. My photoshop skills are a bit low. I just draw a circle (of radius 70 since my sprite is 50x50 size) and cut that circle at 10 degree angle with ~10 pixels width and added a stroke style.
(if anyone is curious how i did this angle stuff i used this protractor image as reference :wink:
http://www.helpingwithmath.com/images/geometry/angles_measuring07.gif)
Used same technique to create one filled one empty bar, with different colors.

Step 2:
While creating my sprite I created I also created a CCSpriteBatchNode like this and added it to the sprite.
Since i cut my healthBar at 10 degree and i added another 5 degree angle to have some space between the sprites.
so the number of circular bars are 360/15 = 24. (total circle angle = 360 degree, each bar will occupy 15 degree)

sprite = CCSprite::spriteWithFile("Tank.png");
sprite->circularHealthSheet = CCSpriteBatchNode::batchNodeWithTexture(CCTextureCache::sharedTextureCache()->addImage("healthBar.png"), 24);

for(int i=0 ; i<24 ; i++){
   CCSprite* healthBarNode = CCSprite::spriteWithTexture(sprite->circularHealthSheet->getTexture(), CCRectMake(0, 0, 8, 70));
   // setting rotation for sprite so that the sprite is arranged in circular manner
   healthBarNode->setRotation( i*15 );
   sprite->circularHealthSheet->addChild( healthBarNode, 99 );
}
sprite->circularHealthSheet->setPosition( ccp(sprite->getContentSize().width / 2, sprite->getContentSize().height/2) );
sprite->addChild( sprite->circularHealthSheet );

the result for me looks like this :smiley:

Step 3:
Now that we have health ready, lets hurt him. :wink:
I have a function damageTaken where I do damage to the tank. Inside that function I added this code. (you can add this where ever you are doing damage or wrap it in function and call that function like i did :wink: )

// getting the children inside the CCSpriteBatchNode
CCArray *nodes = this->circularHealthSheet->getDescendants();

int nodesToDeleteInHealthCircle = nodes->count()*(1.0 - (float)currentHealth/(float)totalHealth);

// calculating the percentage of health remaining
float healthPercentage = ((float)currentHealth/(float)totalHealth)*100;

// in which stage the health is in
// if stage is 0 then we show green health bar.
// if stage is 1 yellow
// if stage is 2 orange
// if stage is 3 red
int stage = 0;

// calculating the stage
if( healthPercentage > 75.0 && healthPercentage <= 100)
   stage = 0;
else if( healthPercentage > 50.0 && healthPercentage <= 75.0 )
   stage = 1;
else if( healthPercentage > 25.0 && healthPercentage <= 50.0 )
   stage = 2;
else if( healthPercentage >= 0.0 && healthPercentage <= 25.0)
   stage = 3;

// reassigning the texture rectangle for all the nodes depending
// on what stage the life is in
for(int i=0 ; icount() ; i++){
   CCSprite* tmp = (CCSprite*)nodes->objectAtIndex(i);
   // since each health bar's height is 70, I multiply the stage with 70
   // to get the y value in texture rectangle
   // if the i is less than the barsToDeleteInHealthCircle then that means they
   // should be empty so increased the x to 8 to get empty node texture
   // else x will be 0 to get filled node texture
   if( i < barsToDeleteInHealthCircle)
      tmp->setTextureRect(CCRectMake(8, stage*70, 8, 70));
   else
      tmp->setTextureRect(CCRectMake(0, stage*70, 8, 70));
}

annnnnnd the result looks like this:

Step 4:
Hold your breath! Its almost done. There is one final small thing that needs to be done. Since we added the circularHealthSheet to sprite itself so when ever we rotate the sprite the circularHealthSheet rotates along with sprite which is kind of annoying and something i dont want in my game. It kind of looks like this.

You can leave it like that if you want that in your game as feature or something. But i dont want it so where ever i rotates the sprite i inverse rotate the circularHealthSheet

sprite->setRotation( rotationAngle );
circularHealthSheet->setRotation( -rotationAngle );

Tadaaaaaaaa!

Use your imagination and creativity to change the way health bar looks :wink:

This is my 1st tutorial. So i hope i havent made any mistakes and explained every step.

This is my implementation please let me know if there any other method which is more faster than mine :slight_smile:

Thanks for your sharing. I tweak your post a bit, enable coderay to your source code.

Oh, amazing!

i would change this:

// in which stage the health is in
// if stage is 0 then we show green health bar.
// if stage is 1 yellow
// if stage is 2 orange
// if stage is 3 red
int stage = 0;

for an enum with its values:

enum Stage
{
    Green,
    Yellow,
    Orange,
    Red
};

Stage stage = Green;

But other than that, great tutorial!

it’s awesome, i so like it !

Use CCProgressTimer Luke!

Yore need 2 white image.
First is empty healthbar as CCSprite, second as CCProgressTimer, and just tone them with setColor();

2 Likes