The update loop

The update function for Inverse Universe will be similar to most other games where we update all the game elements and check for collisions. The code looks like this:

void GameWorld::update(float dt)
{
  // don't process if player is dying
  if(player_->is_dying_)
    return;

  // update each enemy
  CCObject* object = NULL;
  CCARRAY_FOREACH(enemies_, object)
  {
    Enemy* enemy = (Enemy*)object;
    if(enemy)
    {
      enemy->Update(player_->getPosition(), player_->GetShield() == NULL);
    }
  }

  // update each power-up
  object = NULL;
  CCARRAY_FOREACH(powerups_, object)
  {
    PowerUp* powerup = (PowerUp*)object;
    if(powerup)
    {
      powerup->Update();
    }
  }

  CheckCollisions();
  CheckRemovals();
}

We skip processing anything if the player's death animation is playing. We then iterate over the enemies_ and powerups_ arrays and update each object they contain. Finally, we check for collisions and for objects that must be removed. We will skip discussing the CheckRemovals function but to give you a gist, it basically checks the state of the must_be_removed_ flag for Enemy, PowerUp, Blast, and Missile and removes them if the flag is enabled.

Let's now look at the CheckCollisions function. This function will basically use circular collision detection by using the CIRCLE_INTERSECTS_CIRCLE function defined in GameGlobals.h. All you need to do is provide the center and radius of the two circles and the function returns true if a collision is found.

Let's take a look at the following code:

void GameWorld::CheckCollisions()
{
  // save player position & radius
  CCPoint player_position = player_->getPosition();
  float player_radius = player_->getRadius();
  
  // iterate through all enemies
  CCObject* object = NULL;
  CCARRAY_FOREACH(enemies_, object)
  {
    Enemy* enemy = (Enemy*)object;
    if(enemy)
    {
      CCPoint enemy_position = enemy->getPosition();

      // check with Player
      if(CIRCLE_INTERSECTS_CIRCLE(player_position, player_radius, 
        enemy_position, ENEMY_RADIUS))
      {
        // if shield is enabled, kill enemy
        if(player_->GetShield())
        {
          enemy->Die();
          EnemyKilled();
        }
        // else kill player...but only if enemy has finished spawning
        else if(!enemy->getIsSpawning())
          player_->Die();
      }

A lot of the collision detection revolves around the enemies. So, we first check for collisions of each enemy with the player. If a collision is found, we either kill the player or the enemy based on the shield being enabled or disabled. Another thing to consider is how we check whether the enemy is spawning and skip killing the player. We do this on purpose, or else the player will die before the enemy's spawning is complete—thereby leaving the user bewildered about the player's death.

Let's take a look at the following code:

 // check with all blasts
      CCObject* object2 = NULL;
      CCARRAY_FOREACH(blasts_, object2)
      {
        Blast* blast = (Blast*)object2;
        if(blast)
        {
          if(CIRCLE_INTERSECTS_CIRCLE(blast->getPosition(), 
            blast->getRadius(), enemy_position, ENEMY_RADIUS*1.5f))
          {
            enemy->Die();
            EnemyKilled();
          }
        }
      }

      // check with all missiles
      object2 = NULL;
      CCARRAY_FOREACH(missiles_, object2)
      {
        Missile* missile = (Missile*)object2;
        if(missile)
        {
          if(CIRCLE_INTERSECTS_CIRCLE(missile->getPosition(), 
            MISSILE_RADIUS, enemy_position, ENEMY_RADIUS*1.5f))
          {
            missile->Explode();
          }
        }
      }
    }
  }

We then proceed to check collisions of the enemy with the blasts and kill any enemy coming in contact with the blast. We also explode any missile that has come in contact with any enemy.

Let's take a look at the following code:

// check if player collides with any of the power-ups
  // activate the power-up if collision is found
  object = NULL;
  CCARRAY_FOREACH(powerups_, object)
  {
    PowerUp* powerup = (PowerUp*)object;
    if(powerup && !powerup->getIsActive())
    {
      if(CIRCLE_INTERSECTS_CIRCLE(player_position, player_radius, powerup->getPosition(), POWERUP_ICON_OUTER_RADIUS))
      {
        powerup->Activate();
      }
    }
  }
}

Finally, we check for collisions between the player and the power-ups and activate the power-up on contact with the player. That wraps up the CheckCollisions function but before we move to the Tick function, let's take a look at the EnemyKilled function:

void GameWorld::EnemyKilled()
{
  // increment counters
  ++ enemies_killed_total_;
  ++ enemies_killed_combo_;
  // reset combo time
  combo_timer_ = COMBO_TIME;

  // add score & update the label
  score_ += 7;
  char buf[16] = {0};
  sprintf(buf, "Score: %d", score_);
  score_label_->setString(buf);
}

Quite simply, we increment the total number of enemies killed as well as the number of enemies killed in the current combo. We can then use the number of enemies killed in the current combo to reward the player with some extra points when the combo timer elapses. We also reset the combo timer, increment the score, and update the HUD. Next up is the Tick function:

void GameWorld::Tick(float dt)
{
  // don't tick if player is dying
  if(player_->is_dying_)
    return;

  ++ seconds_;

  -- combo_timer_;
  // show the combo achieved if time is up
  if(combo_timer_ < 0)
    combo_timer_ = 0;
  else if(combo_timer_ == 0)
    ComboTimeUp();

  // Tick each enemy
  CCObject* object = NULL;
  CCARRAY_FOREACH(enemies_, object)
  {
    Enemy* enemy = (Enemy*)object;
    if(enemy)
    {
      enemy->Tick();
    }
  }

  // Tick each power-up
  object = NULL;
  CCARRAY_FOREACH(powerups_, object)
  {
    PowerUp* powerup = (PowerUp*)object;
    if(powerup)
    {
      powerup->Tick();
    }
  }

  // add an enemy formation every 5 seconds
  if(seconds_ % 5 == 0)
    AddEnemyFormation();
  // add a powerup formation every 4 seconds
  if(seconds_ % 4 == 0)
    AddPowerUp();
}

The first thing to do is to increment the seconds_ counter. The use of this variable will become clear in the last section when we use it to make the game progressively more difficult. Next, we decrement the combo_timer_ and call the ComboTimeUp function that displays the number of enemies killed by the player in the last 3 seconds.

Then, we call the Tick function for the Enemy and PowerUp objects. Finally, we add a new formation of enemies every 5 seconds and a new power-up every 4 seconds.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.149.249.252