• Najnowsze pytania
  • Bez odpowiedzi
  • Zadaj pytanie
  • Kategorie
  • Tagi
  • Zdobyte punkty
  • Ekipa ninja
  • IRC
  • FAQ
  • Regulamin
  • Książki warte uwagi

Dlaczego wyskakuje mi tu błąd z vectorem C++

Object Storage Arubacloud
0 głosów
516 wizyt
pytanie zadane 31 lipca 2021 w C i C++ przez tonn204 Mądrala (7,440 p.)

Piszę prostą grę w SFML. Chcę aby po uderzeniu pocisku w przeciwnika znikał on. Nie wiem dlaczego linia 159 sprawia problem. Kiedy odpalam program w momencie spotkania się pocisku z przeciwnikiem VS 2019 wywala mi błąd : Vector subscript out of range. Jak mam to naprawić i dlaczego to nie działa skoro kilka linii niżej używam dokładnie tego samego kodu do usuwania pociskow kiedy wylecą poza mapę?

#include <iostream>
#include <stdlib.h>
#include <time.h>
#include <vector>

#include <SFML/Graphics.hpp>

int main()
{
    const int WINDOW_WIDTH = 800, WINDOW_HEIGHT = 600;
    const float PLAYER_Y_POS = 500.0f;

    int score = 0;

    bool space_pressed = false;
    bool bullet_on_fire = false;
    bool enemy_going_right[5] = {
        true, true, true, true, true
    };

    std::vector<sf::Sprite> bullets;

    // Window init
    sf::RenderWindow window(sf::VideoMode(WINDOW_WIDTH, WINDOW_HEIGHT), "Aliens invasion");
    window.setVerticalSyncEnabled(true); 

    sf::Font my_font;
    if (!my_font.loadFromFile("myFont.ttf"))
    {
        std::cout << "Failed to load a font" << std::endl;
    }

    sf::Text text;
    text.setFont(my_font);
    text.setCharacterSize(20);
    std::string score_text;

    sf::Texture background_texture;
    if (!background_texture.loadFromFile("background.jpg"))
    {
        std::cout << "Failed to load background" << std::endl;
    }

    sf::Sprite backgorund(background_texture, sf::IntRect(0, 0, 800, 600));

    // Player init
    sf::Texture player_texture;
    if (!player_texture.loadFromFile("player.png"))
    {
        std::cout << "Failed to laod player texture" << std::endl;
    }
    sf::Sprite player;
    player.setTexture(player_texture);
    player.setPosition(sf::Vector2f(400.0f, PLAYER_Y_POS));
    player.setScale(sf::Vector2f(2.0f, 2.0f));

    sf::Texture bullet_texture;
    if (!bullet_texture.loadFromFile("bullet.png"))
    {
        std::cout << "Failed to load bullet texture" << std::endl;
    }
    sf::Sprite bullet;

    sf::Texture enemy_texture;
    if (!enemy_texture.loadFromFile("enemy.png"))
    {
        std::cout << "Failed to load enemy image" << std::endl;
    }

    srand(time(NULL));

    sf::Sprite enemies[5];
    for (int i = 0; i < 5; i++)
    {
        enemies[i].setTexture(enemy_texture);
        enemies[i].scale(sf::Vector2f(2.0f, 1.5f));
        enemies[i].setPosition(sf::Vector2f(float(rand() % 740 + 0), float(rand() % 300)));
    }

    sf::Clock game_clock;

    while (window.isOpen())
    {
        float deltaTime = game_clock.restart().asMilliseconds();

        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::Escape)
                    window.close();
                else if (event.key.code == sf::Keyboard::Space && !space_pressed)
                {
                    space_pressed = true;
                    std::cout << "Space pressed!!!" << std::endl; 
                    bullets.push_back(bullet);
                    bullets.back().setPosition(sf::Vector2f(player.getPosition().x + 16, PLAYER_Y_POS));
                    bullets.back().setTexture(bullet_texture);                       
                    std::cout << bullets.size() << std::endl;
                }
                break;
            case sf::Event::KeyReleased:
                if (event.key.code == sf::Keyboard::Space && space_pressed)
                {
                    space_pressed = false;
                }
                break;
            }
        }
        
        if (sf::Keyboard::isKeyPressed(sf::Keyboard::A))
        {
            player.move(sf::Vector2f(-0.3f * deltaTime, 0.0f));
            if (player.getPosition().x <= 0.0f)
                player.setPosition(sf::Vector2f(0.0f, PLAYER_Y_POS));
        }
        else if (sf::Keyboard::isKeyPressed(sf::Keyboard::D))
        {
            player.move(sf::Vector2f(0.3f * deltaTime, 0.0f));
            if (player.getPosition().x >= 740.0f)
                player.setPosition(sf::Vector2f(740.0f, PLAYER_Y_POS));
        }

        for (int i = 0; i < 5; i++)
        {
            if (enemy_going_right[i])
            {
                enemies[i].move(sf::Vector2f(0.2f * deltaTime, 0.0f));
                if (enemies[i].getPosition().x >= 740.0f)
                {
                    enemies[i].setPosition(sf::Vector2f(740.0f, enemies[i].getPosition().y + 10.0f));
                    enemy_going_right[i] = false;
                }              
            }
            else
            {
                enemies[i].move(sf::Vector2f(-0.2f * deltaTime, 0.0f));
                if (enemies[i].getPosition().x <= 0.0f)
                {
                    enemies[i].setPosition(sf::Vector2f(0.0f, enemies[i].getPosition().y + 10.0f));
                    enemy_going_right[i] = true;
                }
            }
        }


        for (int z = 0; z < bullets.size(); z++)
        {      
            bullets[z].move(0.0f, -1.0f * deltaTime);
            for (int i = 0; i < 5; i++)
            {
                if (enemies[i].getGlobalBounds().intersects(bullets[z].getGlobalBounds()))
                {   
                    bullets.erase(bullets.begin() + z);
                    enemies[i].setPosition(sf::Vector2f(float(rand() % 740 + 0), float(rand() % 300)));
                    score++;          
                }
            }          
            if (bullets[z].getPosition().y <= 0.0f)
                bullets.erase(bullets.begin() + z);
        }            
        
        score_text = std::to_string(score);
        text.setString("Score: " + score_text);

        window.clear();
        window.draw(backgorund);
        window.draw(player);
        for (int i = 0; i < 5; i++)
        {
            window.draw(enemies[i]);
        }
        for (int i = 0; i < bullets.size(); i++)
        {
            window.draw(bullets[i]);
        }        
        window.draw(text);
        window.display();
    }
    return 0;
}

