Extending CCProgressTimer to use different sprites depending on percentage value

by on Nov.14, 2013, under Cocos2d-x, Technical posts

I have developed a little extension for CCProgressTimer control in cocos2d-x. I needed to use different images (sprites) for my progress bar depending on the percentage, and I did not want to enter that code into my Scene’s code. Let’s Imagine the following case:

We have a progress bar and we would like to use a different CCSprite depending on the percentage. For example a green bar when the values are between 25%-100%. Then when below 25% an orange bar (or even more, a different one for each 25% block). Of course we can control this using a simple “if” statement in our “update” logic. But as our game’s code size would increase, it could be a bit messy. I decided to create an extension from CCProgressTimer. It works in a way that every time we assign a new “setPercentage”, it will check which sprite is supposed to use. Having stored previously all sprites in std::map, including the percentages in which that sprite shall be used.

I think is better if we start building our extended control, so we will understand better its functionality. First of all, we will create a small class / data type to store each sprite and its percentage limits:

class ProgressStates
{
public:
	/* The sprite to be used in the CCProgressTimer when the current state is active */
	CCPointer <CCSprite> m_stateSprite; 

	/* The current state lower limit */
	float lowerLimit; 

	/* The current state upper limit */
	float upperLimit; 
};

As you can see ProgressStates is a simple class with 3 elements / properties:

  • CCSprite object containing the sprite that we will use as a progress bar for this state.
  • float containing this state lower limit (percentage).
  • float containing this state upper limit (percentage).

Now we have our states data type class. One of our main CCProgressTimerMulti class methods will be the “addState” method. Within this method we will add a new state to our progress bar, with its lower and upper limits together with the sprite to be used:

void CCProgressTimerMulti::addState 
                           ( const float _lowerLimit, 
                             const float _upperLimit,
                             const string _spriteName,
                             const int _alpha )
{
        // State initialization
       ProgressStates temp = ProgressStates();
       temp.lowerLimit = _lowerLimit;
       temp.upperLimit = _upperLimit;
       temp.m_stateSprite = 
              CCSprite::createWithSpriteFrameName (_spriteName.c_str());
       temp.m_stateSprite->setOpacity(_alpha);

       // First state added to this control, 
       //will be always the default current state
       if (currState == -1)
       {
              currState  = 0;
              currLowerLimit = _lowerLimit;
              currUpperLimit = _upperLimit;
              this->initWithSprite(CCSprite::createWithSpriteFrameName
                                                (_spriteName.c_str()));           
       }      

        // Add the state to our states list
       m_states->insert(make_pair(m_states->size(),temp));
}

This method receives as a parameter the upper and lower limits, the string containing the sprite’s name to be used and the alpha value for it. The first thing we do is to create a new ProgressState object assigning the values which we have received as a parameter (creating a new CCSprite object using the image name received as a parameter). Then if this is the first state, we will assign it as the current state. We will be able to change it later on.

Now the other method that needs to be implemented is the “setPercentage”. With it we will assign a new percentage to our progress bar. If this percentage is not between the current upper and lower limits, we will change the sprite for the correct one. Let’s better see the code for it:

void CCProgressTimerMulti::setPercentage (float _percentage)
{
       // Check if the new percentage is NOT between 
       // the current limits (upper and lower)
       if ( (_percentage < currLowerLimit) || 
             (_percentage > currUpperLimit) )
       {
              map <int, ProgressStates>::iterator states;
              ProgressStates tempState;

              // Iterate throw all states in order to find the 
              // new correct state
              for (states = m_states->begin(); 
                    states != m_states->end(); states++)
              {                    
                     tempState = states->second;

                     // If the new percentage is between 
                     // the current iterated state limits
                     if (tempState.lowerLimit <= _percentage && 
                         tempState.upperLimit >= _percentage)
                     {
                           // Exchange the current state information 
                           // and CCPorgresTimer sprite
                           currState = states->first; 
                           currLowerLimit = tempState.lowerLimit;
                           currUpperLimit = tempState.upperLimit;
                           setSprite(tempState.m_stateSprite);                           
                     } 
              }
       }
       // Call CCPorgresTimer what we hidde using this metod
       this->CCProgressTimer::setPercentage(_percentage); 
}

What I am trying to do, is to hide the “setPercentage” function from CCPorgresTimer adding some extra functionality before calling it. As you can see in the code, there is no secret here, just check if the given percentage is between the current state limits. If not, we just need to find the adequate state, and set it as current.

Now we just need to include this class into our scene. To do it, we will instantiate it for example as follows:

CCProgressTimerMulti m_fuelBar;
m_fuelBar = new CCProgressTimerMulti();

m_fuelBar->setPosition(ccp( 200,200 ));

// 4 States(every 25%), with different sprites on each one.
m_fuelBar->addState(0,25,"0_25_img.png",255);
m_fuelBar->addState(25,50,"25_50_img.png",255);
m_fuelBar->addState(50,75,"50_75_img.png",255);
m_fuelBar->addState(75,100,"75_100_img.png",255);

// Type of CCProgressTimer, set anchor and midpoint
m_fuelBar->setType(kCCProgressTimerTypeBar);
m_fuelBar->setMidpoint(ccp(0,0));
m_fuelBar->setAnchorPoint(ccp(0,0));

// To make the height 100% all the time (only use the 
m_fuelBar->setBarChangeRate(ccp(1,0));          

// Add this control to the current scene
this->addChild(m_fuelBar);

As you can see in the code above, there is not much secret. First we create an object of our new extended CCProgressTimerMulti. We treated it as a normal CCProgressTimer, assigning a position, changeRate, AnchorPoint, etc. The only difference, is that we have to create the states with its different sprites and limits. In this example I would like to use a different sprite every 25%.

Last, we include this into our update method:

// Change you_value for the variable you are 
// willing to show into the progress bar !!!!!!
m_fuelBar->setPercentage(your_value);

And this is it folks. You can find the source code of this example in github here. The file names are CCProgressTimerMulti.h and CCProgressTimerMulti.cpp


Leave a Reply