One of the first tool we developed for Drifting Lands was a brand new FX / particle system. The artistic direction of the game is headed toward a very graphic and stylized rendering and we do love traditional animation ! Here’s a sample of particle FXs in the current alpha version. Each of these effects requires from 3 to 20 or 30 particles max.
For Transcripted, we had used our Flash-like MovieClip system but it had a major flaw I didn’t want in the way this time : to have ‘long’ and smoothly animated FX we had to create a big array of bitmap frames and store them in atlases or spritesheets. Though we had total control over the animation, it was requiring a large amount of RAM and VRAM to store the image sequence if the FX was a bit too large.
See this modest organic explosion effect above ? We’ll it’s kind of the only one used throughout all of Transcripted because it’s already taking way too much texture space for what it’s worth ;)
For Drifting Lands, we sure mean to have mighthy big explosions and all kind of pyrotechnic effects all over the place. Going the Transcripted way didn’t feel right from the start.The art style we have chosen will not suffer low-resolution or blurry textures stretched to big explosions. A few months ago, a game I’ve played helped me realize that there was other techniques to achieve extremely cool effects : Prince of Persia (the 2008 reboot by the talented folks at Ubisoft Montreal).
First, let me clarify something : I don’t mean in any way that this game was the first to use this technique but it is the first I’ve played that struck me as using it so apparently and so cleverly. In fact, I don’t mean either that this game is actually working as I will describe but it’s what I’ve interpreted and how we translated it in our own tools.
What is special about the FXs and particles in Prince of Persia? Well, they use a kind of threshold-based animation when they disappear (which is pretty standard) but a lot of them also have a different threshold-based animation when they appear. The fact that the appearance animation is different is quite important : it does not look at all like a ping-pong animation.
The simplest way to animate particles’ appearance and disappearance is through a simple fade of opacity applied uniformly. Often there’s not even any fade-in animation : the particle just pops and then fades away while moving, scaling, rotating, shifting color, etc… To produce visually interesting effects with those, you often need a lot of particles.
When I speak of threshold-based animation, I mean a kind of animated mask based on a gradient map. A threshold operation is the simple act of testing all the pixels of a bitmap against well, a threshold value. Pixels with a higher value are treated one way and pixels with a lower value are treated differently. Typically if you want to make a threshold animated mask, you use a grayscale map against an animated value : pixels ‘brighter’ than the current value are opaque, pixels darker than the current value are just ignored. It will give you something a bit like that :
Notice how the edges of the disappearing shape are very pixelated and absolutely not anti-aliased. That’s your typical basic thresold effect revealing its binary nature. If you are familiar with Photoshop you may know, that you can achieve something very similar to a thresold mask on a grayscale base but with a much nicer quality : to do that, you have to use the “levels” controls. And you will end up with something a bit like that :
Better eh ? So what are we exactly doing by modifying the levels of a picture ? It’s really not that complicated : your base grayscale image is composed of pixels with values between 0 and 255 (you can only do that much with 8 bits). With levels, you kind of ‘resample’ the scale of brightness : you define a new black and a new white among the greys of your original picture. Everything previously darker than your new black is black and everything previously brighter than your new white is… white of course. Everything in between the values of your new black and new white is ‘stretched’ to cover the whole length of the luminosity range.
If you select a range with your new black and white which is very very very small. You’ll end up with something terribly similar to a simple thresold. If you select a relatively wide range of values, the effect will be more diffuse. But if you select a small range, you’ll get the previously presented result : something looking like a sharp shape with anti-aliasing.
That last setting is perfect to do what we aim for in drifting lands : a nice animated mask with clear edges but not so much as to be devoid of any anti-aliasing.
We know what we want but how do we achieve that in Unity ? Well, that’s just shader work from there. Applying a level operation on a grayscale picture is just one substraction , one multiplication and a clamping of the result between 0 and 1 :
new_luminosity = saturate ( ( old_luminosity – B ) * S ) ;
B is the value of the new black between 0 and 1 in the range of luminosity of the original image.
S is the scale applied to the range of luminosity. S is actually equal to :
1 / range of luminosity from the original image if the range is expressed with a float between 0 and 1…
… or 255 / range of luminosity from the original image if the range is expressed with an integer between 1 and 255.
saturate() is a Cg function which will essentially clamped the parameter value between 0 and 1 (everything above 1 will be 1, everything under 0 will be 0, everything else doesn’t change).
As I told you earlier, what’s cool about PoP’s particles is that they appear and disappear with a different animation. So we don’t need one but two animated masks based on a threshold or level operation ! We need only grayscale informations and because it’s always a good idea to work with the minimum number of textures, we will fuse these two sets of informations into only one texture : one mask will be stored in the R channel, one mask will be stored in the G channel. Since we don’t like to waste, we will also use the B channel to modify ‘per pixel’ the size of the range applied in the level operations. By just applying a different value to the blue channel we can decide in the shader wether the appearance or disappearance will be sharp-edged, soft or even blurry for each part of the FX.
All these informations stuffed into a single texture will look a bit like that :
It’s clearly not something you can draw directly from scratch, so how do we produce those ? As usual when it comes to animation, we try as hard as possible to make it with Flash. Because we love Flash <troll mode>and no we don’t like HTML5 very much for now</troll mode>. Ok so we need one appearance animation, one disappearance animation (and usually we will add the optionnal mask in the blue channel by hand in photoshop). One thing you have to remember is that we can’t do EVERY animations with this technique because it’s ‘just’ threshold animation. So basically, for each frame of the appearance animation, you can only add stuff compared to the previous frame. And for the disappearance animation you can only erase stuff compared to the previous frame. Another thing is that for now, it’s only a mask. We will add colors later on but for now let’s consider only monochrome effects.
Our Flash workflow is as follow : we draw one MovieClip with the appearance animation. Usually we start with the end frame where the FX is complete (a flame, a cloud of smoke, a lighting bolt) and we move backwards erasing one bit every frame. Then we do another clip for the disappearance animation. Its first frame is the exact copy of the last frame of the appearance animation and we erase one bit every frame moving forward this time. In theory, we could have as much as 255 steps of animation but we usually only draw 10 to 20 or 30 steps for each half of the FX life. That’s already way more than what we could reasonably do with spritesheets in Transcripted ! For now, every steps of the animations are hard-edged but we will be able to choose the softness of the actual effect later.
Then it’s actionScript time : we just take the opacity channel of each frame of the animation, we divide it by the number of frames of the animation, and we draw additively each bitmap obtained on a black canvas. This will lead for the appearance animation to a black and white gradient image where the bright part is what appears first and the dark part is what appears last. For the disappearance animation, the bright part is what stays longer. We use an additional trick to save time : we add some blur to each step of our animation before the additive draw operation. Adding blur does not mean we will have a blurry FX in the end because this is decided only by the levels operation’s parameters (in the shader). Adding blur here is just a way to simulate additional steps to our animation. The result would be roughly the same if we had no blur and took the time to create 255 steps of animation. The 2 loops over the 2 MovieClips produce two greyscaled BitmapDatas which are then drawn into the red and green channel of the final image. We usually just press the print screen button, copy this into photoshop and possibly add some informations in the blue channel to define where the FX will have to be more blurry or more hard-edged when treated by the shader. The 2 animations do not have to have the same number of steps and will most likely not have the same duration in the game.
We’ll stop here for now… In the next part, we will study the actual implementation of everything we’ve seen so far in the Unity engine and how we can add some cool extra color manipulations to enhance the effects.
You can download here a zip file with a FLA (CS5 or higher) example of our export process. The actionscript code is included in the first frame of the timeline and the exported clip must be on the stage and named ‘clip’.
See you soon !
If you like what we do, please share our work by liking our facebook page by following us on twitter or sharing our youtube channel. We’ll self publish Drifting Lands and we need all the love we can get out there ;)