std :: vector Sprite分段错误

时间:2016-11-12 11:15:19

标签: c++ vector sfml

我在一个名为OpenClassrooms的法国论坛上尝试后发帖,但没有答案,我也在这里发帖。

所以我警告你,我是C ++和SFML的新手,所以可能有一万个错误,而且我读的这本书似乎是一本非常糟糕的书,所以我试着用Bjarne Stroustrup的书来解决这个问题。 / p>

我的问题如下:

当我按Enter或Space(同一键盘上有两个玩家)时,我创建了射弹。每当我按下它时,我都会为新射弹创建一个射弹精灵的副本,我们将它放入std::vector<sf::Sprite>。问题是当我启动游戏时,如果两个玩家同时按下他们的射击键(Enter和Space)(我的意思是,只要第一个射弹可见),游戏就会崩溃并显示{{1} }。为了解决这个问题,我创造了两个精灵(每个玩家一个),并为它们的射弹影响它。问题是当他们的攻击速度很快时,他们可以在第一次射击之前射击他们的第二个射弹,所以碰撞会有问题,因为有两次相同的精灵......并且第一个不起作用。所以,为了解决这个问题,我想使用std :: vector。顺便说一下,我不打算只为两个玩家解决这个问题,我计划增加一些,所以我需要一些可以与1000名玩家合作的东西(例如我当然不会对1000名玩家这样做,但如果它适用于这个数量,它也适用于5个玩家。)

为了创建我的射弹,我使用了对我后来展示的对象Sprite的引用,这要归功于我的类Game中的一个方法。此引用是对std :: vector中的sprite的引用。我也意识到,如果我们拍摄第一个射弹,等待它消失,然后让两个玩家射击,它工作正常(有时,它有时也会崩溃)......我不明白为什么,但它主要是在我开始它崩溃的游戏。

这是我的代码:

Segmentation Fault (core dumped)

当然,我删除了一些部分,我不会复制粘贴我的整个代码,有没用的东西。如果你觉得你需要我的课程(Projectile,Game,Entity,Bombard),我会发布它们。

我想它可以帮助你看到Projectile的构造函数:

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;
}

感谢您的帮助!

2 个答案:

答案 0 :(得分:2)

已经发生了很多事情,所以我可能错过了一些事情。

有一点似乎很奇怪:两名玩家在射击时都使用相同的Projectile指针!

玩家1射击:第一个射弹是堆分配,你将其地址保存在p。到目前为止一切都很好。

然后玩家2射击。你创建了一个新的射弹(具有正确的位置和精灵等等)但是,你也将它的地址存储在p

除非您已在代码中的其他位置保存了第一个射弹的地址,否则您如何设法访问它?你怎么知道它是否达到目标(然后,玩家1应该得分)或者是否已经超出屏幕(然后你可以将其删除以清理内存)?

我怀疑周围有什么东西。也许你应该尝试将所有射弹存放在std::vector<Projectile*>甚至更好,std::vector<std::unique_ptr<Projectile>>。这样(如果我确实理解了正确的代码),玩家可以射击多个射弹。

(如果你想知道那个unique_ptr部分,请不要在意)

让我们了解您的尝试,是吗?

答案 1 :(得分:0)

好的,经过一天的工作,感谢huge_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;
}

你了解我也改变了我的课程。但它仍然无法解释为什么原始指针会使整个事件崩溃而智能指针正常工作。

再一次,非常感谢giant_teapot在很长时间内帮助了我,并向我展示了这些精彩的指针。还要感谢Martin Bonner告诉他向我展示这些内容。

编辑:在我的代码中发生了一些变化之后,我意识到为什么make_shared正在工作,而原始指针却没有工作。我并不真正理解如何使用智能指针来说实话,这就像是随机写的东西,因为它可以正常工作&#34;。但最后,这个问题并没有真正解决。我们没有真正访问该向量。这段代码创建了我的精灵的副本,并将其地址放在shared_pointer中。但是如果在射弹类中,我们修改了精灵,我们不会修改矢量中的精灵,而是修改副本。因此它通过实现另一个问题来解决问题:如果向量的成员因复制而无用,那么该向量的使用是什么?

对于那些想知道如何解决向量问题的人,我实际上没有任何线索,问题似乎是一个向量问题,而不是SFML。我可能需要更多地研究它们。所以我的代码有效,但是非常糟糕。至少,它帮助我学习了智能指针和std :: make_shared的使用。

编辑2:在互联网上试图搜索如何访问矢量成员的地址后,它在SO上发现了一个帖子,并意识到我并没有想到它。我可以使用迭代器,但它不是我搜索的东西。现在我完全理解为什么我的程序没有用。根据游戏的进展情况,它可能会或者不会崩溃,但正是这种未定义的行为存在问题。说实话,我不知道如何使用矢量,这对我来说真的很新,我正在试验我不能掌握的东西。通过阅读关于SO的帖子,我了解到访问向量地址是危险的。如果向量大小更改并且变得高于分配的大小,则元素可能会移动,从而使指针无效。这可能是当我按下Space + Enter,创造两个射弹时会发生什么。这只是运气。所以我必须找到一些可以绕过这种行为的东西。制作副本是一种解决方案,但还有很多其他方法,我会挖掘这个主题。