komentarz 31 lipca 2021 przez tkz Nałogowiec (42,000 p.)
Doczytaj jak działa metoda erase. Dokładnie jak działa na rozmiar.
komentarz 31 lipca 2021 przez Oscar Nałogowiec (29,290 p.)
edycja 31 lipca 2021 przez Oscar
Usuwasz pocisk, ale pętla po i (enemies) idzie dalej i sprawdzasz kolejnego enemy z tym samym indeksem pocisku. Ale to już nie będzie ten sam pocisk, a nawet może nie być pocisku pod tym indeksem . Chyba (nie znam logiki tego programu) powinieneś przerwać pętle i po trafieniu pierwszego enemy.

Także po pętli sprawdzasz raz jeszcze czy ten, być może już usunięty, pocisk nie wyszedł poza krawędź.

Być może używanie wektorów nie jest najlepszym pomysłem. Pamiętaj "Algorytmy + Struktury danych = Programy" (N. Wirth)
komentarz 1 sierpnia 2021 przez tonn204 Mądrala (7,440 p.)

Chyba (nie znam logiki tego programu) powinieneś przerwać pętle i po trafieniu pierwszego enemy.

Faktycznie zmieniłem kod na taki jak poniżej i pomogło, dzięki za pomoc przy tym problemie, a co do samego używania wektorów to zdecydowałem się na to, bo użytkownik forum związanego z SFML polecił coś takiego pod jednym postem. Swoją drogą to jeżeli nie wektory to czego na przykład można by było użyć (myślałem nad tworzeniem wykorzystaniu wskaźników coś w tym stylu jak w kodzie poniżej ale nie wiem czy to dobry pomysł)? 

 sf::Sprite* bullet = new sf::Sprite();

Kod końcowy po rozwiązaniu problemu:

#include <iostream>
#include <stdlib.h>
#include <time.h>
#include <vector>

#include <SFML/Graphics.hpp>

