std::vector 精灵分割错误
std::vector Sprite segmentation fault
我 post 在一个名为 OpenClassrooms 的法语论坛上尝试过后,我在这里 post,但没有答案,我 post 也在这里。
所以我警告你,我是 C++ 和 SFML 的新手,所以可能有一万个错误,而且我读的书似乎是一本非常糟糕的书,所以我尝试用 Bjarne Stroustrup 的书来纠正它.
我的问题如下:
我在按下 Enter 或 Space 时创建射弹(同一个键盘上有两个玩家)。每次我按下它,我都会为新的射弹创建一个射弹精灵的副本,然后我们将它放入 std::vector<sf::Sprite>
中。问题是当我启动游戏时,如果两个玩家同时按下他们的射击键(Enter 和 Space)(我的意思是,只要第一个弹丸可见),游戏就会崩溃并显示Segmentation Fault (core dumped)
。为了解决这个问题,我创建了两个精灵(每个玩家一个),并为他们的射弹影响它。问题是当他们的攻击速度很高时,他们可以在第一颗炮弹消失之前发射第二颗炮弹,所以碰撞会有问题,因为有两次相同的精灵......而第一个不会起作用。所以,为了解决这个问题,我想使用 std::vector。顺便说一下,我不会尝试只为两个玩家解决这个问题,我打算添加更多,所以我需要一些可以与 1000 名玩家一起使用的东西,例如(当然我不会对 1000 名玩家这样做,但如果它适用于该数量,它也适用于 5 个玩家)。
为了创建我的射弹,我使用了对 Sprite 对象的引用,这要感谢我的 class 游戏中的方法。此引用是对 std::vector 中精灵的引用。我还意识到,如果我们发射第一颗炮弹,等待它消失,然后让两个玩家射击,它会正常工作(有时,有时也会崩溃)......我不明白为什么,但主要是当我开始游戏崩溃了。
这是我的代码:
std::vector<sf::Sprite> sprites;
int main()
{
Game game;
sf::ContextSettings settings;
settings.antialiasingLevel = 8;
sf::RenderWindow window(sf::VideoMode(1600, 900), "Bombardes", sf::Style::Default, settings);
sf::Texture text;
if (!text.loadFromFile("resources/projectile.png")) {
logg.error("Could not create texture for projectile. Aborting.");
}
Bombard bomb(50, 150, &game, &pSprite, &movement2); // player class
Bombard bomb2(1550, 850, &game, &pSprite2, &movement);
std::vector<std::shared_ptr<Projectile>> p;
while (window.isOpen())
{
sf::Event event;
while (window.pollEvent(event))
{
switch (event.type) {
case sf::Event::Closed:
window.close();
break;
case sf::Event::KeyPressed:
if (event.key.code == sf::Keyboard::Return) { // Player 2
auto current2 = std::chrono::steady_clock::now();
auto elapsed2 = std::chrono::duration_cast<std::chrono::milliseconds>(current2 - last2);
if (elapsed2.count() >= 1000 / bomb2.getAttackSpeed()) { // Time the frequency of shots
last2 = std::chrono::steady_clock::now();
if (bomb2.getAmmo() > 0) { // Check if there's still ammo
sprites.push_back(sf::Sprite(text)); // New sprite in vector
p.push_back(std::make_shared<Projectile>(bomb2.getPos().getX(), bomb2.getPos().getY(), &sprites[sprites.size()-1], &game, bomb2.getProjectileMovement(), bomb2.getPenetration(),
bomb2.getSpeed())); // Create the projectile
bomb2.fire(); // Remove an ammo
}
}
}
else if (event.key.code == sf::Keyboard::Space) { // Player 1
auto current = std::chrono::steady_clock::now();
auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(current - last);
if (elapsed.count() >= 1000 / bomb.getAttackSpeed()) {
last = std::chrono::steady_clock::now();
if (bomb.getAmmo() > 0) {
sprites.push_back(sf::Sprite(text));
p.push_back(std::make_shared<Projectile>(bomb.getPos().getX(), bomb.getPos().getY(), &sprites[sprites.size()-1], &game, bomb.getProjectileMovement(), bomb.getPenetration(), bomb.getSpeed()));
bomb.fire();
}
}
}
break;
}
}
}
return 0;
}
当然,我删除了一些部分,我不会复制粘贴我的整个代码,有无用的东西。如果你觉得你需要我的 classes(射弹、游戏、实体、轰击),我会 post 他们。
我想它可以帮助您了解 Projectile 的构造函数:
Projectile::Projectile(int posX, int posY, sf::Sprite *sprite, Game *game, sf::Vector2f direction, double penetration, double speed) {
/** @brief Full constructor.
@param int posX : The X position on the map.
@param int posY : The Y position on the map.
@param sf::Sprite *sprite : A pointer to the sprite.
@param Game *game : A pointer to the game.
@param sf::Vector2f direction : The direction in which the projectile will move. */ }
感谢您的帮助!
已经发生了很多事情,所以我可能错过了一些事情。
有一件事看起来很奇怪:两名球员在射击时使用相同的 Projectile
指针!
玩家 1 射击:第一个射弹是堆分配的,您将其地址保存在 p
中。到目前为止一切顺利。
然后玩家 2 射击。您创建了一个新的射弹(具有正确的位置和精灵等...)但是,您还将其地址存储在 p
.
除非您在代码中的其他地方保存了第一个射弹的地址,否则您如何设法访问它?你怎么知道它是否达到了目标(然后,玩家 1 应该得分)或者是否已经超出了屏幕(然后你可以删除它以清理内存)?
我怀疑它周围有东西。也许您应该尝试将所有射弹存储在 std::vector<Projectile*>
或更好的 std::vector<std::unique_ptr<Projectile>>
中。这样(如果我没有理解代码的话)玩家可能可以发射不止一枚炮弹。
(如果您想知道 unique_ptr
部分,请不要介意询问)
让我们了解您的尝试,好吗?
好的,所以,经过一天的努力,感谢 giant_teapot 的帮助,我想我终于解决了这个问题。但我不确定。
我不再试验这个问题了,但我不明白为什么。
我决定不使用原始指针,而是使用智能指针。好像没有bug了。但为什么 ?我没有改变任何东西。有件事我不明白。但这是更改后的代码:
std::vector<sf::Sprite> sprites;
int main()
{
Game game;
sf::ContextSettings settings;
settings.antialiasingLevel = 8;
sf::RenderWindow window(sf::VideoMode(1600, 900), "Bombardes", sf::Style::Default, settings);
sf::Texture text;
if (!text.loadFromFile("resources/projectile.png")) {
logg.error("Could not create texture for projectile. Aborting.");
}
Bombard bomb(50, 150, &game, std::make_shared<sf::Sprite>(pSprite), &movement2);
Bombard bomb2(1550, 850, &game, std::make_shared<sf::Sprite>(pSprite2), &movement);
std::vector<std::shared_ptr<Projectile>> p;
while (window.isOpen())
{
sf::Event event;
while (window.pollEvent(event))
{
switch (event.type) {
case sf::Event::Closed:
window.close();
break;
case sf::Event::KeyPressed:
if (event.key.code == sf::Keyboard::Return) { // Player 2
auto current2 = std::chrono::steady_clock::now();
auto elapsed2 = std::chrono::duration_cast<std::chrono::milliseconds>(current2 - last2);
if (elapsed2.count() >= 1000 / bomb2.getAttackSpeed()) { // Time the frequency of shots
last2 = std::chrono::steady_clock::now();
if (bomb2.getAmmo() > 0) { // Check if there's still ammo
sprites.push_back(sf::Sprite(text)); // New sprite in vector
p.push_back(std::make_shared<Projectile>(bomb2.getPos().getX(), bomb2.getPos().getY(), std::make_shared<sf::Sprite>(sprites[sprites.size()-1]), &game, bomb2.getProjectileMovement(), bomb2.getPenetration(), bomb2.getSpeed())); // Create the projectile
bomb2.fire(); // Remove an ammo
}
}
}
else if (event.key.code == sf::Keyboard::Space) { // Player 1
auto current = std::chrono::steady_clock::now();
auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(current - last);
if (elapsed.count() >= 1000 / bomb.getAttackSpeed()) {
last = std::chrono::steady_clock::now();
if (bomb.getAmmo() > 0) {
sprites.push_back(sf::Sprite(text));
p.push_back(std::make_shared<Projectile>(bomb.getPos().getX(), bomb.getPos().getY(), std::make_shared<sf::Sprite>(sprites[sprites.size()-1]), &game, bomb.getProjectileMovement(), bomb.getPenetration(), bomb.getSpeed()));
bomb.fire();
}
}
}
break;
}
}
}
return 0;
}
你会明白我也改变了我的 classes。但它仍然没有解释为什么原始指针使整个事情崩溃而智能指针正常工作。
再次非常感谢 giant_teapot,他们在很多时间里帮助了我,并向我展示了这些精彩的建议。还要感谢 Martin Bonner 让他把这些给我看。
编辑:在我的代码中进行了一些更改后,我意识到为什么 make_shared 正在工作而原始指针不起作用。老实说,我真的不明白如何使用智能指针,就像 "random stuff written here because it works"。但最终,问题并没有真正解决。我们不真的访问向量。此代码创建了我的 sprite 的副本并将其地址放在 shared_pointer 中。但是如果在炮弹 class 中,我们修改精灵,我们不修改向量中的精灵,而是修改副本。所以它通过实施另一个问题解决了这个问题:向量的用途是什么,如果它的成员因为复制而无用?
对于那些想知道如何解决向量问题的人,我其实没有头绪,问题似乎是向量问题,而不是SFML。我可能需要更多地研究它们。所以我的代码有效,但确实很糟糕。至少,它帮助我学习了智能指针和std::make_shared.
的使用
编辑 2:在网上搜索如何访问 vector 成员的地址后,它在 SO 上找到了一个 post,并意识到我的想法不错。我可以使用迭代器,但它不是我搜索的东西。现在我完全明白为什么我的程序不起作用了。根据游戏的进行情况,它可能会或不会崩溃,但正是这种未定义的行为才是有问题的。老实说,我不知道如何使用矢量,这对我来说真的很新,我在试验我不掌握的东西。通过阅读 SO 上的 post,我了解到访问向量地址是危险的。如果向量大小发生变化并变得高于分配的大小,元素可能会移动,从而使指针无效。这可能是我按下 Space + Enter 时发生的情况,创建了两个射弹。这只是运气。所以我必须找到一些东西来绕过这种行为。打个副本是一个解决办法,但是还有很多其他的,我来深挖一下。
我 post 在一个名为 OpenClassrooms 的法语论坛上尝试过后,我在这里 post,但没有答案,我 post 也在这里。
所以我警告你,我是 C++ 和 SFML 的新手,所以可能有一万个错误,而且我读的书似乎是一本非常糟糕的书,所以我尝试用 Bjarne Stroustrup 的书来纠正它.
我的问题如下:
我在按下 Enter 或 Space 时创建射弹(同一个键盘上有两个玩家)。每次我按下它,我都会为新的射弹创建一个射弹精灵的副本,然后我们将它放入 std::vector<sf::Sprite>
中。问题是当我启动游戏时,如果两个玩家同时按下他们的射击键(Enter 和 Space)(我的意思是,只要第一个弹丸可见),游戏就会崩溃并显示Segmentation Fault (core dumped)
。为了解决这个问题,我创建了两个精灵(每个玩家一个),并为他们的射弹影响它。问题是当他们的攻击速度很高时,他们可以在第一颗炮弹消失之前发射第二颗炮弹,所以碰撞会有问题,因为有两次相同的精灵......而第一个不会起作用。所以,为了解决这个问题,我想使用 std::vector。顺便说一下,我不会尝试只为两个玩家解决这个问题,我打算添加更多,所以我需要一些可以与 1000 名玩家一起使用的东西,例如(当然我不会对 1000 名玩家这样做,但如果它适用于该数量,它也适用于 5 个玩家)。
为了创建我的射弹,我使用了对 Sprite 对象的引用,这要感谢我的 class 游戏中的方法。此引用是对 std::vector 中精灵的引用。我还意识到,如果我们发射第一颗炮弹,等待它消失,然后让两个玩家射击,它会正常工作(有时,有时也会崩溃)......我不明白为什么,但主要是当我开始游戏崩溃了。
这是我的代码:
std::vector<sf::Sprite> sprites;
int main()
{
Game game;
sf::ContextSettings settings;
settings.antialiasingLevel = 8;
sf::RenderWindow window(sf::VideoMode(1600, 900), "Bombardes", sf::Style::Default, settings);
sf::Texture text;
if (!text.loadFromFile("resources/projectile.png")) {
logg.error("Could not create texture for projectile. Aborting.");
}
Bombard bomb(50, 150, &game, &pSprite, &movement2); // player class
Bombard bomb2(1550, 850, &game, &pSprite2, &movement);
std::vector<std::shared_ptr<Projectile>> p;
while (window.isOpen())
{
sf::Event event;
while (window.pollEvent(event))
{
switch (event.type) {
case sf::Event::Closed:
window.close();
break;
case sf::Event::KeyPressed:
if (event.key.code == sf::Keyboard::Return) { // Player 2
auto current2 = std::chrono::steady_clock::now();
auto elapsed2 = std::chrono::duration_cast<std::chrono::milliseconds>(current2 - last2);
if (elapsed2.count() >= 1000 / bomb2.getAttackSpeed()) { // Time the frequency of shots
last2 = std::chrono::steady_clock::now();
if (bomb2.getAmmo() > 0) { // Check if there's still ammo
sprites.push_back(sf::Sprite(text)); // New sprite in vector
p.push_back(std::make_shared<Projectile>(bomb2.getPos().getX(), bomb2.getPos().getY(), &sprites[sprites.size()-1], &game, bomb2.getProjectileMovement(), bomb2.getPenetration(),
bomb2.getSpeed())); // Create the projectile
bomb2.fire(); // Remove an ammo
}
}
}
else if (event.key.code == sf::Keyboard::Space) { // Player 1
auto current = std::chrono::steady_clock::now();
auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(current - last);
if (elapsed.count() >= 1000 / bomb.getAttackSpeed()) {
last = std::chrono::steady_clock::now();
if (bomb.getAmmo() > 0) {
sprites.push_back(sf::Sprite(text));
p.push_back(std::make_shared<Projectile>(bomb.getPos().getX(), bomb.getPos().getY(), &sprites[sprites.size()-1], &game, bomb.getProjectileMovement(), bomb.getPenetration(), bomb.getSpeed()));
bomb.fire();
}
}
}
break;
}
}
}
return 0;
}
当然,我删除了一些部分,我不会复制粘贴我的整个代码,有无用的东西。如果你觉得你需要我的 classes(射弹、游戏、实体、轰击),我会 post 他们。
我想它可以帮助您了解 Projectile 的构造函数:
Projectile::Projectile(int posX, int posY, sf::Sprite *sprite, Game *game, sf::Vector2f direction, double penetration, double speed) {
/** @brief Full constructor.
@param int posX : The X position on the map.
@param int posY : The Y position on the map.
@param sf::Sprite *sprite : A pointer to the sprite.
@param Game *game : A pointer to the game.
@param sf::Vector2f direction : The direction in which the projectile will move. */ }
感谢您的帮助!
已经发生了很多事情,所以我可能错过了一些事情。
有一件事看起来很奇怪:两名球员在射击时使用相同的 Projectile
指针!
玩家 1 射击:第一个射弹是堆分配的,您将其地址保存在 p
中。到目前为止一切顺利。
然后玩家 2 射击。您创建了一个新的射弹(具有正确的位置和精灵等...)但是,您还将其地址存储在 p
.
除非您在代码中的其他地方保存了第一个射弹的地址,否则您如何设法访问它?你怎么知道它是否达到了目标(然后,玩家 1 应该得分)或者是否已经超出了屏幕(然后你可以删除它以清理内存)?
我怀疑它周围有东西。也许您应该尝试将所有射弹存储在 std::vector<Projectile*>
或更好的 std::vector<std::unique_ptr<Projectile>>
中。这样(如果我没有理解代码的话)玩家可能可以发射不止一枚炮弹。
(如果您想知道 unique_ptr
部分,请不要介意询问)
让我们了解您的尝试,好吗?
好的,所以,经过一天的努力,感谢 giant_teapot 的帮助,我想我终于解决了这个问题。但我不确定。 我不再试验这个问题了,但我不明白为什么。
我决定不使用原始指针,而是使用智能指针。好像没有bug了。但为什么 ?我没有改变任何东西。有件事我不明白。但这是更改后的代码:
std::vector<sf::Sprite> sprites;
int main()
{
Game game;
sf::ContextSettings settings;
settings.antialiasingLevel = 8;
sf::RenderWindow window(sf::VideoMode(1600, 900), "Bombardes", sf::Style::Default, settings);
sf::Texture text;
if (!text.loadFromFile("resources/projectile.png")) {
logg.error("Could not create texture for projectile. Aborting.");
}
Bombard bomb(50, 150, &game, std::make_shared<sf::Sprite>(pSprite), &movement2);
Bombard bomb2(1550, 850, &game, std::make_shared<sf::Sprite>(pSprite2), &movement);
std::vector<std::shared_ptr<Projectile>> p;
while (window.isOpen())
{
sf::Event event;
while (window.pollEvent(event))
{
switch (event.type) {
case sf::Event::Closed:
window.close();
break;
case sf::Event::KeyPressed:
if (event.key.code == sf::Keyboard::Return) { // Player 2
auto current2 = std::chrono::steady_clock::now();
auto elapsed2 = std::chrono::duration_cast<std::chrono::milliseconds>(current2 - last2);
if (elapsed2.count() >= 1000 / bomb2.getAttackSpeed()) { // Time the frequency of shots
last2 = std::chrono::steady_clock::now();
if (bomb2.getAmmo() > 0) { // Check if there's still ammo
sprites.push_back(sf::Sprite(text)); // New sprite in vector
p.push_back(std::make_shared<Projectile>(bomb2.getPos().getX(), bomb2.getPos().getY(), std::make_shared<sf::Sprite>(sprites[sprites.size()-1]), &game, bomb2.getProjectileMovement(), bomb2.getPenetration(), bomb2.getSpeed())); // Create the projectile
bomb2.fire(); // Remove an ammo
}
}
}
else if (event.key.code == sf::Keyboard::Space) { // Player 1
auto current = std::chrono::steady_clock::now();
auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(current - last);
if (elapsed.count() >= 1000 / bomb.getAttackSpeed()) {
last = std::chrono::steady_clock::now();
if (bomb.getAmmo() > 0) {
sprites.push_back(sf::Sprite(text));
p.push_back(std::make_shared<Projectile>(bomb.getPos().getX(), bomb.getPos().getY(), std::make_shared<sf::Sprite>(sprites[sprites.size()-1]), &game, bomb.getProjectileMovement(), bomb.getPenetration(), bomb.getSpeed()));
bomb.fire();
}
}
}
break;
}
}
}
return 0;
}
你会明白我也改变了我的 classes。但它仍然没有解释为什么原始指针使整个事情崩溃而智能指针正常工作。
再次非常感谢 giant_teapot,他们在很多时间里帮助了我,并向我展示了这些精彩的建议。还要感谢 Martin Bonner 让他把这些给我看。
编辑:在我的代码中进行了一些更改后,我意识到为什么 make_shared 正在工作而原始指针不起作用。老实说,我真的不明白如何使用智能指针,就像 "random stuff written here because it works"。但最终,问题并没有真正解决。我们不真的访问向量。此代码创建了我的 sprite 的副本并将其地址放在 shared_pointer 中。但是如果在炮弹 class 中,我们修改精灵,我们不修改向量中的精灵,而是修改副本。所以它通过实施另一个问题解决了这个问题:向量的用途是什么,如果它的成员因为复制而无用?
对于那些想知道如何解决向量问题的人,我其实没有头绪,问题似乎是向量问题,而不是SFML。我可能需要更多地研究它们。所以我的代码有效,但确实很糟糕。至少,它帮助我学习了智能指针和std::make_shared.
的使用编辑 2:在网上搜索如何访问 vector 成员的地址后,它在 SO 上找到了一个 post,并意识到我的想法不错。我可以使用迭代器,但它不是我搜索的东西。现在我完全明白为什么我的程序不起作用了。根据游戏的进行情况,它可能会或不会崩溃,但正是这种未定义的行为才是有问题的。老实说,我不知道如何使用矢量,这对我来说真的很新,我在试验我不掌握的东西。通过阅读 SO 上的 post,我了解到访问向量地址是危险的。如果向量大小发生变化并变得高于分配的大小,元素可能会移动,从而使指针无效。这可能是我按下 Space + Enter 时发生的情况,创建了两个射弹。这只是运气。所以我必须找到一些东西来绕过这种行为。打个副本是一个解决办法,但是还有很多其他的,我来深挖一下。