As I quickly learned, there are a ton of techniques and tools used for creating water in Unity. The Standard Assets package includes pretty much everything you need to get the basics done, but there’s loads of third party tools availble to extend that functionality. There’s tools like Flow and SmartWater, and there’s also lots of interesting optimizations, like creating custom water meshes. But, to my dismay, almost everything out there is designed for 3D environments.
Granted, it probably would have been possible to adapt some of these tools to achieve an acceptable 2D top-down water effect, but it would almost certainly involve code changes and creating a new shader or two. I was looking for a solution that would be simple to implement, well documented, and that didn’t involve adapting a lot of 3D objects or concepts. Here’s a summary of what I was looking for:
- Don’t need to interact with lighting
- Don’t need reflections
- Needs support for a transparency mask. Because we don’t have any 3D terrain in a true 2D game, we need a mask to define where the water is drawn, and at what opacity.
- Multi-directional and multi-speed flow map support (different areas of the same water texture can flow in different directions and at different speeds).
- A flow map generator/editor that can be used within Unity, so I don’t have to create them by hand in Photoshop
A few third party tools looked promising, like Flow, but they clearly designed for a 3D environment, and were a little lacking on documentation and examples. I wasn’t about to blow good money on something that I wasn’t even certain would support what I was trying to achieve. Since I was unable to find any sure-fire solutions on the asset store, I set out to create my own.
The first step was to create a shader that drew a water texture with a transparency mask. This was pretty simple. Basically, I drew my level’s water on a separate layer in Photoshop, saved it as a separate image, and used that image as input for my shader. You can see what I mean below.
Next, I modified my shader to use a bump map. This was also a pretty easy step. You can use some of Unity’s built in shaders as a reference, or check out this tutorial I used long ago. Ignore the fact that my shader has two slots for normal maps – we’ll get to that later.
Flow Map Vertex Painter
The next step would be to implement some sort of flow map editor. Flow maps can either be implemented as textures, or as I recently discovered, as mesh vertex colors. My semi-functional XNA solution used a texture, but in Unity, I opted to go with vertex colors. My original XNA solution, and subsequently my Unity solution were both based off of the ideas presented in this fantastic article. It’s a great read, so if you want some more information, definitely give it a look.
Here’s the basic idea:
- In a 2D world, water will flow along the x and y axes
- X and Y can be represented by red and green vertex colors respectively. The blue and alpha vertex colors can be any color – they will not be used.
Movement coordinates are in the interval [-1,1] but color values are in [0, 1]. Perform some simple math to convert between the two.
- // [0,1] to [-1,1]
- value = (value * 2) - 1;
- // [-1,1] to [0,1]
- value = (value / 2) + .5;
- When I am drawing on my mesh, if the mouse is dragged upwards, paint red 1. If it’s dragged down, paint 0. Do the same for dragging the mouse left and right in the green channel.
- Allow a brush strength to allow in-between speeds (not just moving or not moving)
The underlying concept itself is fairly simple. The vertex painter itself ended up being pretty difficult to pull off. I tried using an existing vertex painter on the asset store as an example (back when it was free), but my Lord it has a lot of issues. Creating a custom editor tool and creating a good custom editor tool are two very different things. Anyway, you can see the fruits of my labor below.
A Plane with More Vertices
A default plane really doesn’t have very many vertices. If you want to make your flow map more precise, you need to create a mesh with more verts. Luckily, a tool exists on the Unity community wiki to do just that.
Accounting for Uneven Skew
The last piece of the puzzle was to account for uneven skewing of our bump map. Normally, a bump map will be scrolled at an even speed across its mesh. In our case however, we scroll different sections of our bump map at different speeds (because we want portions of our water to move faster than others). This isn’t a big deal over a short period of time, but if we let the effect go on long enough, it becomes extremely obvious.
The solution is to use two bump map textures, and fade them both in and out over time, opposite of each other. When one image is fully opaque, the other is fully transparent. At the moment when one of our bump maps is fully transparent, we can reset it’s offset back to zero, allowing us to start over. The effect can be visualized below.
For a final touch, I added support for a ripple height map. Water ripples can be caused by rain or objects moving through the water. For this feature to work, you’ll need Unity pro (because it uses a RenderTexture). Here’s how it works:
- White ripples are generated using a particle system. They are drawn onto a RenderTexture.
- The ripples in the particle system expand outwards over time.
- The RenderTexture is passed into the water shader as a parameter, and the shader modifies the water based on the height (how much white there is at a particular pixel)
- Ripples can have various shapes. For instance, objects moving through water will generate a sort of “V” shape (think of the wake created by a speed boat). Raindrops however, will be a perfect circle (more of a donut shape actually).
That about wraps everything up. Overall, I’d have to say the hardest part was creating a decent vertex painter. There’s quite a few things to consider when you want to create a high quality custom editor in Unity. But now that I’ve gone through it once, it shouldn’t be so bad next time. In fact, I even created a generic vertex painter tool while I was at it.
Despite the fairly in depth write-up, I’m still debating whether or not to release all of the code for free. I may put it up on the Unity store for a few dollars. We’ll see. Anyway, I hope it was informative. Here’s a quick video demonstrating the end effect. Notice how the river splits and flows both southwest and southeast, all on the same plane. In this video, the raindrops are a little hard to see on the water, sorry about that.