int main()
{
    const int WINDOW_WIDTH = 800, WINDOW_HEIGHT = 600;
    const float PLAYER_Y_POS = 500.0f;

    int score = 0;

    bool space_pressed = false;
    bool bullet_is_collyding = false;
    bool enemy_going_right[5] = {
        true, true, true, true, true
    };

    std::vector<sf::Sprite> bullets;

    // Window init
    sf::RenderWindow window(sf::VideoMode(WINDOW_WIDTH, WINDOW_HEIGHT), "Aliens invasion");
    window.setVerticalSyncEnabled(true); 

    sf::Font my_font;
    if (!my_font.loadFromFile("myFont.ttf"))
    {
        std::cout << "Failed to load a font" << std::endl;
    }

    sf::Text text;
    text.setFont(my_font);
    text.setCharacterSize(20);
    std::string score_text;

    sf::Texture background_texture;
    if (!background_texture.loadFromFile("background.jpg"))
    {
        std::cout << "Failed to load background" << std::endl;
    }

    sf::Sprite backgorund(background_texture, sf::IntRect(0, 0, 800, 600));

    // Player init
    sf::Texture player_texture;
    if (!player_texture.loadFromFile("player.png"))
    {
        std::cout << "Failed to laod player texture" << std::endl;
    }
    sf::Sprite player;
    player.setTexture(player_texture);
    player.setPosition(sf::Vector2f(400.0f, PLAYER_Y_POS));
    player.setScale(sf::Vector2f(2.0f, 2.0f));

    sf::Texture bullet_texture;
    if (!bullet_texture.loadFromFile("bullet.png"))
    {
        std::cout << "Failed to load bullet texture" << std::endl;
    }
    sf::Sprite bullet;

    sf::Texture enemy_texture;
    if (!enemy_texture.loadFromFile("enemy.png"))
    {
        std::cout << "Failed to load enemy image" << std::endl;
    }

    srand(time(NULL));

    sf::Sprite enemies[5];
    for (int i = 0; i < 5; i++)
    {
        enemies[i].setTexture(enemy_texture);
        enemies[i].scale(sf::Vector2f(2.0f, 1.5f));
        enemies[i].setPosition(sf::Vector2f(float(rand() % 740 + 0), float(rand() % 300)));
    }

    sf::Clock game_clock;

    while (window.isOpen())
    {
        float deltaTime = game_clock.restart().asMilliseconds();

        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::Escape)
                    window.close();
                else if (event.key.code == sf::Keyboard::Space && !space_pressed)
                {
                    space_pressed = true;
                    std::cout << "Space pressed!!!" << std::endl; 
                    bullets.push_back(bullet);
                    bullets.back().setPosition(sf::Vector2f(player.getPosition().x + 16, PLAYER_Y_POS));
                    bullets.back().setTexture(bullet_texture);                       
                    std::cout << bullets.size() << std::endl;
                }
                break;
            case sf::Event::KeyReleased:
                if (event.key.code == sf::Keyboard::Space && space_pressed)
                {
                    space_pressed = false;
                }
                break;
            }
        }
        
        if (sf::Keyboard::isKeyPressed(sf::Keyboard::A))
        {
            player.move(sf::Vector2f(-0.3f * deltaTime, 0.0f));
            if (player.getPosition().x <= 0.0f)
                player.setPosition(sf::Vector2f(0.0f, PLAYER_Y_POS));
        }
        else if (sf::Keyboard::isKeyPressed(sf::Keyboard::D))
        {
            player.move(sf::Vector2f(0.3f * deltaTime, 0.0f));
            if (player.getPosition().x >= 740.0f)
                player.setPosition(sf::Vector2f(740.0f, PLAYER_Y_POS));
        }

        for (int i = 0; i < 5; i++)
        {
            if (enemy_going_right[i])
            {
                enemies[i].move(sf::Vector2f(0.2f * deltaTime, 0.0f));
                if (enemies[i].getPosition().x >= 740.0f)
                {
                    enemies[i].setPosition(sf::Vector2f(740.0f, enemies[i].getPosition().y + 10.0f));
                    enemy_going_right[i] = false;
                }              
            }
            else
            {
                enemies[i].move(sf::Vector2f(-0.2f * deltaTime, 0.0f));
                if (enemies[i].getPosition().x <= 0.0f)
                {
                    enemies[i].setPosition(sf::Vector2f(0.0f, enemies[i].getPosition().y + 10.0f));
                    enemy_going_right[i] = true;
                }
            }
        }


        for (int i = 0; i < bullets.size(); i++)
        {      
            bullets[i].move(0.0f, -1.0f * deltaTime);
            for (int z = 0; z < 5; z++)
            {
                if (enemies[z].getGlobalBounds().intersects(bullets[i].getGlobalBounds()))
                {
                    enemies[z].setPosition(sf::Vector2f(float(rand() % 740 + 0), float(rand() % 300)));
                    score++;
                    bullets.erase(bullets.begin() + i);
                    bullet_is_collyding = true;
                    break;
                }
            }
            if (bullet_is_collyding)
                break;

            if (bullets[i].getPosition().y <= 0.0f)
                bullets.erase(bullets.begin() + i);
        }    

        bullet_is_collyding = false;

        score_text = std::to_string(score);
        text.setString("Score: " + score_text);

        window.clear();
        window.draw(backgorund);
        window.draw(player);
        for (int i = 0; i < 5; i++)
        {
            window.draw(enemies[i]);
        }
        for (int i = 0; i < bullets.size(); i++)
        {
            window.draw(bullets[i]);
        }        
        window.draw(text);
        window.display();
    }
    return 0;
}

 

