This project, just like the Pong Game took two iterations to complete; With and without a hash-map to deal with deleted objects and avoid passing raw pointers that could potentially lead to memory leaks or a program crash
/*
* Carlos Adan Cortes De la Fuente
* All rights reserved. Copyright (c)
* Email: dev@carlosadan.com
*/
std::hash<std::string> s_hash; // Hash creator of type string
class GameObjectFactory : public IMissileExplosionEvent
{
private:
exEngineInterface* mEngine;
// Counters to properly create a unique handle name
int mMissileCounter;
int mExplosionCounter;
int mCityCounter;
public:
// Constructor
GameObjectFactory(exEngineInterface* pEngine) : mEngine(pEngine),
mMissileCounter(0),
mExplosionCounter(0),
mCityCounter(0)
{
World::mExplosionListeners.push_back(this); // Registers itself to the explosion events
srand((unsigned int)time(NULL)); // Makes the random number generator work
}
// Destructor
~GameObjectFactory() {}
// Static Game Objects
void CreateCrosshair()
{
GameObject* pGO = new GameObject(s_hash("Crosshair"));
COGTransform* transform = new COGTransform(pGO, { 0.0f, 0.0f });
pGO->AddComponent(transform);
COGMouse* mouse = new COGMouse(pGO);
pGO->AddComponent(mouse);
COGBoxShape* shape = new COGBoxShape(pGO, 20.0f, 20.0f);
pGO->AddComponent(shape);
COGRender* render = new COGRender(pGO, mEngine, RenderType::Line);
pGO->AddComponent(render);
pGO->Initialize();
}
void CreateScore()
{
GameObject* pGO = new GameObject(s_hash("Score"));
COGTransform* transform = new COGTransform(pGO, { 310.0f, 25.0f });
pGO->AddComponent(transform);
COGText* text = new COGText(pGO, mEngine->LoadFont("pixel.ttf", 20));
pGO->AddComponent(text);
COGRender* render = new COGRender(pGO, mEngine, RenderType::Font);
pGO->AddComponent(render);
pGO->Initialize();
}
// Dynamic Game Objects
GameObjectHandle CreateCity(float xPos)
{
// Get random value to add to the hash
int hashRandom = rand() % 1001;
GameObject* pGO = new GameObject(s_hash(std::to_string(hashRandom) + "-" + std::to_string(mCityCounter) + "City"));
mCityCounter++;
COGTransform* transform = new COGTransform(pGO, { xPos, 585.0f });
pGO->AddComponent(transform);
COGBoxShape* shape = new COGBoxShape(pGO, 50.0f, 30.0f);
pGO->AddComponent(shape);
COGPhysics* physics = new COGPhysics(pGO, false);
pGO->AddComponent(physics);
COGRender* render = new COGRender(pGO, mEngine, RenderType::Fill);
render->GetColor().mColor[0] = 37;
render->GetColor().mColor[1] = 106;
render->GetColor().mColor[2] = 219;
pGO->AddComponent(render);
pGO->Initialize();
return pGO->GetHandle();
}
void CreateFriendlyMissile(exVector2 vTarget)
{
// Get random value to add to the hash
int hashRandom = rand() % 1001;
GameObject* pGO = new GameObject(s_hash(std::to_string(hashRandom) + "-" + std::to_string(mMissileCounter) + "FriendlyMissile"));
mMissileCounter++;
// Determines
float xPos;
// Divides the screen in 3 sections to spawn the missiles
if (vTarget.x <= 267)
{
xPos = 50.0f;
}
else if (vTarget.x > 267 && vTarget.x <= 533)
{
xPos = 400.0f;
}
else
{
xPos = 750.0f;
}
COGTransform* transform = new COGTransform(pGO, { xPos, 600.0f });
pGO->AddComponent(transform);
COGLineShape* shape = new COGLineShape(pGO, { xPos, 600.0f });
pGO->AddComponent(shape);
COGPhysics* physics = new COGPhysics(pGO, false);
pGO->AddComponent(physics);
COGMissile* missile = new COGMissile(pGO, vTarget, MissileType::Friendly);
pGO->AddComponent(missile);
COGRender* render = new COGRender(pGO, mEngine, RenderType::Fill);
render->GetColor().mColor[0] = 38;
render->GetColor().mColor[1] = 214;
render->GetColor().mColor[2] = 76;
pGO->AddComponent(render);
pGO->Initialize();
}
void CreateEnemyMisile()
{
// Similar to friendly missile but targeting a specific existing city
}
void CreateExplosion(exVector2 vOrigin)
{
// Get random value to add to the hash
int hashRandom = rand() % 1001;
GameObject* pGO = new GameObject(s_hash(std::to_string(hashRandom) + "-" + std::to_string(mExplosionCounter) + "Explosion"));
mExplosionCounter++;
COGTransform* transform = new COGTransform(pGO, vOrigin);
pGO->AddComponent(transform);
COGCircleShape* shape = new COGCircleShape(pGO, 0.0f);
pGO->AddComponent(shape);
COGPhysics* physics = new COGPhysics(pGO, true);
pGO->AddComponent(physics);
COGExplosion* explosion = new COGExplosion(pGO);
pGO->AddComponent(explosion);
COGRender* render = new COGRender(pGO, mEngine, RenderType::Fill);
render->GetColor().mColor[0] = 224;
render->GetColor().mColor[1] = 149;
render->GetColor().mColor[2] = 11;
pGO->AddComponent(render);
pGO->Initialize();
}
// I Missile Explosion Event Interface
virtual void OnExplosionStart(exVector2 vOrigin) override
{
CreateExplosion(vOrigin);
}
};
/*
* Carlos Adan Cortes De la Fuente
* All rights reserved. Copyright (c)
* Email: dev@carlosadan.com
*/
typedef unsigned int Hash;
class GameObject
{
private:
std::vector<Component*> mComponents;
public:
Hash mHash;
GameObject(Hash hash) : mHash(hash)
{
GameObjectInventory::GetInstance()->Register(this);
}
~GameObject()
{
for (Component* pComponent : mComponents)
{
pComponent->Destroy();
delete pComponent;
}
GameObjectInventory::GetInstance()->Unregister(this);
}
void Initialize()
{
for (Component* pComponent : mComponents)
{
pComponent->Initialize();
}
}
void AddComponent(Component* pComponent)
{
mComponents.push_back(pComponent);
}
GameObjectHandle GetHandle()
{
GameObjectHandle temp;
temp.mHash = mHash;
return temp;
}
template<class T>
T* FindComponent(ComponentType eType)
{
for (Component* pComponent : mComponents)
{
if (pComponent->GetType() == eType)
{
return (T*)pComponent;
}
}
return nullptr;
}
};
/*
* Carlos Adan Cortes De la Fuente
* All rights reserved. Copyright (c)
* Email: dev@carlosadan.com
*/
typedef unsigned int Hash;
class GameObjectHandle
{
public:
Hash mHash;
GameObjectHandle() {}
~GameObjectHandle() {}
bool IsValid() const { return GameObjectInventory::GetInstance()->Exists(mHash); }
GameObject* Get() const { return GameObjectInventory::GetInstance()->Lookup(mHash); }
};
/*
* Carlos Adan Cortes De la Fuente
* All rights reserved. Copyright (c)
* Email: dev@carlosadan.com
*/
class GameObjectInventory
{
private:
static GameObjectInventory* mInstance;
HashMap<GameObject*, 256> mMap;
std::vector<GameObjectHandle> mStaleObjects;
public:
GameObjectInventory() {}
~GameObjectInventory() {}
// Singleton Pattern Implementation
// Accesor method for the instance
static GameObjectInventory* GetInstance()
{
if (mInstance == nullptr)
{
mInstance = new GameObjectInventory();
}
return mInstance;
}
// Destructor method for the class
static void DestroyInstance()
{
if (mInstance != nullptr)
{
delete mInstance;
mInstance = nullptr;
}
}
// Removes the copy constructor
GameObjectInventory(const GameObjectInventory&) = delete;
// Removes the copy assignment operator
GameObjectInventory& operator= (const GameObjectInventory) = delete;
// Handle System Manipulation
void Register(GameObject* pGO) { mMap.InsertNoCheck(pGO); }
void Unregister(GameObject* pGO) { mMap.Delete(pGO); }
bool Exists(Hash hash) const { return (mMap.Find(hash) != nullptr); }
GameObject* Lookup(Hash hash) { return mMap.Find(hash); }
const GameObject* Lookup(Hash hash) const { return mMap.Find(hash); }
/* Deletion of Game Objects */
void deleteOnFrameEnd(GameObjectHandle goHandle) { mStaleObjects.push_back(goHandle); }
void deleteStaleObjects()
{
// Loops to delete stale game objects using the handle
if (mStaleObjects.size() > 0)
{
for (GameObjectHandle goHandle : mStaleObjects)
{
if (goHandle.IsValid())
{
GameObject* staleObject = goHandle.Get();
delete staleObject;
}
}
mStaleObjects.clear(); // Clears the game objects to be deleted
}
}
};
// Singleton Patter Implementation
GameObjectInventory* GameObjectInventory::mInstance = nullptr;
/*
* Carlos Adan Cortes De la Fuente
* All rights reserved. Copyright (c)
* Email: dev@carlosadan.com
*/
// Used integer as a mask to access the different buckets containing lists of game objects
typedef unsigned int Hash;
template<class T, int SIZE>
class HashMap
{
private:
Hash mMask;
std::list<T> mBuckets[SIZE];
int Translate(Hash hash) const
{
return (hash & mMask); // Makes the bitwise operation to get the bucket index
}
public:
HashMap()
{
mMask = SIZE - 1;
}
~HashMap() {}
void InsertNoCheck(T& data)
{
int index = Translate(data->mHash);
mBuckets[index].push_back(data);
}
T Find(Hash hash)
{
int index = Translate(hash);
std::list<T>& bucket = mBuckets[index];
for (auto& value : bucket)
{
if (value->mHash == hash)
{
return value;
}
}
return nullptr;
}
const T Find(Hash hash) const
{
int index = Translate(hash);
const std::list<T>& bucket = mBuckets[index];
for (auto& value : bucket)
{
if (value->mHash == hash)
{
return value;
}
}
return nullptr;
}
void Delete(T data)
{
int index = Translate(data->mHash);
std::list<T>& bucket = mBuckets[index];
bucket.remove(data);
}
};