我正在尝试为Windows控制台编写Space Invaders克隆,感觉一切都很好,但是出现了此崩溃。该程序编译没有错误,但是在启动时立即崩溃。 我承认我不太了解指针,并且我相信问题出在那儿。
#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;
}
我认为,指针(或对象)之前似乎已经清除过,但是我不知道如何追踪它。预先感谢。
答案 0 :(得分:3)
在::Update
中遍历对象时,您可以通过各种gameObjects
方法在Playfield::Update
中添加和删除对象。这是有保证的崩溃,因为它会使for
循环中的隐式迭代器无效。
要解决此问题,请:
GameObject::Update
返回一个布尔值,该布尔值指示是否可以删除该对象。这要求您使用显式迭代器重写Playfield::Update
中的循环,以便执行it = gameObjects.erase(it);
。不过,这仍然不允许您添加新对象。markForAddition
/ markForRemoval
方法,在 Playfield::Update
完成后,将这些对象从游戏世界中移除。您将需要做一些额外的簿记工作,以确保您不会更新或绘制在同一循环中较早删除的对象,但这是可以克服的。std::list
不会在删除某个对象时使它的迭代器无效。不过,您仍然需要注意当前元素。