0
\$\begingroup\$

Hey I am a bit confused about how to properly check a Jagged array for "index out of range exceptions"

Im quite sure im somehow supposed to use the .Length of the array but it confuses me to read this, because my tiles are of a TileType type (enum):

https://msdn.microsoft.com/en-us/library/system.array.length.aspx

https://stackoverflow.com/questions/37233225/c-sharp-how-can-i-check-if-a-multidimensional-array-has-a-value-and-its-index

https://stackoverflow.com/questions/25562496/how-to-check-if-multidimensional-array-row-contains-non-zero-value

What im doing is creating a grid of tiles and running through them after the fact and setting their values to have a collider (red in this case) if they are in the correct position. In order to do this I have to check the tiles surrounding the one I am currently on. This sometimes, albeit rarely, leads to an index of out range exception because the tile I am on is on the edge of the tile grid and thus it cannot find the tile I am looking for.

Here is a small peice of the code:

 void SetTilesValuesForRooms()
{
    // Go through all the rooms...
    for (int i = 0; i < rooms.Length; i++)
    {
        Room currentRoom = rooms[i];

        // ... and for each room go through it's width.
        for (int j = 0; j < currentRoom.roomWidth; j++)
        {
            int xCoord = currentRoom.xPos + j;

            //// For each horizontal tile, go up vertically through the room's height.
            for (int k = 0; k < currentRoom.roomHeight; k++)
            {
                int yCoord = currentRoom.yPos + k;

                // Make the left wall have colliders
                // this line is the culprit    
                if (xCoord == currentRoom.xPos && tiles[xCoord - 1][yCoord - 1] == TileType.Wall || tiles[xCoord - 1][yCoord] == TileType.Wall)
                {
                    tiles[xCoord - 1][yCoord - 1] = TileType.Red;

                    if (yCoord == currentRoom.yPos + currentRoom.roomHeight - 1 && tiles[xCoord - 1][yCoord] == TileType.Wall)
                    {
                        tiles[xCoord - 1][yCoord] = TileType.Red;

                        if (tiles[xCoord - 1][yCoord + 1] == TileType.Wall)
                        {
                            tiles[xCoord - 1][yCoord + 1] = TileType.Red;
                        }
                    }
                }

Im quite interested to know how I would properly go about checking if the tile I am about to correct is out of range or not.

thank you.

\$\endgroup\$
2
\$\begingroup\$

Length of Jagged Arrays can be a little weird. Since a jagged array is just an "array of arrays," you need to know if you're asking "How many arrays do I have?" vs "How long is this array?

If you have a 2D jagged array x[][], saying x.Length will give you the number of arrays within x. Calling x[0].Length will tell you how long the array at index 0 is. The contained arrays do not all have to be the same length.


Before we talk about your other question, here are a few quality of life suggestions.

You're mixing for loops and this gets VERY confusing! Separate your room iteration from your tile iteration. When using generic iterators like i,j,k it's very hard to keep track of which loop vars are which, especially when different vars iterate over different sets. Move the layers to different methods, it makes things easier to read, develop, and bug test.

For loops don't have to start at 0: rather than having iterators that are one off variables, turn those into your vars. Rather than having int xCoord = currentRoom.xPos + j; write for(int xCoord = currentRoom.xPos; xCoord < currentRoom.xPos + currentRoom.width; xCoord++) Saves you a line of code, and allows your code to function as written rather than have a layer of abstraction. This is personal preference, but it keeps the loop code in the loop and the useful code in the body.

Rather than checking if your answer, write code that can't be wrong: This isn't always applicable. But try to reduce the amount of possible errors. Best case is 0, but any error you can't create is an edge case you don't have to check.


You're implicit question of how to iterate over tiles in a grid can be solved in several ways.

You can do a multipass check. This prevents the need for error checking since you can guarantee that you'll never exceed your boundaries, but requires significantly more code. this generally works by writing code blocks for each edge case (and corner case).

It could look like this (if your room is rectangular). If not, just break each edge into it's own method. This (quite literally) removes the edge cases and allows free reign on the inside elements.

void checkTopBottom(){

   //check corner values first
   checkCorners(Top);
   checkCorners(Bottom);

   //checks top and bottom edges
   for(int i = 1; i < width-1; i++){
       checkTop(i);
       checkBottom(i);
   }
}

void checkLeftRight() // same as TopBottom but using different direction

void checkGeneral() // standard double for checking everything else

The easiest, and most compact / maintainable way is to compartmentalize your problem. Check a specific tile's neighbor in its own method. In that method bound your iterators to the array's min/max values. This requires error checking since border tiles will always have at least one wrong tile (equal to the current tile). However 1 error is easier to check than 3.

Here's a rough approach of how I would do it.

foreach(var currentRoom in rooms){
   checkRoomTiles(currentRoom);
}

void checkRoomTiles(Room current){
    for(int x = current.xpos; x < current.xpos + current.width;x++){
        for(int y = current.ypos; y < current.ypos + current.height;y++){
            checkSpecificTile(x,y, current);
        }
    }
}

void checkSpecificTile(int x, int y){
   Tile current = tiles[x][y];
   for(int xMod = x-1; xMod <= x+1; xMod++){
      for(int yMod = x-1; yMod <= x+1; yMod++){

         //bounds checking. Ensures node is always within range
         tX = max(tX,0); 

         // x length is the length of the array of arrays
         tx = min(tX, tiles.Length-1);

         tY = max(tY,0); 

         // y length is the length of the current array
         tY = min(tY, tiles[x].Length-1);

         //checks if the error correction made us reference current
         (if tiles[tX][tY] == current) continue;

         handleTile(tX,tY);
      }
   }
}

Don't expect the code to work as written, there's probably errors somewhere. But this should give a general idea.

\$\endgroup\$
  • \$\begingroup\$ Thank you so much for thorough answer to my question, the detail in your response is much appreciated. I played around with something similar to this but not as concise or clean. However in the meantime the solution that I found that has worked very well was so simply clamp the rooms upon creation so that I do not end up going outside of the array which was in the problem in the first place. Your answer however has shown me a nice way to clean up the code that sets values for the tiles and gave me good insight into how to do work with jagged arrays in the future. Much appreciated! \$\endgroup\$ – Svp Feb 20 '18 at 22:19

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.