Creating Colliders by Magic

So we realized that our wall colliders were too many to be efficient. Each wall segment the size of one tile had its own collider which was created by the tile itself at the time of its instantiation. Every wall instance knew what kind of wall segment it was (information that was also used for determining which texture to load), and shaped the collider accordingly. (We had some struggles here because the shape of certain wall segments called for a more complex collider than a rectangle. It could have been achieved with two rectangular colliders, but this would have caused further problems with looping through all wall colliders when using them for collision checking; so we stuck with only one for each segment but adjusted the size of some colliders until there were no gaps.) In the picture below, the colliders can be seen visualized in yellow. Note that the angled perspective entails the colliders being placed at the bottom of the walls.

old_colliders_EDIT2

The point is that many colliders in a row just as well could be merged into one big collider to greatly reduce the number of collision calculations to be done. I coded a function that looks at the tile map and creates the colliders separately from the walls, storing pointers to them in a separate std::vector for later access. The result looks like below.

What it basically does is finding the end segments and creating colliders between them. It does so by looping through the walls looking at one at a time, starting at the upper left corner. The tile map can be seen below.

First of all, we copy the numbers in the tile map to an std::vector for easier access, like below. (The numbers in the tile map are actually separated by commas and it doesn’t contain any blank spaces like above. Those just make it align more prettily for a blog post.)

std::string number;
    for (int i = 0; i <= height - 1; i++)
    {
        for (int j = 0; j <= width - 1; j++)
        {
            std::getline(dataStream, number, ',');
            wallData[j + i * width] = stoi(number);
        }
    }

We do horizontal and vertical walls separately. The first round we do horizontal, and start by looking for any segment that works as a left end. (There are four different: 1, 3, 8 & 14.) After finding one, we step to the right until finding any segment that works as a right end (2, 4, 7 & 14). We then create a collider between these two tiles (the height being the constant value of a wall’s thickness). After this we continue from the rightmost tile and repeat the process of finding a left end segment and then a right end segment. When having reached the end of the row, we go to the row below, and so on. The code for this can be seen below:

int index = 0;
while (index SetWidthHeight(colWidth, 32); // The height of horizontal walls is 32, the walls' thickness
                m_wall_colliders.push_back(collider); // Put the new collider in an std::vector for easy access
                index = search; // Set the position for the next search
                break;
            }
        }
    }
    index++; // Step one index to the right
}

 

Creating the vertical walls is very similar, except counting with adjacent positions vertically requires a bit more thinking than horizontally. For example, going one position down means adding the map’s width. I simplified the iteration so some unnecessary positions are checked, but this is insignificant in terms of optimization since it is still quite light and, most importantly, only performed at the start of each level.

index = 0;
while (index < size)
{
    if (wallData[index] == 1 || wallData[index] == 6 || wallData[index] == 2 || wallData[index] == 10)
    {
        for (int search = index; search SetWidthHeight(32, colHeight);
                m_wall_colliders.push_back(collider);
                index++;
                break;
            }
        }
    }
    index++;
}

About Johan Öhman

2014  Programming