Back to the elliptical collisions I mentioned in my last post. I’ve experimented a bit. There are several pretty quick ways to approximate an ellipse, but none of them have great results. For starters, take the following approach:
To give you a quick visualisation, I’ve thrown together a demo prototype. White is the actual ellipse. Red is the predicted radius.
Well, after a bit of thinking I came up with something. Lookup tables. This does mean I’ll have to do a bunch of expensive computations when a bullet is created, but once those are completed, I’ll be able to retrieve the radius relatively quickly. There is only one problem, and that is that I can’t have lookup tables that are infinitely large. This means I have to sacrifice on precision.
After some tweaking I got the following:
As you can see, there are some artifacts, but it’s much closer than the image above. On small enough scales they will be basically unnoticeable. It’s fairly good on an ellipse of size 100 x 50 and I don’t think I’m going to be using any elliptical bullets that large anyway, meaning that I could potentially reduce the resolution of my lookup table a little further without sacrificing on player experience.
I haven’t done enough testing yet to conclusively say that I’ll be going with one method or another, but this looks promising, and I think I should leave this as-is for the time being and move on to other matters.
Here is how I’ve achieved the lookup table:
int resolution = 10 * r1 / r2; // Assuming r1 > r2 vector<float> radiuses(resolution + 1); for (int i = 0; i < radiuses.size(); ++i) { float rad = i / 2 / resolution * π; float s = sin(rad); float c = cos(rad); radiuses[i] = (r1 * r2) / sqrt(r1 * r1 * s * s + r2 * r2 * c * c); }
I’ve made resolution depend on the difference between r1 and r2. The larger the difference, the more important having a high resolution is (a resolution of 0 would be a circle). I’ll have to tweak the resolution further, but I’ll do so in the future.
The resulting vector is representative of a 90-degree slice of the ellipse. The only thing that remains is a way to retrieve the correct value even if I’m not on that slice, which would be a simple triangle wave 1 - abs(mod((α / 90) + 1, 2) - 1) where α is the angle I’m retrieving.
It’s possible (I’d even say likely!) there’s a faster way of doing all that, but it seems reasonably efficient, which at this point in time I’m satisfied with.
One of the most important problems to tackle in almost any game is determining when and how elements of the game interact.
Luckily, in a scrolling shoot-em-up, there’s not a lot of things that can affect an element’s behaviour. Mostly, it’s limited to the enemy targeting the player, and the player being unable to leave the screen.
Luckily, unlike in a lot of other cases, it doesn’t matter from which direction the collision takes place, or how fast, or really anything. All that matters is that the collision occurs. We don’t have to move the player out of the bullet like you’d want to move a player out of a wall if they clip into it. It’s very simple. We merely need to know if a collision has occurred.
One of the most straightforward methods of collision checking is using a bounding box. It’s pretty quick, but unfortunately, it’s a box, and my bullets are generally round, which means it shouldn’t matter from which direction the player approaches the bullet.
So with bounding boxes ruled out, I need to start looking at other methods. The most important thing to consider here, is what shape my collisions should be. And the answer to that is “generally round”. We’ll get to “generally” later. For now let’s stick with “round”.
The only variable a circle has is its radius. If two circles overlap, that can be rephrased as saying that the distance between the circles’ centres is less than that of their combined radius. Thus, a simple collision check between two circles will be no more complicated than this:
Using the Pythagorean theorem is perhaps somewhat slow, but it’s the only thing we have to do. There may be a few ways to make it faster (if we know that abs(c1.x - c2.x) > c1.r + c2.r || abs(c1.y - c2.y) > c1.r + c2.r we can be confident there’s no collision without using the Pythagorean theorem, letting us skip a more expensive computation) but this should do the trick.
You may have noticed, that this is a collision check between two circles, not a circle and a rectangle. A character’s collisions are often handled by a bounding box, so it makes sense to assume I’ll be using one. However, this is unnecessary. Many bullet hell games use “magic pixel” collisions for their player, which means that the area of collision tends to be far smaller than the player’s sprite, usually only a couple of pixels in size. Using a circular collision area makes perfect sense for a game like this, so I will be using a “magic pixel” to simplify collision checks.
With “round” out of the way, let’s get to “generally”. Not all bullets will have the same shape. Most will be roundish, so a circular collision area will suffice, but there will be elongated bullets as well, and perhaps on rare occasions even bullets with more complex shapes. For these sorts of objects I can use a set of circles, but this might not be ideal. Another option is to use ellipses.
Ellipses complicate the simple collision checking function outlined above quite a bit, but they could still be preferable over using a bunch of circles. I haven’t yet tested this, but I will definitely be going into more depth on how to check collisions between ellipses and circles in another post when I get there.