I'm trying to write the Space Invaders clone for Windows console, and it felt like everything was going fine, but here comes this crash. The program compiles without errors, however instantly crashes on start up. I admit that I don't know pointers very well, and I believe that the problem is somewhere there.
#include "stdafx.h"
#include <vector>
#include <random>
#include <chrono>
#include <thread>
#include <memory>
#include <SDKDDKVer.h>
#include "Vector2D.h"
#include "Renderer.h"
std::default_random_engine rGen;
typedef std::uniform_int_distribution<int> intRand;
typedef std::uniform_real_distribution<float> floatRand;
char ObjectType[][64] =
{
"ot_AlienShip",
"ot_PlayerShip",
"ot_AlienLaser",
"ot_PlayerLaser",
"ot_Explosion"
};
class PlayField;
class GameObject
{
public:
char* m_objType = nullptr;
Vector2D pos;
unsigned char sprite;
virtual void Update(PlayField& world) {};
virtual bool DecreaseHealth() { return true; };
};
class Input
{
public:
virtual bool Left() = 0;
virtual bool Right() = 0;
virtual bool Fire() = 0;
};
class RndInput : public Input
{
public:
virtual bool Left() override { floatRand keyRate(0, 1); return (keyRate(rGen) < 0.3f); }
virtual bool Right() override { floatRand keyRate(0, 1); return (keyRate(rGen) < 0.4f); };
virtual bool Fire() override { floatRand keyRate(0, 1); return (keyRate(rGen) < 0.5f); };
};
class PlayField
{
private:
typedef GameObject* GameObjPtr;
std::vector<GameObjPtr> gameObjects;
public:
Input* cotrollerInput = nullptr;
GameObject* it = new GameObject;
//it = new GameObject;
Vector2D bounds;
// Number of available active laser slots for aliens and player
int AlienLasers = 10;
int PlayerLasers = 4;
explicit PlayField(Vector2D iBounds) : bounds(iBounds) {};
const std::vector<GameObjPtr>& GameObjects() { return gameObjects; }
void Update()
{
// update list of active objects in the world
for (auto it : gameObjects)
{
it->Update(*this); //The crash is here "Unhandled exception thrown: read access violation. it was 0xFFFFFFFFFFFFFFFF."
}
}
GameObject* GetPlayerObject()
{
auto it = std::find_if(gameObjects.begin(), gameObjects.end(), [](GameObjPtr in) { return (strcmp(in->m_objType,"ot_PlayerShip")==0); });
if (it != gameObjects.end())
return (*it);
else
return nullptr;
}
void SpawnLaser(GameObject* newObj)
{
if (strcmp(newObj->m_objType, "ot_AlienLaser")==0)
AlienLasers--;
else if (strcmp(newObj->m_objType, "ot_PlayerLaser")==0)
PlayerLasers--;
AddObject(newObj);
}
void DespawnLaser(GameObject* newObj)
{
if (strcmp(newObj->m_objType, "ot_AlienLaser")==0)
AlienLasers++;
else if (strcmp(newObj->m_objType, "ot_PlayerLaser")==0)
PlayerLasers++;
RemoveObject(newObj);
}
void AddObject(GameObject* newObj)
{
//gameObjectsToAdd.push_back(GameObjPtr(newObj));
gameObjects.push_back(newObj);
}
void RemoveObject(GameObject* newObj)
{
//gameObjectsToRemove.push_back(newObj);
auto it = std::find_if(gameObjects.begin(), gameObjects.end(), [&](GameObjPtr in) { return (in==newObj); });
gameObjects.erase(it);
}
};
class Explosion : public GameObject
{
public:
// Explosion lasts 5 ticks before it dissappears
int timer = 5;
Explosion() { m_objType = new char[64]; strcpy(m_objType, "ot_Explosion"); sprite = RS_Explosion; }
~Explosion() { delete[] m_objType; }
void Update(PlayField& world) override
{
timer--;
if (!timer)
{
world.RemoveObject(this);
delete this;
}
}
};
class AlienLaser : public GameObject
{
public:
AlienLaser() { m_objType = new char[64]; strcpy(m_objType, "ot_AlienLaser"); sprite = RS_AlienLaser; }
~AlienLaser() { delete[] m_objType; }
void Update(PlayField& world) override
{
bool deleted = false;
pos.y += 1.f;
if (pos.y > world.bounds.y)
{
deleted = true;
}
GameObject* player = world.GetPlayerObject();
if (player && pos.IntCmp(player->pos))
{
deleted = true;
//Spawn explosion, kill player
GameObject& no = *(new Explosion);
no.pos = pos;
world.AddObject(&no);
world.RemoveObject(player);
}
if (deleted)
{
world.DespawnLaser((GameObject*)this);
delete this;
}
}
};
class PlayerLaser : public GameObject
{
public:
PlayerLaser() { m_objType = new char[64]; strcpy(m_objType, "ot_PlayerLaser"); sprite = RS_PlayerLaser; }
~PlayerLaser() { delete[] m_objType; }
void Update(PlayField& world) override
{
bool deleted = false;
pos.y -= 1.f;
if (pos.y < 0)
{
deleted = true;
}
for (auto it : world.GameObjects())
{
if (strcmp(it->m_objType,"ot_AlienShip")==0 && it->pos.IntCmp(pos))
{
deleted = true;
//Spawn explosion, kill the alien that we hit
GameObject& no = *(new Explosion);
no.pos = pos;
world.AddObject(&no);
if (it->DecreaseHealth())
world.RemoveObject(it);
}
}
if (deleted)
{
world.DespawnLaser(this);
delete this;
}
}
};
class Alien : public GameObject
{
public:
Alien() { m_objType = new char[64]; strcpy(m_objType, "ot_AlienShip"); sprite = RS_Alien; }
~Alien() { delete m_objType; }
private:
const float maxUpdateRate = 0.01f;
const float transformEnergy = 1.f;
enum AlienState
{
as_Normal,
as_Better
};
// Variables dictating energy level for upgrade, direction of movement, and current speed
float health = 1.f;
float energy = 0.f;
float direction = 1.f;
float velocity = 0.5f;
AlienState state{};
void Transform()
{
state = as_Better;
sprite = RS_BetterAlien;
velocity *= 2.f;
}
bool DecreaseHealth() override { health -= 1.f; return health <= 0; }
void Update(PlayField& world) override
{
pos.x += direction * velocity;
// Border check
if (pos.x < 0 || pos.x >= world.bounds.x - 1)
{
direction = -direction;
pos.y += 1;
}
// Border check vertical:
if (pos.y >= world.bounds.y - 1)
{
// kill player
GameObject* player = world.GetPlayerObject();
if (player && pos.IntCmp(player->pos))
{
//Spawn explosion
GameObject& no = *(new Explosion);
no.pos = pos;
world.AddObject(&no);
world.RemoveObject(player);
}
}
// Transform into better Alien
if (state!=as_Better)
{
floatRand updateRate(-maxUpdateRate, 2*maxUpdateRate);
energy += updateRate(rGen);
if (energy >= transformEnergy)
Transform();
}
floatRand fireRate(0, 1);
if (fireRate(rGen) < 0.5 && world.AlienLasers>0)
{
//Spawn laser
GameObject& newLaser = *(new AlienLaser);
newLaser.pos = pos;
world.SpawnLaser(&newLaser);
}
}
};
class PlayerShip : public GameObject
{
public:
PlayerShip() { m_objType = new char[64]; strcpy(m_objType, "ot_PlayerShip"); sprite = RS_Player; }
~PlayerShip() { delete m_objType; }
void Update(PlayField& world) override
{
if (world.cotrollerInput->Left())
pos.x -= 1;
else if (world.cotrollerInput->Right())
pos.x += 1;
if (world.cotrollerInput->Fire() && world.PlayerLasers>0)
{
//Spawn laser
GameObject& newLaser = *(new PlayerLaser);
newLaser.pos = pos;
world.SpawnLaser(&newLaser);
}
}
};
int main()
{
rGen.seed(1);
Vector2D size(80, 28);
Renderer mainRenderer(size);
PlayField world(size);
intRand xCoord(0, (int)size.x-1);
intRand yCoord(0, 10);
// Populate aliens
for (int k = 0; k < 20; k++)
{
Alien& a = *(new Alien);
a.pos.x = (float)xCoord(rGen);
a.pos.y = (float)yCoord(rGen);
world.AddObject(&a);
}
// Add player
PlayerShip& p = *(new PlayerShip);
p.pos = Vector2D(40, 27);
world.AddObject(&p);
world.Update();
{
RenderItemList rl;
for (auto it : world.GameObjects())
{
RenderItem a = RenderItem(Vector2D(it->pos), it->sprite);
rl.push_back(a);
}
mainRenderer.Update(rl);
// Sleep a bit so updates don't run too fast
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
return 0;
}
In addition, VS gives the following info:
I assume, that the pointer (or the object) appears to be cleared somewhere before, but I have no idea how to trace it. Thanks in advance.