komentarz 1 sierpnia 2021 przez Oscar Nałogowiec (29,290 p.)
Wektory to taki dynamiczny odpowiednik tablic. Zapewniają szybki dostęp indeksowany, gdy z góry znasz numer komórki. Natomiast wszelkie wstawiania i usuwania są wolne - wymagają kopiowania fragmentów wektora w celu stworzenia wolnego miejsca, lub zatkania dziuru po elemencie usuniętym.

Byłoby to dobre gdyby silnik sam dostarczał informacji o tym, że dwa obiekty (dawniej to się nazywało sprites) się zderzyły. Dostałby numery obiektów - szybki dostęp do tablicy i już. Nawet nie trzeba by było usuwać obiektu, tylko zaznaczyć w tablicy, że pozycja jest wolna (indeksy pozostałych elementów się nie zmienią). Nawet w czasach komputerów 8 bitowych, ich układy graficzne posiadały generatory sprites i same wykrywały kolizje zgłaszając przerwania. Nigdy nie zajmowałem się pisaniem gier, ale dziwne by było, gdyby dzisiejsze silniki nie miały podobnego mechanizmu.

W sytuacji gdy i  tak sam przeglądasz te kolekcje to bardziej efektywne będą listy.
komentarz 1 sierpnia 2021 przez tkz Nałogowiec (42,000 p.)

Wektory to taki dynamiczny odpowiednik tablic. Zapewniają szybki dostęp indeksowany, gdy z góry znasz numer komórki. Natomiast wszelkie wstawiania i usuwania są wolne - wymagają kopiowania fragmentów wektora w celu stworzenia wolnego miejsca, lub zatkania dziuru po elemencie usuniętym.

To zależy ile "capacity" zostało. Od uniknięcia tego mamy reserve(const std::size_t). 

komentarz 1 sierpnia 2021 przez Oscar Nałogowiec (29,290 p.)
Nie o taki przypadek mi chodziło, tylko np. o wstawianie lub usuwanie ze środka wektora.
komentarz 2 sierpnia 2021 przez mokrowski Mędrzec (155,460 p.)

https://en.cppreference.com/w/cpp/container/vector/erase

Invalidates iterators and references at or after the point of the erase, including the end() iterator.

https://en.cppreference.com/w/cpp/container/vector/insert

Causes reallocation if the new size() is greater than the old capacity(). If the new size() is greater than capacity(), all iterators and references are invalidated.

W każdej takiej operacji, iteratory ulegają unieważnieniu. W cytacie masz informację jakie :)

Zaloguj lub zarejestruj się, aby odpowiedzieć na to pytanie.

Podobne pytania

0 głosów
0 odpowiedzi 255 wizyt
0 głosów
1 odpowiedź 170 wizyt
pytanie zadane 15 lutego 2021 w C i C++ przez Kwiatek_PK Nowicjusz (180 p.)

92,566 zapytań

141,420 odpowiedzi

319,606 komentarzy

61,952 pasjonatów

Motyw:

Akcja Pajacyk

Pajacyk od wielu lat dożywia dzieci. Pomóż klikając w zielony brzuszek na stronie. Dziękujemy! ♡

Oto polecana książka warta uwagi.
Pełną listę książek znajdziesz tutaj.

Akademia Sekuraka

Kolejna edycja największej imprezy hakerskiej w Polsce, czyli Mega Sekurak Hacking Party odbędzie się już 20 maja 2024r. Z tej okazji mamy dla Was kod: pasjamshp - jeżeli wpiszecie go w koszyku, to wówczas otrzymacie 40% zniżki na bilet w wersji standard!

Więcej informacji na temat imprezy znajdziecie tutaj. Dziękujemy ekipie Sekuraka za taką fajną zniżkę dla wszystkich Pasjonatów!

Akademia Sekuraka

Niedawno wystartował dodruk tej świetnej, rozchwytywanej książki (około 940 stron). Mamy dla Was kod: pasja (wpiszcie go w koszyku), dzięki któremu otrzymujemy 10% zniżki - dziękujemy zaprzyjaźnionej ekipie Sekuraka za taki bonus dla Pasjonatów! Książka to pierwszy tom z serii o ITsec, który łagodnie wprowadzi w świat bezpieczeństwa IT każdą osobę - warto, polecamy!

...