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.
Anya is live and ready to show you everything. Watch her strip, dance, and perform exclusive shows just for you. Interact in real-time and make your fantasies come true.
ā Live Streamingā Interactive Chatā Private Showsā HD Quality
Anya is LIVE right now
FREE
Free to watch ⢠No registration required ⢠HD streaming
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.