FeelZoR FeelZoR - 24 days ago 10
C++ Question

std::vector Sprite segmentation fault

I post here after an attempt on a french forum called OpenClassrooms, but having no answers, I post here too.

So I warn you, I'm new in C++ and SFML so there is probably ten thousand mistakes, and it seems that the book I read was a really bad book, so I try to correct that with Bjarne Stroustrup's book.

My issue is the following :

I create projectiles when I press Enter or Space (there is two players on the same keyboard). And every time I press it, I create a copy of the projectile's sprite for the new projectile, and we put it into a

std::vector<sf::Sprite>
. The issue is when I launch the game, if the two players press their shoot key (Enter and Space) at the same time (I mean, as long as the first projectile is visible), the game will crash and show
Segmentation Fault (core dumped)
. To resolve this, I created two sprites (one for each player), and affect it for their projectiles. The problem is when their attack speed is huge, they can shoot their second projectile before the first disappear, so the collisions will have a problem, because there is the same sprite twice... and the first one will not work. So, to resolve this problem, I wanted to use a std::vector. By the way, I don't try to resolve this for only two players, I plan to add some more, so I need something which would work with 1000 players, for example (of course I won't do this with 1000 players, but if it works with that amount, it will also work for 5 players).

To create my projectile, I use a reference to the object Sprite that I show later thanks to a method in my class Game. This reference is the reference to a sprite in the std::vector. I also realized that if we shoot a first projectile, wait for it to disappear and then let the two players shoot, it works properly (sometimes, it crashes sometimes too)... I don't understand why, but it's mostly when I start the game that it crashes.

Here is my code :

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


Of course, I removed some parts, I wouldn't copy paste my whole code, there are useless things. If you feel like you need my classes (Projectile, Game, Entity, Bombard), I'll post them.

I guess it can help you to see the Projectile's constructor :

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. */ }


Thanks for your help !

Answer

There's already quite a lot going on, so I might have missed a few things.

One thing seems odd: both player are using the same Projectile pointer when they shoot!

Player 1 shoots: a first projectile is heap allocate and you keep its address in p. So far so good.

Then Player 2 shoots. You create a new projectile (with the correct position and sprite and so on...) BUT, you also store its address in p.

Unless you have saved the first projectile's address somewhere else in your code, then how do you manage to access it? How can you know if it has reached its target (and then, Player 1 should score) or if has gone outside the screen (and then you can delete it to clear up memory) ?

I suspect there is something around it. Maybe you should try storing all your projectiles in a std::vector<Projectile*> or even better, a std::vector<std::unique_ptr<Projectile>>. This way (if I did understand the code right) player might be able to shoot more than one projectile.

(if you are wondering about that unique_ptr part, don't mind asking)

Keep us informed of what you've tried, will you?