Game Design, Programming and running a one-man games business…

Programming RTS Unit selection outlines

Programming RTS Unit selection outlines is a pain. I had an amazing GUI mocked up by an artist, and most of it is now in Gratuitous Tank Battles, but today’s todo list included unit selection outlines. He had mocked up this:

And now I want to batter him with a baseball bat.

(I always tell artists to do the best stuff they can possibly imagine, and let me worry about how I’ll make it work. Reach for the stars etc…)

You might think it’s easy. Just draw the image bigger first, with a flat shaded effect (I use render states, but YMMV), and then draw the unit on top. WRONG!

Firstly, that means your UI doesn’t shine through smoke or other effects layered on top, which isn’t as cool. Secondly. it means the units shadows are cast onto its own selection UI (yuck) thirdly, it just plain doesn’t work.  It works with squares or circles or other basic shapes, but take a complex image, scale it up, then draw the smaller image inside it. See what I mean?

What I really need is some way of doing what photoshop calls the ‘stroke’ effect, where the outline of an image is expanded. Not expanded directly from the image center, but expanded in all directions.

One solution mentioned online is to draw the flat-shaded bit (enlarged selection) 4 times, moving it up down left and right by 1 pixel each time. That’s great if you want a 1 pixel outline, but 1 pixel sucks, and beyond that, you will get corner issues, plus… 4x rendering potentially every unit in your army every frame is not nice.

Another solution would be to have extra versions of each sprite, already-scaled up in photoshop, and render them for the enlarged versions instead. This has issues where the image already touches the sprite corners, and in any case, that means that the selection outline is a fixed percentage of the unit size, rather than a uniform 4 or 8 pixels, which would look tons sweeter.

So… how did I fix it?

