Tutorial: Creating a spaceshooter in HGE – Part 3

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:

enemy spritesheets

Enemy spritesheets

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();
	}
Enemies!

Enemies!

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! :D

Effects

We’re going to create a nice explosion effect now:

Explosion-Sprite-Sheet

Explosion-Sprite-Sheet

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. :D 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!

Leave a Reply