Most objects in the game fire bullets and they all pretty much need to be checked for collisions against bullets as well; the bottom line—bullets are important in Alien Attack. The game has a dedicated BulletHandler
class that handles the creation, destruction, updating, and rendering of bullets.
There are two types of bullets in the game, PlayerBullet
and EnemyBullet
, both of which are handled in the same
BulletManager
class. Both of the bullet classes are declared and defined in Bullet.h
:
class PlayerBullet : public ShooterObject { public: PlayerBullet() : ShooterObject() { } virtual ~PlayerBullet() {} virtual std::string type() { return "PlayerBullet"; } virtual void load(std::unique_ptr<LoaderParams> pParams, Vector2D heading) { ShooterObject::load(std::move(pParams)); m_heading = heading; } virtual void draw() { ShooterObject::draw(); } virtual void collision() { m_bDead = true; } virtual void update() { m_velocity.setX(m_heading.getX()); m_velocity.setY(m_heading.getY()); ShooterObject::update(); } virtual void clean() { ShooterObject::clean(); } private: Vector2D m_heading; }; // Enemy Bullet is just a Player Bullet with a different typename class EnemyBullet : public PlayerBullet { public: EnemyBullet() : PlayerBullet() { } virtual ~EnemyBullet() {} virtual std::string type() { return "EnemyBullet"; } };
Bullets are very simple, they just move in one direction and at a certain speed.
The BulletHandler
class uses two public functions to add bullets:
void addPlayerBullet(int x, int y, int width, int height, std::string textureID, int numFrames, Vector2D heading); void addEnemyBullet(int x, int y, int width, int height, std::string textureID, int numFrames, Vector2D heading);
The
BulletHandler
class is also a singleton. So, if an object wants to add a bullet to the game, it can do so using one of the above functions. Here is an example from the ShotGlider
class:
TheBulletHandler::Instance()->addEnemyBullet(m_position.getX(), m_position.getY() + 15, 16, 16, "bullet2", 1, Vector2D(-10, 0));
This will add a bullet at the current location of ShotGlider
, with a heading vector of V(-10,0).
Both add
functions are very similar; they create a new instance of PlayerBullet
or EnemyBullet
and then push it into the correct vector. Here are their definitions:
void BulletHandler::addPlayerBullet(int x, int y, int width, int height, std::string textureID, int numFrames, Vector2D heading) { PlayerBullet* pPlayerBullet = newPlayerBullet(); pPlayerBullet->load(std::unique_ptr<LoaderParams>(new LoaderParams(x, y, width, height, textureID, numFrames)), heading); m_playerBullets.push_back(pPlayerBullet); } void BulletHandler::addEnemyBullet(int x, int y, int width, int height, std::string textureID, int numFrames, Vector2D heading) { EnemyBullet* pEnemyBullet = new EnemyBullet(); pEnemyBullet->load(std::unique_ptr<LoaderParams>(new LoaderParams(x, y, width, height, textureID, numFrames)), heading); m_enemyBullets.push_back(pEnemyBullet); }
A big advantage of having a separate place to store bullets like this, rather than have objects themselves manage their own bullets, is that there is no need to pass objects around just to get their bullets to check collisions against. This BulletHandler
class gives us a centralized location that we can then easily pass to the collision handler.
The update
and
draw
functions are essentially just loops that call each bullet's respective functions, however the update
function will also destroy any bullets that have gone off the screen:
for (std::vector<PlayerBullet*>::iterator p_it = m_playerBullets.begin(); p_it != m_playerBullets.end();) { if((*p_it)->getPosition().getX() < 0 || (*p_it) ->getPosition().getX() >TheGame::Instance()->getGameWidth() || (*p_it)->getPosition().getY() < 0 || (*p_it)-> getPosition().getY() >TheGame::Instance()->getGameHeight() || (*p_it)->dead())// if off screen or dead { delete * p_it; // delete the bullet p_it = m_playerBullets.erase(p_it); //remove } else// continue to update and loop { (*p_it)->update(); ++p_it; } }
18.118.31.67