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 (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
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
Step 3:
Now that we have health ready, lets hurt him.
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 )
// 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
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