How I Made Pixel Art Water Trails - Godot Visual Shader

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
Hey everyone, a lot has happened since  my last video. Most notably I’ve been   porting the entire game over from Unity  to Godot - no prizes for guessing why. Anyway despite that, today I’d like  to show you how I created this water   trail effect and also how I made the sprite  gradually cut off as it goes into the water. Let’s start with the water trail.  Previously I just had these particles,   but I wanted to see if I could make something  a bit nicer. And it would also need to be   applicable to all the different creatures.  After a lot of experimenting I had this   idea to combine a Line trail with  a shader, and this is the result. To make a line in Godot you can simply use the  Line2D node and adjust the points. But in order   to make it act like a trail you need to add a  script that continuously update those points.  In the script there is a queue  to keep track of all the points,   and the current position will get added to that  queue every frame. If the queue reaches the max   length the excess points are then dequeued. Then  all that's left is to overwrite the Line2D’s   point array using the points from our queue. With that done, the line now looks like this…  I also adjusted the gradient and width curves so  that the line is wider and fades out towards the   tail end. Eventually there will be ripples  travelling along the direction of the fade,   so in anticipation of that I also created this  gradient texture in photoshop and assigned it   to the line. The texture kinda gives it that V  shape which the shader will make use of later.  One thing that immediately stuck out to  me was how the line collapses in a very   unnatural way. The tail end should not remain  that wide when the line becomes short. Instead,   it should collapse like this. So I added  a bit of logic in that script from earlier   to adjust the tail width based on how far away  it is from the player. Now it looks like this: Cool so now that we've got the trail, the  next step is to add in the ripples.The main   idea behind this shader is to take the red  channel of the texture and plug that into a   sine function. To make it move, we simply add the  current time to our input for the sine function.  It looks quite strange to  use this result by itself,   so instead we can remap and subtract it from the  original colour to make the effect more subtle.  Right now the colours are also quite blended  and fuzzy, which doesn’t really fit with my   pixel art style. So I decided to add another  section of logic to quantize the colour. This   was achieved by multiplying, flooring and  dividing the value by some number parameter.  It’s looking better but now we have to tackle the  problem of making it into pixel art. Unfortunately   because the uv’s of the Line2D node are  very stretched and distorted, we can’t   use the old trick of just quantizing them. In order to pixelate it without distortion,   the only way I could think of was to use a  SubViewport. This node will render its children   onto a texture which you can then use on a sprite.  For those of you that are familiar with Unity,   it’s sort of like Unity’s RenderTexture.  I really wanted to avoid using this due   to performance concerns, but I’m hoping by setting  the resolution of the texture to a very low number   it won’t have much impact. The low resolution  will also create the pixelised look we want!  Now another thing I wanted to do was  cover up the end under the player,   because I don’t like the way it disappears into  nothing. To do this I simply added this circular   sprite as a child of the SubViewport. Then it  kinda gets merged together. I put different   sized circles on each animal, and this ended up  creating some nice stationary ripples as a bonus!  Ok so now all that’s left to fix is  the trail being visible on land. To   address this I decided to slowly fade the  trail in and out depending on the depth.  This was easily done by adding a new brightness  parameter to the shader, and then adding some code   to update that parameter based on depth. Aand that’s the water trail complete! So the other thing I wanted to show was how I made  the sprite disappear into the water. Although it   might look slightly strange at times, what’s great  about this method is that it can be applied to   any creature. There’s no need to painstakingly  hand-draw a different animation for each one.  Basically if you move the sprite down and  mask off the bottom at the same time, it   kinda gives the illusion that it’s in the water. In Godot 4, creating a mask is really simple.   Sprite2D nodes have the option to clip all  children, which is exactly what we need. So   in Photoshop I drew up these mask shapes,  making them slightly different depending   on the creature’s imaginary contour  lines. I made sure to put a band of   semi-transparency in the middle to help soften  the line. This is what it looks like in game. Next we want the sprite to  move up and down automatically,   while the mask stays stationary. So  I wrote a lil function to override   the position of the body relative  to its parent (which is the mask).  First, an InverseLerp function is used to  remap the current water height to a percentage,   where 0% would be at the shore and  100% would be at some very deep point.  By the way if you are wondering where the current  height comes from, it comes from the heightmap   used to procedurally-generate the terrain. Next we scale and add any initial positioning   back. The scale should be set depending on  how much you want the sprite to move down.  Now sometimes we may not want our animals to  move down that much. For example if this chicken   moved down as much as the player, it would go  completely underwater. So to prevent this we   can clamp the value to a “max depth” amount. Then  that can be set individually for each creature.  That’s pretty much it for the code, all  that’s left is to apply the new position. As a finishing touch I also added  a small bobbing movement when the   creature reaches max depth. This was  done using a simple sine wave offset. Alright that’s all I wanted to show today, hope  you found it interesting or helpful! By the way,   if anyone has any other ideas on how  to implement a water trail like effect   for 2D games, I would really like to hear them.   In the meantime I’ll be continuing to  port everything over to Godot. See ya
Info
Channel: jess::codes
Views: 14,129
Rating: undefined out of 5
Keywords:
Id: W4eVR_Fm5Gs
Channel Id: undefined
Length: 6min 19sec (379 seconds)
Published: Tue Dec 05 2023
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.