Just a short post. I’ve been mostly working on learning OpenGL, which has left me a little bit frustrated at times. (Though I think I’m making good progress!)
In the regular breaks I’ve had to take, I’ve been playing Touhou 8, and I’ve noticed a particular problem with its bullet patterns, as illustrated below, that I’ve been thinking a lot about.
This is one of Reisen’s spell cards, and as you can see, the perfect circular symmetry is broken by the fact that there are bullets on top of others. Of course, these bullets are simply drawn last, so they have to go on top. But it’s still sort of ugly, and there may be a way around it.
Studying OpenGL has made me think about depth in a different way, even for 2D games. Because while you can render an orthographic projection which makes everything 2D, there is still that seemingly useless depth component to the equation.
Of course, just moving bullets forward or backward won’t do anything useful, but something that’s easily overlooked is that these bullets can also be rotated in 3D space. If we tilt every bullet on its side just slightly, it means that one side will be lower than the other, and thus one side will always get rendered on top. There will not be a “top” bullet any longer, because the bullets are laid out in 3D space in a turbine formation. Thus, any pattern similar to the one above will be rendered with perfect circular symmetry.
I will definitely be implementing this technique once I get a little further into development, and perhaps this is a useful insight for anyone reading my blog as well.
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.
I’ve begun work on a shoot-em-up, and one of the first things on my mind was “How the hell do I program bullet patterns?”
I’ve worked on similar projects in the past and always ended up hardcoding my patterns; do this for so many steps, do that for so many steps, etc. However, if I’m actually serious about this project, that approach is untenable on a large scale. It’s fine for pumping out a prototype or two, but if I want to be able to create a lot of bullet patterns without having to do a ton of coding for each, I’ll need to do two things.
I need to find a universal system that can dictate movement for all bullets
Let me address step[1] right away, as it will be brief to explain what I’m trying to do.
Simply put, I can make a system as robust as I want it to be, but if I don’t make any concessions to increase usability, I’m essentially still hardcoding my bullet patterns--I’ve just put a ribbon on it. I’m still setting all the same parameters and putting in the exact same amount of work for every single part of the pattern. But I’ll be calling my system instead of modifying the bullets’ position directly. It’ll make my code look a bit nicer, but it won’t be any easier to modify.
So onto step[0], the main focus of this post, and quite possibly a number of them to come. Any good system starts with some research, and there’s plenty of information to be found online. I’m hardly the first to try and tackle this issue. In addition to this I’ve done some thinking myself.
That’s when I had the idea. It was brought on by a post somewhere online that mentioned a “weaving” bullet pattern.
I had to sit down and think... How was I ever going to achieve something like that? In a step-by-step system it meant I’d have to change the bullet’s direction one way for a while, and then flip it around, and then back again, forever. I couldn’t think of any way to do that that would be even remotely easy. And yet, a “weaving” bullet is such an easy concept!
That’s when I decided to throw away any preconceived notions about how my system was going to work, and think in terms of pure math for a while. And the answer that math gave me was nothing short of stunning in its elegance. A weaving bullet pattern could be captured using only the following:
x = sin(t); y = t;
This was something of a “Eureka” moment. I’d been far too stuck thinking incrementally... but by providing a mathematical formula, increments are something that doesn’t even enter the picture until much, much later. I can use the variable t to know exactly where my bullet is going to be at any point in time, without having to rely on any of the previous timesteps to have been executed.
In short, my system isn’t going to be using a set of instructions that determine how the bullet’s parameters are going to be updated from one step to the next... it’s going to be using a set of formulae that will always be able to tell you the state a bullet is going to be in at any point in time.
This approach has other advantages as well, such as allowing a variable timestep rather than forcing me to use a fixed timestep, but that is something I might get to at a later date.
However, the biggest advantage that this approach has is that my system's core will be something that already exists and is well-explored. If I build a system all on my own, from the ground up, if I can’t figure out how to do something in it, I’m stuck. However, by using mathematical formulae I can look to the internet for help if I can’t figure out how to do something, not to mention the existence of many great tools (WolframAlpha and FooPlot being some of the more instrumental) that will help me get to an answer on my own.
Using my own incremental system had me stuck on something as simple as a weaving pattern. If I’d used it from this point forward, even if I had been able to solve that specific problem, I’d no doubt get stuck again just as easily.
I guess the moral of this story is... a triangular peg can still fit into a square hole. Look for solutions to the problem you have, not the problem you think you have. Sometimes understanding what the actual problem is, can take a bit of time--but that is time well-spent.