2D Reflections, Quick & Dirty


Final result of reflections at time of writing:

Tree Reflections

In addition to reflections, I added support for underwater terrain that ripples along with the reflections when enabled:

Underwater Terrain

I knew adding basic reflections to the water would add a lot of much-needed depth to the visuals, but I had some pretty strict requirements:

  • No manual changes to every level -- I have a lot of levels at this point, I needed to be able to do this by modifying existing prefabs
  • No new sprites -- from the images below, you can see this is a tough one, as I'll get into later
  • Performant on mobile

My biggest hurdle was finding a way to reflect the player sprite without commissioning or creating new art. Because I'm using an isometric perspective, not all sprites could simply be flipped over the X axis to obtain their reflected counterpart. Although this can be done for objects like trees, cubes, or other basic shapes, objects that are "facing" a direction other than forward (that are not somewhat symmetrical over the Y axis) will cause problems.

Take this for example:

Incorrect Reflection 1

At a glance this may look correct, but the reflection is clearly facing the wrong direction -- it should be "looking" southwest, but is looking northwest instead. You might say, why not just reflect a different sprite, but this is also clearly incorrect:

Incorrect Reflection 2

However, I noticed that the general shape and direction of the reflection is almost correct here, even if the sprite looks entirely incorrect.

So in the end, I compromised by fading the reflection to a single color so that the reflected sprite doesn't stick out as much. I also used a basic water ripple effect to mask the issues further. Here's a close-up of the final result on the player:

Correct, "Hacked" Reflection

Admittedly, this doesn't look as good as the reflections on the trees, and I may have to dial back the fidelity on the tree (and other object) reflections to avoid it looking bad by comparison. But the shape of the reflection is spot-on, and doesn't draw negative attention to itself.

To implement the reflections and the water ripple technically speaking:

  • Made the water semi-transparent, over a blue background layer
  • Created and added a reflection generator script to my base tile prefab, which duplicates sprites and places them between the water and the background layer
  • Added a "ripple layer" above the reflections but below the water -- this layer renders using a grabpass to redraw everything beneath it with ripples

So, this ultimately met all of my requirements. It was not super painful to implement and it performs fairly well on mobile. The ripple layer is definitely the least performant aspect of the whole thing since it redraws everything behind it (all the reflections), but I've added graphics settings to remove the ripple effect or disable reflections altogether if there are any performance issues as a result of this change.

I've been experimenting with small changes to make the terrain look even more dynamic by making it ripple along with the reflections (as in the second screenshot at the top), but these effects tend to increase my draw call count a lot. I'm already drawing my tilemaps in Individual mode rather than Chunked (this is not something I really want to change at this point since a lot of things are decided by this one decision -- whether you split up your sprites, how "flying" objects are implemented, etc.), so my draw call count increases a lot if I duplicate terrain.

To reduce how much terrain I duplicate for this ripple effect and therefore reduce the draw calls I incur, I wrote a script to identify tiles with water beneath them (this was difficult to determine at the edges of the map, since this script needs to run BEFORE the water registers itself in Component.Start()).

Rippling Terrain Identifier

This reduced the draw calls to a pretty reasonable place -- on my biggest levels, I don't exceed 200 draw calls with all the bells and whistles enabled, and it runs fine on my (albeit pretty new) phone. Disabling the water ripple effect on lower tier hardware should prevent performance issues, though. EDIT: I tried to get this number even lower recently, but I don't think I will be able to cut it down without changing the draw mode of my tilemaps. Currently I have them drawn in Individual mode, which seems to greatly increase the draw call count in my case. But I may not be able to get rid of this requirement, mainly due to the way I'm drawing some world decor and water sprites currently. Need to look into this further.

All in all, I'm really happy with how the reflections turned out!

Get Flowa

Leave a comment

Log in with itch.io to leave a comment.