EMMA

A Catgirl Software product

8 - Particles

Having recovered from my previous post-Doing Things malaise, it’s time to get back to where I really belong - weirdly technical posts about game design and problem solving. Which I haven’t actually done on this blog very much, it’s mostly been more on the artistic side than the technical - my personal blog series about a previous game project were more technically involved, I think. But it’s never too late to start dragging that in here.

Right now, a lot of what we’re doing is aimed at improving the feel of the game, and one of the ways we are doing that is with interesting visual effects. Which means we get to dive into the Godot particle system!

The Anatomy of a Particle

Particles are, simply, a system for generating lots of visually similar things at once. If you play modern games you’ve probably seen the idea, if not been conscious of it; rain effects, smoke clouds, electrical sparks, bursts of flame or fireworks or explosions are frequent uses of particles of some form. On a high level, that’s all a particle system is - a way to draw lots of the same thing easily.

But this is my domain, and I don’t want to stop at the high level. So. Typically, your computer/console/phone(?)/particularly rigorous imagination works by doing lots of maths, and it does this in two separate parts. The CPU is good at doing lots of different things pretty well, and it is supported by a GPU, which does certain types of maths very well and very quickly indeed. Simply, GPUs can do lots of parallel calculations on different blocks of data at once, which makes them great at generating images to display on your screen (and also at things like solving the maths that drives cryptocurrencies, or processing machine learning models, which has made them increasingly valuable over time, much to the frustration of people who just want to have pretty images on their screen).

Config for our ember particle effects, showing all of the options available for changing

Most of the logic in a game is processed by the CPU, but handling Large Numbers of Things can make that difficult1. If you have lots of explosions going off, generating hundreds of game objects for each one could scale into the tens, or hundreds, of thousands. But as it turns out, all of those objects have basically the same behaviour, they don’t need to do anything beyond exist and fly off in a direction, and you have a separate processor that is great at handling lots of the same thing! Which is the actually interesting thing about particle systems, or at least the one we use in Godot; by generating and processing them entirely on the GPU, you can have tens of thousands of particles with the same basic behaviour without even breaking a sweat.

You can even have tens of thousands of particles with not-so-basic behaviour; as it turns out, there are a lot of things that GPUs are very good at doing to images. The Godot particle system gives lots of tools for controlling particles’ behaviour; physics simulation effects like movement, gravity, drag, and random movement from things like air currents, but also changing their size, colour, having them deform with their movement, or even having them emit other particles with their own behaviour. Most of these controls have options to randomise for each particle within a range, or change their value over the course of their lifetime with precise detail, giving an impressive level of control over the particles created without ever actually touching them with code directly.

The unfortunate side of this system, though, is that we can’t touch particles directly, even if we wanted to; existing on the GPU, so far away from the warm, safe realm of the CPU and memory, means they’re kind of out of reach. They’re not detectable, they’re not controllable, you can’t make references to them or pass them around or anything like that, because for all intents and purposes they don’t exist. Particle emitters are real, those exist and we can move them, but the things they emit are nothing more than the guided hallucinations of a distant graphics card.

Implementation - Embers

Recently we’ve been working on a couple of particular2 uses of lighting, particle effects and other visual systems to give individual elements of the game a vague sense of satisfying polish. One instance that’s been given a lot of focus adds sparks and burning embers, spewing out of the blacksmith’s forge.

A gif of the blacksmith's forge in the game, showing ember particles being emitted

Most of the design for these particles was done by Sean, so I don’t have too much to say about it - we’re using damping and randomly directed turbulence heavily to give the particles a feel of light driftiness, like they’re being carried around by air currents, and a shift in colour and size over time simulates burning embers flaring and then going out as they are exposed to the cold air outside the fire. I like them, I think they’re kind of cute, and they add act as a neat little system to indicate the heat of the forge; the hotter your fire is, the more sparks come out. Which sounds simple, and for almost anything else in Godot it would be! But particles come with some unique challenges, and that’s where this particular feature became my job.

Now, I need to be clear, much of the technical details in this article are educated conjecture. I haven’t studied the engine code in question, but this is what I can glean from help forums, github feature proposals, and other discussions of the systems online. With that established; as we’ve discussed previously, particles exist in the far off land of GPU processing, and they do things efficiently over there. When your particle emitter code requests, say, 50 particles, the GPU nods and sets aside a little block of memory that is enough for 50 particles. The particle amount is typically how many will be on screen at once; when a particle reaches the end of its existence, its block of memory will be repurposed for the next particle to be emitted, and so the GPU can keep using its little block of particle memory very efficiently.

But the moment you change how many particles you want, that block of memory is the wrong size! You can’t necessarily expand it - blocks of computer memory kind of exist in long strings, like segments of tape or frames in camera film, and you want your particle memory to be all in one piece, and what if there’s something else already in the next block? And you don’t really want to shrink it either, because then you’re leaving a really annoyingly sized space for something else to try to use. So the GPU gives up on your current memory block and goes and finds a new place to put your particles - abandoning the particles that currently exist. Effectively, you can’t change the number of particles that an object is emitting without completely resetting the emitted particles, and since these ones normally hang around for a while, the effect is very noticeable.

The same particle display as above. The number of particles being emitted is changed twice in a full run of the gif, and each time all of the existing particles vanish and are replaced from nothing

So, if we’re changing the number of particles, we don’t want to do it that way. The next thought was to have each emitter only handle one particle, and to create or destroy or turn them on or off as needed, but this version doesn’t work either - some effects, like the even flow of particles or the random turbulence that catches nearby particles in the same way like a gust of wind, only work between particles that are from the same set and processed as a group. You can imagine how “emit 10 particles evenly spaced over a second” differs from “emit 1 particle evenly spaced every second, right next to your 9 clones that are doing the same”.

So we need some mix of these approaches. After some testing, it turns out about 4-6 particles is the number you need for them to start feeling cohesive and smooth, so each emitter handles 4 particles each, and we have (number / 4) emitters active at any given time, which gives us enough resolution to turn one on or off to change the number without it feeling like too much of a jump.

Display of the forge as it cools and fewer particles are emitted over time.

Future Changes

At some point I’d like to make this slightly more nuanced; have emitters that each handle 10 or so particles, and have a separate set of emitters that can be turned on or off individually for steps in between. Or the more interesting version, which I think has individual emitters for 1-4 and then powers of 2 above that, letting you easily make any total number of particles by turning specific combinations on and off. This feels like an annoyance that I have seen enough people having that I will probably try to solve it well at some point, and I am not sure exactly what state that will be when it happens but maybe I’ll come back to it at some point.

Right now we are working on funding applications, trying to plan budgets and timelines and figure out how to give ourselves the best shot at success. Actual development is taking a back seat for a while, so I’m not sure what to write here for the next few weeks, but I’m sure I’ll come up with something :)

Thanks for reading~

Notes


  1. Note: If you happen to be a game developer, or indeed any kind of programmer, please ignore this concept. Really. You will Never Have A Problem with having too many things. Don’t Worry About It. Modern CPUs are stupidly good. Did you know your computer runs so fast it probably gets rate limited by the speed of light? Genuinely! At the speed my laptop’s CPU runs, a signal literally cannot move more than 12cm in a single clock cycle. Technological design aside, the laws of physics dictate that it is impossible for my computer to access data from a USB drive in a single processing cycle. And this isn’t even a particularly impressive CPU. So please, I beg you, ignore me. Have your Large Numbers of Things. By the time that ever becomes an actual problem you will know better than I do what to do about it. 

  2. hehehehehehehehehehehehehehehehehehehehehehehe