The Lightning
class inherits from CCDrawNode
because we refrain from using a CCSprite
for something as electric and random as a bolt of lightning. Also, the length of the bolt of lightning might change over the course of time with the tower being upgraded.
Thus, the init
function of the Lightning
class looks like this, from the Lightning.cpp
file:
bool Lightning::init(CCPoint from, CCPoint to, ccColor4F color, bool is_animated) { if(!CCDrawNode::init()) { return false; } color_ = color; GenerateKeyPoints(from , to); if(!is_animated) { DrawSegments(); } else { schedule(schedule_selector(Lightning::DrawNextSegment)); } return true; }
The init
function takes four arguments: the start and end points for the lightning bolt, the color of the bolt, and another parameter named is_animated
. If this flag is true
, the lightning bolt will be shown shooting out from source to destination and the lightning bolt will appear whole if this flag is false
.
After storing the color, we call the GenerateKeyPoints
function, passing in the source and destination of the lightning bolt. For a bolt that doesn't need animation, we draw all its constituent segments at once and for a bolt that is not animated, we schedule a function to draw a segment at each tick.
Let's now move on to the GenerateKeyPoints
function of Lightning.cpp
to define the shape of our lightning bolt:
void Lightning::GenerateKeyPoints(CCPoint from, CCPoint to) { // how many key points do we need? float distance = ccpDistance(from, to); num_key_points_ = (int)(distance / LIGHTNING_KEY_POINT_DIST); CCPoint next_point = CCPointZero; // calculate the difference between two key points CCPoint delta = ccp( (to.x - from.x) / num_key_points_, (to.y - from.y) / num_key_points_ ); for(int i = 0; i < num_key_points_; ++i) { // add the delta next_point = ccpAdd(from, ccpMult(delta, i)); // randomise the delta next_point.x += LIGHTNING_KEY_POINT_DIST * CCRANDOM_MINUS1_1(); next_point.y += LIGHTNING_KEY_POINT_DIST * CCRANDOM_MINUS1_1(); // save the key point key_points_.push_back(next_point); } }
We first calculate the number of key points the lightning bolt will require to interpolate between the source and destination. We calculate the delta between each key point and run a loop to interpolate. In this loop, we also add a random factor to each segment's vertex in order to get the electric feel of a lightning bolt. Let's now take a quick glance at the DrawSegments
and DrawNextSegment
functions of Lightning.cpp
:
void Lightning::DrawSegments() { // draw all segments at once for(int i = 0; i < num_key_points_ - 1; ++i) { drawSegment(key_points_[i], key_points_[i+1], 6, color_); } } void Lightning::DrawNextSegment(float dt) { // draw one segment at a time if(++ last_key_point_ >= num_key_points_ - 2) { unschedule(schedule_selector(Lightning::DrawNextSegment)); } drawSegment(key_points_[last_key_point_], key_points_[last_key_point_+1], 6, color_); }
As you can imagine, both these functions involve calls to the drawSegment
function with the appropriate vertices for the segment. With this, we have achieved a simple and effortless lightning bolt effect. It's time now to head back to the Tower
class and finish off the remaining functionalities.
With our shiny new bolt of lightning, the ShootLightning
function from Tower.cpp
looks relatively simple:
void Tower::ShootLightning() { // damage the enemy CCActionInterval* damage_enemy = CCSequence::createWithTwoActions( CCDelayTime::create(LIGHTNING_DURATION * 0.5f), CCCallFuncO::create(target_, callfuncO_selector( Enemy::TakeDamage), this)); target_->runAction(damage_enemy); // create the lightning without animation Lightning* lightning = Lightning::create(m_obPosition, target_->getPosition(), ccc4f(0.1098f, 0.87059f, 0.92157f, 1.0f), false); game_world_->addChild(lightning, E_LAYER_TOWER - 1); // animate the lightning CCActionInterval* shake = CCSequence::create( CCMoveTo::create(0.01f, ccp(3, 0)), CCMoveTo::create(0.01f, ccp(-3, 0)), CCMoveTo::create(0.01f, ccp(0, 3)), CCMoveTo::create(0.01f, ccp(0, -3)), NULL); lightning->runAction(CCRepeat::create(shake, 5)); // remove the lightning CCActionInterval* wait_remove = CCSequence::createWithTwoActions( CCDelayTime::create(LIGHTNING_DURATION), CCRemoveSelf::create(true)); lightning->runAction(wait_remove); }
We begin by creating a sequence of actions so that the enemy is damaged, similar to the ShootBullet
function. We then create a Lightning
object with the tower's position as the source and the target enemy's position as the destination, before adding the lightning to GameWorld
.
We also perform a simple shake effect to make the lightning bolt funkier, while running another action to remove it after a short delay. The last couple of functions in the Tower
class are CreateRangeNode
and ShowRange
, which simply create an object of class CCDrawNode
to draw a semi-transparent green circle with a border that signifies the range for this particular tower. Since you already know how to do that, we shall conclude the Tower
class and move on to the next big class: Enemy
.
3.128.198.59