0
\$\begingroup\$

I'm trying to setup a 2d game and have been thrown up an error on vector subscript out of range. I've been trying to look up about it, but have not seen anything that helps. If someone could help and take the time to look at my code and help me fix my error i'd be very grateful. I think it's got something to do with something about that it can't handle more then 0-9 or something but my knowledge/understanding of it is not great and would appreciate the help. I'll paste my code below so you can see what i'm trying to do.

//tile setup
level.loadTexture("gfx/Maptiles.png");
Tile tile;
vector<Tile> tiles;
for (int i = 0; i < 10; i++)
{
    tile.setSize(sf::Vector2f(32, 32));
    tile.setAlive(true);
    tiles.push_back(tile);
}

//sky tile set to false so we don't collide
tiles[0].setAlive(false);
//    X   Y   W   H  of the sprite sheet 
/*tiles[0].setTextureRect(sf::IntRect(187, 51, 70, 70));
tiles[1].setTextureRect(sf::IntRect(0, 0, 70, 70));*/
tiles[0].setTextureRect(sf::IntRect(864, 221, 70, 70));
tiles[1].setTextureRect(sf::IntRect(73, 2, 70, 70));
tiles[2].setTextureRect(sf::IntRect(140, 2, 70, 70));
tiles[3].setTextureRect(sf::IntRect(210, 2, 70, 70));
tiles[4].setTextureRect(sf::IntRect(280, 2, 70, 70));
tiles[5].setTextureRect(sf::IntRect(350, 2, 70, 70));
tiles[6].setTextureRect(sf::IntRect(420, 2, 70, 70));
tiles[7].setTextureRect(sf::IntRect(506, 577, 70, 70)); //ground block
tiles[8].setTextureRect(sf::IntRect(560, 2, 70, 70));
tiles[9].setTextureRect(sf::IntRect(630, 2, 70, 70));
//tiles[].setTextureRect(sf::IntRect(630, 0, 70, 70));
//tiles[11].setTextureRect(sf::IntRect(770, 34, 70, 70));
//tiles[12].setTextureRect(sf::IntRect(840, 51, 70, 70));
//tiles[13].setTextureRect(sf::IntRect(910, 0, 70, 70)); //^^ 1st row
/*tiles[14].setTextureRect(sf::IntRect(0, 0, 70, 70));
tiles[15].setTextureRect(sf::IntRect(70, 0, 70, 70));
tiles[16].setTextureRect(sf::IntRect(140, 0, 70, 70));
tiles[17].setTextureRect(sf::IntRect(17, 34, 70, 70));
tiles[18].setTextureRect(sf::IntRect(34, 34, 70, 70));*/
level.setTileSet(tiles);
// Map dimensions
    sf::Vector2u mapSize(25, 19);
// build map
std::vector<int> map = {
    0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, //25 X 19
    2, 2, 3, 4, 5, 6, 7, 8, 9, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 
    2, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 
    2, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2,
    2, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2,
    2, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2,
    2, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2,
    2, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2,
    2, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2,
    2, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2,
    2, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2,
    2, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2,
    2, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2,
    2, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2,
    2, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2,
    2, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2,
    2, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2,
    2, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2,
    7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7       
};
//fix tile layout
level.setTileMap(map, mapSize);
level.setPosition(sf::Vector2f(0, 0));
level.buildLevel();


level.render(window);
\$\endgroup\$
4
  • \$\begingroup\$ Please only include the part of the code that's important for this question. \$\endgroup\$
    – Bálint
    Mar 17 '17 at 21:46
  • \$\begingroup\$ And you should probably use multiple files if you aren't doing that already. \$\endgroup\$
    – Bálint
    Mar 17 '17 at 21:47
  • \$\begingroup\$ This is a general C++ question. You haven't made your vector big enough. You push_back 10 elements in a loop, so its size is 10. If you want the vector to be bigger, call .resize() on it, or use push_back/emplace_back instead of assigning the indices directly. \$\endgroup\$ Mar 17 '17 at 23:34
  • \$\begingroup\$ @SeanMiddleditch we've been somewhat less strict about forcing general programming questions to StackOverflow lately. Your comment looks to me like it might work as an answer here - want to try converting it? \$\endgroup\$
    – DMGregory
    Mar 18 '17 at 4:00
1
\$\begingroup\$

You haven't made your vector big enough.

You push_back 10 elements in a loop so the vector's size is 10. If you want the vector to be bigger, call .resize() on it, or use push_back/emplace_back instead of assigning the indices directly.

Your commented-out code uses indices up to 18. Since you can access indices [0,size) in a vector, that means the size would need to be at least 19 for all your indices to be legal. You could assign these in several ways:

// automatically increases size (and capacity, when needed)
for (int i = 0; i != 19; ++i)
  tiles.push_back(tile);

// explicit size/capacity
tiles.resize(19);
tiles[0] = ...;
tiles[1] = ...;
...
tiles[18] = ...;

Remember that it is always illegal to access indices outside the bounds [0,size), so you want to check the bounds anywhere you might have untrusted user data. For instance, with a tilemap, you will generally want to check the values of any tile indices in your 2D grid before looking them up in the tiles array, since a user may conceivably put a bogus tile index into the level.

for (int y = 0; y < height; ++y)
  for (int x = 0; x < width; ++x)
    if (map[y * width + x] < tiles.size()) // check tile validity
      draw(map[y * width + x]);

Note that checking the bounds repeatedly is generally poor form, and may be a big performance sink. In this example, it's likely better to check all the indices at the time the map is created (or whenever a map location is changed) so that it's guaranteed that its indices are always safe to use everywhere else.

While very atypical, you can also use the .at() member function to "safely" access a vector's elements at runtime. The at() member is just like operator[] but it will check the bounds automatically and throw a C++ exception if the index is illegal, which can be caught and handled by your code. This usage is uncommon, especially in games, due to the performance overhead of checking bounds along with the unpopularity of C++ exceptions in the game industry.

vector<int> example;
example.resize(10); // explicit size of 10

example[0] = 1;
example.at(0) = 1; // same as above

example[20] = 2; // ILLEGAL - will crash or corrupt memory!
try {
  example.at(20) = 2; // legal and safe - throws an exception
} catch (std::out_of_range e) {
  // handle out-of-bounds access here
}

See the cppreference documentation for vector for more information.

\$\endgroup\$
1
  • \$\begingroup\$ Thanks very much! After reading through what you have explained help'ed me a lot. \$\endgroup\$
    – Cerberus
    Mar 18 '17 at 8:08

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.