Welcome to part three! We’re going to cover enemies, collision detection and effects in this part.
You need some files for this part, download them here.
Part 3:
- Enemies
- Collision detection
- Effects
Enemies
Let’s start by creating the enemies class, it looks similar the the players class:
#ifndef C_ENEMY_H
#define C_ENEMY_H
#pragma once
#include <hge.h>
#include <hgevector.h>
#include <hgeanim.h>
#define ENEMY_FRICTION 0.95
class c_enemy
{
private:
static HGE* hge;
hgeVector Position;
hgeVector Velocity;
hgeAnimation* Sprite;
hgeRect BoundingBox;
float Speed;
float CenterY;
float Radius;
float Angle;
bool bCenterYSet;
bool bOscillate;
short Health;
public:
c_enemy(hgeVector Position, hgeVector Velocity, short Health, HTEXTURE Texture);
~c_enemy();
bool Update(float delta);
void Render();
short GetHealth() { return Health; };
void SetHealth(short health) { Health = health; };
void SetOscillate(bool Value) { bOscillate = Value; };
float GetSpeed() { return Speed; };
void SetSpeed(float speed) { Speed = speed; };
hgeRect GetBoundingBox() { return BoundingBox; };
hgeVector GetPosition() { return Position; };
};
#endif
As you might noticed we prepare the enemy to oscillate later, this will make the “game” a bit harder.
Before we implement the enemy I want to point out that we have a few different colored enemy textures:
Each time an enemy spawns, we choose randomly one of those colors. But since we’re going to have quite a lot of those enemies we’re pre-loading those textures in main.cpp:
Add a handle for the textures:
//Enemies
HTEXTURE g_tEColors[5] = { 0 };
Load them into memory and free them afterwards:
//Enemies
g_tEColors[0] = hge->Texture_Load("images/eSpritesheet_40x30.png");
g_tEColors[1] = hge->Texture_Load("images/eSpritesheet_40x30_hue1.png");
g_tEColors[2] = hge->Texture_Load("images/eSpritesheet_40x30_hue2.png");
g_tEColors[3] = hge->Texture_Load("images/eSpritesheet_40x30_hue3.png");
g_tEColors[4] = hge->Texture_Load("images/eSpritesheet_40x30_hue4.png");
//...
for(short i = 0; i < 5; i++) hge->Texture_Free(g_tEColors[i]);
Alright, now we can start implementing the enemy class, we start by the constructor and destructor:
#include "c_enemy.h"
HGE* c_enemy::hge = 0;
c_enemy::c_enemy(hgeVector position, hgeVector velocity, short health, HTEXTURE Texture) : Position(position), Velocity(velocity), Health(health)
{
hge = hgeCreate(HGE_VERSION);
Sprite = new hgeAnimation(Texture,6,6,0,0,40,30);
Sprite->SetHotSpot(20,15);
Sprite->Play();
Speed = 0.030;
CenterY = 0;
Radius = hge->Random_Float(50.0f,80.0f);
Angle = 0.0f;
bCenterYSet = false;
bOscillate = false;
}
c_enemy::~c_enemy()
{
delete Sprite;
hge->Release();
}
There’s not much to explain about this so we’ll just continue with the update function:
bool c_enemy::Update(float delta)
{
Velocity.x *= ENEMY_FRICTION;
Velocity.y *= ENEMY_FRICTION;
Position.x += Velocity.x;
if(!bOscillate) Position.y += Velocity.y;
else
{
if(!bCenterYSet)
{
if(Velocity.y > -0.000001f && Velocity.y < 0.000001f)
{
CenterY = Position.y;
bCenterYSet = true;
}
}
Position.y = CenterY + sin(Angle) * Radius;
Angle += 2 * delta;
}
Sprite->Update(delta);
Velocity.x -= Speed;
Sprite->GetBoundingBox(Position.x, Position.y, &BoundingBox);
return false;
}
It looks very similar to the bullets update function, basic movement calculations plus the ability to oscillate.
Last method is the render function:
void c_enemy::Render()
{
Sprite->Render(Position.x, Position.y);
}
Let’s add them to our main.cpp, we start by including the class and creating a list where we can push them in:
#include "c_enemy.h" //.. std::list<c_enemy*> Enemies;
Cleaning them up in System_Initiate function:
for(auto i = Enemies.begin(); i != Enemies.end(); /**/)
{
delete (*i);
i = Enemies.erase(i);
}
inside our update function we now want to add a few enemies. We use a simple condition for this, lets say we always want five enemies on the screen:
//Enemies
if(Enemies.size() < 5)
{
short Health = hge->Random_Int(50, 100);
c_enemy* Enemy = new c_enemy( hgeVector(830, hge->Random_Int(50,550)), hgeVector(-hge->Random_Int(2,8), hge->Random_Int(-4,4)), Health, g_tEColors[hge->Random_Int(0,4)]);
Enemies.push_back(Enemy);
}
You see we use random values for Health, Position, Velocity and Texture, this will give us some different behaviors. We have to check whether the enemies are still on the screen or not, if they aren’t on screen anymore we delete them:
for(auto i = Enemies.begin(); i != Enemies.end(); /**/)
{
if((*i)->GetPosition().x < 0 || (*i)->GetPosition().y > 580 || (*i)->GetPosition().y < 20)
{
delete (*i);
i = Enemies.erase(i);
}
else
{
(*i)->Update(delta);
i++;
}
}
And finally render them:
//Enemies
for(auto i = Enemies.begin(); i != Enemies.end(); i++)
{
(*i)->Render();
}
That’s is so far, we have a few enemies in our game! In the next step collision detection will be added.
Collision detection
So, we want those bullets we can shoot to hit the spaceships, we do this by implementing collision detection using bounding-boxes. Doing collision detection by bounding-boxes is fairly easy, we have HGE’s little helper class hgeRect where we can check whether two bounding-boxes are colliding or not (Intersecting). Start by adding the collision Bullets vs Enemies inside the Update function (main.cpp):
//Collision Bullet vs Enemy
if(!Bullets.empty() && !Enemies.empty())
{
for(auto i = Bullets.begin(); i != Bullets.end(); /**/)
{
bool bHit = false;
for(auto x = Enemies.begin(); x != Enemies.end(); x++)
{
if((*x)->GetBoundingBox().Intersect(&(*i)->GetBoundingBox()))
{
(*x)->SetHealth((*x)->GetHealth() - (*i)->GetDamage());
delete (*i);
i = Bullets.erase(i);
bHit = true;
break;
}
}
if(!bHit) i++;
}
}
As you can see we’re going through both lists and check with GetBoundingBox().Intersect(&Boundingbox) for collision. If it returns true an collision occurred. Inside it we subtract the bullets damage from the enemy health and remove the bullet. Now we to check the enemy’s health, if it goes below zero we delete it:
//Check Enemy's health
for(auto i = Enemies.begin(); i != Enemies.end(); /**/)
{
if((*i)->GetHealth() <= 0)
{
delete (*i);
i = Enemies.erase(i);
}
else i++;
}
Next we add collision detection between player and the enemy. If the player collides with an enemy the player’s ship should respawn and the enemy explode:
//Enemy vs Player
for(auto i = Enemies.begin(); i != Enemies.end(); /**/)
{
if((*i)->GetBoundingBox().Intersect(&Player1->GetBoundingBox()))
{
delete (*i);
i = Enemies.erase(i);
Player1->SetPosition(hgeVector(10,268));
Player1->SetVelocity(hgeVector(0,0));
}
else i++;
}
That’s it, there are quite a few for loops going through the Enemies list, you should try making them into one big. Prepare for the next step, some explosions are on our way!
Effects
We’re going to create a nice explosion effect now:
Start by adding handles for sound and texture to main.cpp:
HTEXTURE g_tExplosion = 0; HEFFECT g_eExplosion = 0;
Alright, we build an data structure now to keep the explosions inside:
struct explosion
{
hgeAnimation* Animation;
hgeVector Position;
};
std::list<explosion> Explosions;
Obviously each explosion needs it’s animation and position. We’re storing them in an std::list. Also prepare an function that we will implement later:
void CreateExplosion(hgeVector Position);
Load the stuff into memory and free it afterwards: (Inside system_initiate)
//Explosion
g_tExplosion = hge->Texture_Load("images/Explosion-Sprite-Sheet.png");
g_eExplosion = hge->Effect_Load("sounds/21410__heigh-hoo__blow.aif");
//...
hge->Texture_Free(g_tExplosion);
hge->Effect_Free(g_eExplosion);
for(auto i = Explosions.begin(); i != Explosions.end(); /**/)
{
delete (*i).Animation;
i = Explosions.erase(i);
}
So far so good, let’s continue with the CreateExplosion function:
void CreateExplosion(hgeVector Position)
{
explosion exp;
exp.Animation = new hgeAnimation(g_tExplosion,5,10,0,0,118,118);
exp.Animation->SetHotSpot(59,59);
exp.Animation->Play();
exp.Position = Position;
Explosions.push_back(exp);
hge->Effect_PlayEx(g_eExplosion,100,0,hge->Random_Float(1,3));
}
As you can see we create an new object of our explosion structure and push into the explosion list. We also play the sound we just added with an random pitch to get some differ sounds. We shouldn’t forget to render and update our explosions in render and update function:
for(auto i = Explosions.begin(); i != Explosions.end(); i++)
{
(*i).Animation->Render((*i).Position.x, (*i).Position.y);
}
//...
for(auto i = Explosions.begin(); i != Explosions.end(); /**/)
{
if((*i).Animation->GetFrame() == 4)
{
delete (*i).Animation;
i = Explosions.erase(i);
}
else
{
(*i).Animation->Update(delta);
i++;
}
}
If the explosion is on frame four we delete it, it’s the last frame and we only want to play it once. You can add the explosion wherever you want now, like in player vs enemy or when enemy’s health is zero.
You’re done, congratulations the tutorial is over now.
But what’s next? Do you remember the oscillation ability we built in? Make use of it, for both, bullets and enemies! Also add waves, scores, maybe continues shooting?
It’s all up to you now, it’s you’re spaceshooter barebone!
Result of part three:
Download complete tutorial here. (Includes full source code)
If you need help, got feedback or want to share your result just leave me a comment!
PS: Happy new year guys! Keep programming games in 2013!