I haven’t yet… It’s driving me bonkers :( I am considering an offscreen render target that blurs a matted sprite, but that wouldn’t be crisp, which I think would look better. I wonder how they did those outlines in age of empires 2?

Note, almost all discussions online about this refer to 3d meshes. I use 2d sprites with alpha channels. Totally different :(

 

edit: this is what I have so far, quite like it, may compromise on it…


22 thoughts on Programming RTS Unit selection outlines

  1. Run an edge detect shader on the data in the alpha channel? Unless you have semi-transparency that should give you an accurate outline.

    Or since you hae a 2D game, you could pre-render the outlines using stroke in photoshop, and draw the correct outline sprite when the unit is selected. They should compress reasonably well, and you could just use it as a mask and fill in the colour/tint at runtime.

  2. I’d go with Michael’s suggestion. You don’t need to do anything fancy – just pregenerate all of the selection images. :) It’ll increase the size of the game of course, but it’s a simple solution that will work and will likely be pretty fast to render too, I imagine.

  3. but then they are a fixed size…

    If a tank is zooedm out and is thus 32 pixels accross a 2 pixel wide selection outline on the texture itself will be too small to see, whereas if its zoomed to fill the screen, the 2 pixel outline will be huge and ugly.
    Trust me, I wish it was that simple :D

  4. A mask for each sprite is probably your only real option. You could handle the zoom effects by having a gradient on the mask and fiddle with the gamma (?) as you zoom in and out to keep the outline size sane. As a plus you could add more images to make frames so the object’s outline could pulse or such when selected.

  5. Have you looked into stencil buffers?

    I’d imagine getting one working in a 2d game would be “interesting” – (and by interesting I mean extremely tricky)

    In AOE2 they just made a “placed building” mask and then did a pixel-colour swap on any units that were rendered in the mask. the outline of the characters didn’t “grow” like you’re trying to achieve.

    What about thick Line rendering? (give every object a vertex list and draw a dot-to-dot outline) – then alter the thickness depending on the zoom level.. not as nice as the screenshot but it’d work…

  6. ah, very very interesting indeed. I had no idea it was a pixel colour swap. I couldnt find any screenshots when i googled it, so went from memory.
    I’m half way through a possible method involving an offscreen render buffer right now…

  7. Just use an alpha sprite overlay scaled by a value that depends on the actual zoom to adjust for the < 1 pixel problem , this can be done in a lookup table to ensure decent performance and the overlays should be mip-mapped for LOD.

    I'm using jargon here that might be slightly wrong as I'm still learning this!

    But you should be able to get the gist of it.

  8. Yup, mech heads are separate layers.

    regarding simple scaling… it won’t work. Imagine a sprite of a dumbell. Now scale it and overlay the larger sprite behind the first. The centers of the two balls actually get further away, and thus the outline is not an outline at all.
    This is my problem :(

  9. Actually, I take it back, it DID do an outline on AOE2 – but it didn’t “grow”, they just outlined the sprites when they went behind something…

    (I imagine they probably just drew the pixels they wanted to colour swap in another channel in an art package)

  10. If you want the outline size to stay sane regardless of zoom, then the screen space effect using an offscreen buffer that you hinted at will probably be the most direct way. That will get you something similar to the Left 4 Dead teammate outlines – render the objects to be outlined so that they set a flag in the stencil buffer, fill the stencilled areas with the outline color in the offscreen buffer, do your blurring and add the results to the screen buffer.

    Another way to do this without all of the offscreen buffer work would be to use signed distance fields (for this one the reference Valve game is Team Fortress 2: http://www.valvesoftware.com/publications/2007/SIGGRAPH2007_AlphaTestedMagnification.pdf). Basically for each pixel in the texture, you store its distance from the opaque-transparent boundary in the alpha channel, and set the alpha cutoff value to control how much “outline” gets drawn. The idea is that you would animate the cutoff value according to the player’s zoom value so that you get the correct outline width (which you wouldn’t get just by scaling, as you said). It’d be a little harder to get the soft edge from the concept, but still possible if you play with the alpha value in a pixel shader. You would also have to make sure that there’s enough empty space around your sprites for the distance field. The first and second problems you mentioned would still need to be solved, but aren’t those just rendering order problems?

  11. Can’t you just precalculate all the outline images and just render them at the right time?

  12. Plus one for the Flashers because this is super easy to do with the ActionScript 3 API. Even if you’d want to have the outline on a different layer than the unit itself.

  13. Why don’t you just transform the sprite into a binary image in the outline color, use a dilation operation, blur it, subtract the binary image and draw the result on top of everything else? Fast, simple and should give exactly what you want.

  14. anon’s method for using signed distance methods is pretty cool. If you don’t want to create signed distance textures for all of your sprites or do the scaling math, here’s a technique I’ve used before to get a similar effect (very much like the L4D teammate outlines, and basically what anon described):

    1. Render all of your selected units into a render target the size of the screen using a shader just fills the unit with their selection color. This’ll let you do different selection colors for different types of units.
    2. Render your scene normally into the back buffer.
    3. Render all of your selected units into the stencil buffer without rendering into the color channels.
    4. Render your selection image into the stenciled back buffer with a shader that performs a gaussian filter over the image. This’ll get you your blurry stenciled selection hilights.

    The effect doesn’t require new sprite data, will be zoom-independent, won’t have layering issues, and is pretty fast.

    I suspect this is logically very similar to the process your artist went through in photoshop to create that look target – he might even be able to give you the gaussian blur’s tuning parameters :)

    Fortunately, these days it seems like a lot of artist-designed effects can be achieved by just duplicating the steps they went through in photoshop in postprocessing shaders.

  15. I think you could do it this way, break the sprites up into several pieces each of which is a convex shape, then just scale those and place at correct locations, that way you’ll avoid the dumbbell effect, which only affects concave shapes. That might, or might not, be cheaper than the blurring effects.

  16. You are coming at this from the wrong angle, when a unit is selected it doesn’t need an outline you have two copies of the tank, one is a greened/solidgreen, the other is normal brown tank colors. No highlight around the edges is needed because you are informing the player the unit is selected.

    See kind of like this car, notice the car is one solid semi flat color.
    http://pixelpainter.com/blog/PS3Dscene-04.jpg

    One thing that has annoyed me about modern games is the desire to “pretty up” UI, UI has to be functional. The best way to do it would just be to shift the pallete up and down in brightness using patterns that look good. See sprites in old 2D games for the effect, specifically: Final fantasy 6.

    http://www.youtube.com/watch?v=gwgjGhPYQgI&feature=related

  17. I’m not really into the engine specifics or graphics programming, but I assume you have some kind of alpha channel that can tell you the shape of the unit? What you mentioned about rendering off-screen and then blurring, well, in stead of blurring, you could apply a dilation operation or convolution, no (that would expand the unit, just like you seem to want)? Isn’t there some fast function for doing a 2d convolution? And if you want just the outline, you could literally subtract the original alpha channel? I’m not sure how much of this you can do in the GPU though, like I said, I don’t know much about graphics programming.

Comments are currently closed.