mardi 13 avril 2021

C++ problem with sf::sprite in a std::vector not keeping its updated position (despite passing by reference)?

  1. In main, create soldier object

Soldier soldier;

  1. create soldier sprite and set its position

//Load a soldier sprite

sf::Texture texture2;
if (!texture2.loadFromFile(resourcePath() + "soldier.png"))
{
    return EXIT_FAILURE;
}
sf::Sprite spriteSoldier(texture2);
spriteSoldier.setPosition(winwidth/2, winheight/2);
spriteSoldier.setOrigin(spriteSoldier.getLocalBounds().width/2,
spriteSoldier.getLocalBounds().height/2);
  1. Load bullet texture for later //load bullet texture
    if(!bulletTexture.loadFromFile(resourcePath()+"Bullet.png")){
        return EXIT_FAILURE;
    }
    
    
  2. Inside while (window.isOpen())

call to processEvents

while(timeSinceLastUpdate > TimePerFrame)
    {
        timeSinceLastUpdate -= TimePerFrame;
        
        processEvents(window, spriteSoldier, game, soldier, bulletTexture,
                    elapsedTime);
        
    }

void processEvents(sf::RenderWindow& window, sf::Sprite& spriteSoldier, Game& g, Soldier& 
soldier, sf::Texture& bulletTexture, sf::Time elapsed)```
  1. Initially this if statement isn’t entered as there are no bullets in the vector

    std::vector tvec = soldier.RetClip(); if ( tvec.size() > 0 )//only enters if created a bullet and added { //update position of the bullet, this is the sprite sf::Vector2f currentpos{}; currentpos = tvec[0].getPosition(); //std::cout <<" currentpos.x before " << currentpos.x << std::endl;

     currentpos += (tvec[0].getVelocity() * elapsed.asSeconds());
     //std::cout <<" currentpos.x after " << currentpos.x << std::endl;
    
     //set to current position
     tvec[0].setPosition(currentpos);
     //std::cout <<" currentpos.x after reset " << tvec[0].m_sprite.getPosition().x<< 
     std::endl;
     }
    
  2. Later on in this function inside

sf::Event event; while (window.pollEvent(event))

//synch soldier position with its sprite.
        soldier.setPosition(spriteSoldier.getPosition());
        if (event.key.code == sf::Keyboard::Space)
        {
            //going to be fired immediately, it's the data that is put into the bulletclass
            float currentrotation = (soldier.getRotation() + 90.0); //align bullet with gun
            //create sprite
            sf::Sprite tempSprite(bulletTexture);//maybe rotate it??
            tempSprite.rotate(currentrotation);//keeps this its whole life
            tempSprite.Transformable::setPosition(spriteSoldier.getPosition());//start where soldier is
            //create a temp bulletclass object to hold sprite details
            //BulletClass tempBulletClass(currentrotation, direcNormalised, spriteSoldier.getPosition(), tempSprite);
            std::unique_ptr<BulletClass> tempBulletClass ( new BulletClass(currentrotation, direcNormalised, spriteSoldier.getPosition(), tempSprite) );
            //add this tempbullet object so the soldier's clip
            soldier.addBulletToClip(*tempBulletClass);//hoping they are copying object
        }

This creates a bulletclass instance, this is sent to soldier.addBulletToClip

void Soldier::addBulletToClip(BulletClass& bullet)
{
    std::cout <<" bullet added to clip " << std::endl;
    BulletClip.push_back(bullet);
}

So the bulletclass instance is added to the std::vector BulletClip;

This all seems ok, BUT BUT BUT when it returns to main and that if statement again …

std::vector<BulletClass> tvec = soldier.RetClip();
if ( tvec.size() > 0 )//only enters if created a bullet and added
{
    //update position of the bullet, this is the sprite
    sf::Vector2f currentpos{};
    currentpos = tvec[0].getPosition();
    //std::cout <<" currentpos.x before " << currentpos.x << std::endl;

    currentpos += (tvec[0].getVelocity() * elapsed.asSeconds());
    //std::cout <<" currentpos.x after " << currentpos.x << std::endl;

    //set to current position
    tvec[0].setPosition(currentpos);
    //std::cout <<" currentpos.x after reset " << tvec[0].m_sprite.getPosition().x<< std::endl;
}

The problem seems to be that whenever this is looped through (once a bullet exists)

This bit of code always states that the x component of the bullet sprite is 1300 (i.e. where the soldier sprite is at and the initial value of the bullet sprite as it was added to the std::vector).

This bit of code does do what is expected and apparently change the value of currentpos based on velocity and elapsed time

currentpos += (tvec[0].getVelocity() * elapsed.asSeconds());

I then want to make sure that currentpos does update the position of the sprite, hence

//set to current position
tvec[0].setPosition(currentpos);
//std::cout <<" currentpos.x after reset " << tvec[0].m_sprite.getPosition().x<< std::endl;

This all seems fine … it does call

sf::Vector2f BulletClass::setPosition(sf::Vector2f in)
{
    m_position.x = in.x;//keep local copy
    m_position.y = in.y;
    std::cout <<" in.x to change x " << in.x << std::endl;
    m_sprite.setPosition(in);
    std::cout <<" sprite.x now...  " << m_sprite.getPosition().x << std::endl;
}

and the checks seem to tell me, yes all is good I’ll update the sprite i’m storing for you …

BUT then next time round, in process events …

std::vector<BulletClass> tvec = soldier.RetClip();
if ( tvec.size() > 0 )//only enters if created a bullet and added
{
    //update position of the bullet, this is the sprite
    sf::Vector2f currentpos{};
    currentpos = tvec[0].getPosition();
    //std::cout <<" currentpos.x before " << currentpos.x << std::endl;

the currentxpos is back to the very initial value of 1300, consequently the sprite on the screen never move and is always at the same place just below the feet of the soldier.

This is the code at the bottom of main that draws the bullet

//try and draw the bullets details, stored in the clip

std::vector<BulletClass> temp = soldier.RetClip(); //returns a ref to the clip
    for (auto bull = temp.begin(); bull != temp.end(); ++bull)
    {
        window.draw(bull->returnSprite());
    }
    window.display();

It seems that any call to process events (ie. updating the bullet position) isn’t kept (it’s like groundhog day - the bullet always starts off at the initial place) …

I have checked that I am sending the soldier object by reference so that any changes to these objects in process events should be permanent

Soldier& soldier

Clearly the soldier object keeps its bullet object in its std::vector (otherwise it wouldn’t keep returning its position of 1300 on x-axis).

So inside the keyboard::space (in void processevents) the soldier object was permanently modified. But inside the if statement (also in void processevents) the soldier object wasn’t permanently modified.

Would appreciate any help as I’m completely stumped.

Aucun commentaire:

Enregistrer un commentaire