How to Make Water with Dynamic Waves! - Godot Tutorial #1

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hello and welcome to the tutorial number one of our brand new godot tutorial series where we'll be learning how to make a fully responsive 2d water this system is made using basic physics and the concept could be applied to other engines or languages it has all sorts of parameters and you can customize it however you wish i also made a demo on edge you can use to see how each parameter influences the simulation all the files the source code and the link to the demo are available in the description now before implementing it i will first explain how everything works but if you really want to jump straight into implementation godot then go to this time in the video [Music] so first of all how does it work first imagine you have a simple spring which is being extended and held in a height position as you know springs have a natural position and the springs always tries to return to this position let's call it target's height to make things simpler let's call this distance x which is how much we extend the spring what dictates how our spring behaves is the hooke's law f is the force that makes the spring return to the natural position k is the stiffness constant of the spring and x is how much we have extended the spring by applying this force to our velocity vector on each frame we will have this result the spring always tries to return to the target height but since the spring overshoots its destination we end up with a wave to make the wave stop we'll add a loss vector to our wave which will be dependent on our spring velocity v and the spring factor d also known as damping [Music] by adding this loss to our hooke's law equation our wave will have this behavior and actually stop to make it look like water we need more than one spring at our current state they don't affect each other but we do need them to affect their neighbors for example if the spring number 3 moves by a large amount we need springs 2 and 4 to move by a fraction of that amount which in turn move the waves 1 and 5 by a fraction of that amount as well the result will be that every spring will pull each other and we will have a nice responsive wave effect this brings us to the third step which is to simply draw a polygon using those points making it a actual body of water this is looking pretty good already but we can do better by using the magic of splines we will make a nice smooth border around our water body and then make some particles to finish it so let's get into actually implementing it in godot in a brand new project create a new 2d node for the main scene we will call it main if you want to use the exact same settings as i am go to projects project settings and then under the window tab those are the settings for width height test width and test height i'm using i also set the stretch mode to 2d and the stretch aspect ratio to keep then create a new scene and make it a node to d this will be our spring scene which we will call water spring just so we see what we are doing attach a sprite node to the water spring and put the joint dot png image to the texture field save the water spring scene and then put a instance of it in the main scene so we can test it properly back to the water spring scene attach a new script to its root node first we will declare some variables we will need a velocity variable which we will set to 0 then a force variable also equals to 0. we will also need a height variable which is equal to our position dot y and also our target's height variable we'll set it to position dot y plus 80 for testing purposes lastly we will need our spring stiffness constant which we will set to 0.015 we will now create a function called water update which will be responsible for applying the hooke's equation into our spring in each frame we'll set the spring constant k as one of its arguments inside the function we update our height value to our currentposition.y since the position will move at each frame then we declare our spring extension which is the difference between height and target's height then we calculate the force applied to the spring using hooke's law which is minus the spring extension x multiplied by the spring constant k now we add the force to our velocity variable and finally increment our position dot y based on this velocity in a physics process function we simply call a water update function and if everything was made correctly we should have our water spring moving up and down now we will add loss to our system to make the wave stop we will declare one more variable which is our dampening value we'll then add the dampening as an argument to our water update function we will then declare our last variable which is equal to minus damping times velocity add the loss to our force and the wave should stop now our spring modeling is basically done [Music] now we will create a new scene which will be our actual body of water for now we'll manually put 5 water spring distances on it save a water body scene and put it in the main node attach a new script to the water body scene in the script we'll first declare the spring stiffness constant k and the dampening value d we will also declare a spread variable now we will declare our spring array making it empty in the ready function we'll loop through our children inside the loop we append the i variable to the springs array this will basically fill the array with the springs still inside the loop call this initialize function to each of our springs back to the spring script create the initialize function inside this function make height equals to position.y target height equals to position dot y as well and velocity equals to zero this will make sure all the springs are initialized correctly you can also set height and target heights both to zero and delete k and d also be sure to delete the physics process function of our spring back to the water body script we will make a physics process function to update our array of springs loop through the array using a for loop and call the water update functions declare two more arrays which we will call left deltas and right deltas the left delta's array will store the difference in height between each spring and its left neighbor the right delta array will store the difference in height between each spring and its right neighbor make a for loop with the size of our springs vector and in each iteration we will append a 0 to our left deltas and write delta's arrays now make a for loop with the size of our spring array we will make an if statement i greater than 0. inside of it we will calculate our left delta values as i said before left deltas store the difference in height between a spring and its left neighbor multiplied by a spread constant now we can use this value to increment the velocity of our left neighbor since we can't have arrays with indexes greater than the array size we'll check if i is less than spring size -1 as before the right deltas will be the difference in height between a spring and its right neighbor then use this value to increment the velocity of our right neighbor now we can finally make our wave move add a new function called splash it will receive the index of this spring we want to move and the speed value as its arguments make an if statement to make sure the index is inside the boundaries of the array if it is then get the spring with that index and then add velocity to it if we call this from the ready function like so we should see the springs move like a wave what we want is the whole body of water to move in the same frame we created the wave to make that we will declare another variable called passes which is how much this process is repeated every frame then get this whole for loop in which we calculated the wave spread and put everything inside another for loop with the size of our pestis variable now our wave spreads much faster go to your water body scene and delete the water spring instances in your water body script declare a variable called distance between springs also declare a spring number variable we will also reference our water spring scene now go to the for loop in our ready function make the for loop the size of our spring number and delete what is inside of the loop then declare a variable called x position this will be the position.x of each spring in the array we'll also make a variable called w which is an instance of our water spring scene add this w to the scene append the new instance to our spring array then initialize it again but this time put the x position as our argument now go to the water spring initialize function and then add the x position as one of its arguments and inside the function make the position dot x equals to x position [Music] now into your water body scene you can change the distance between springs variable and the spring number variable as you wish now we will draw our water body polygon go to the water body scene and add a child node to it make this child a polygon 2d and hit create we will call it water polygon in the water body script we'll need a depth value then a target height value which is equal to globalposition globalposition.y also a variable that will hold the bottom position of our water body lastly we'll make a reference to our water polygon node we'll create a new function called draw water body first declare an array called surface points this array will hold the positions of our springs then make a for loop with the size of our springs array and append the spring positions to the surface points [Music] then declare two more variables which will hold the first and last indexes of our surface points now declare a new water polygon points variable which will store all the points of the polygon and make it equal to the surface points to enclose our polygon we will need two more points at the bottom of our polygon first add the point to the very right and very bottom like so then add the point to the very left and very bottom as well we will convert our newly formed array to a pool vector2 array like so then we just set the polygon of our wire polygon node to this new variable with this done our water body should be formed this is how it looks for now a little too square for me but we will smooth it out soon if you change the color of our polygon via inspector it will look washed out just so we see how the transparency looks i made a little background and put it into the main scene it doesn't look right it needs more saturation in that case we use a simple shader to make it work to make the shader we'll instance a new scene into the main node it will be a color react node with this color rack selected go to the inspector and under the visibility tab select material and then select a new shader material under shader select new shader be sure to save your shader now click on the shader to open it first let's declare the shader type as canvas item now let's declare a uniform vector4 which we will call tint let's give it a hint color and let's make it equal to vector 1.0 we will blend this color to our background color so make it blue create a new fragment function inside the function make a new vac4 variable called color color will call a texture function with screen texture and screen uv as its arguments this will map the screen textures to the screen coordinates so if we assign the color like so it should go transparent now we will mix the transparent color to our tint color using the mix function the last argument is how much the second argument is mixed with the first one you can play with this value to make it more transparent or more opaque now we will make another mix we will mix a vac for 0.5 to our current color vect4 0.5 is a gray color which should not change our water tone by mixing it with a value greater than 1 we're essentially making the color of our screen stronger save your shader you can delete the color rect now click on your water polygon node now add the shader material as we did previously drag and drop our new shader into the shader tab so that's how it looks at the moment now let's make a nice smooth border after some research i found this page about drawing curves in 2d where a very nice person by the name of d-lean jeans made a curved spline 2 using path to denote we will be using this script to make our water smoother with our new script in hand go back to the water body scene and then make a new child look for a smooth path and hit create under canvas item visibility the self modulate is probably blue and transparent make sure it is plain white rename it and then enter the water body script we will declare a new reference and also a border thickness variable in the ready function set the border width to the thickness value now create a function called new border which will be called each frame declare a new curve variable that will hold a new curve 2d node and make a new surface points array then add those points to our curve with another loop then we just need to assign our new curve to the border curve call the smooth function inside our border and then call the update function of our border with that our water should have a nice smooth line if we look closely though we will see that even if our line is curved our polygon is not let's fix that go to your draw water body function and delete the surface points part make a reference to our curve and then a new points variable this variable is going to get the points of that curve and make an array of them change the location of your polygon points variable like so and make it equal to your new points variable then change where it says surface points to water polygon points and now the polygon should also follow the curve we're almost finished we just need a way to make our scenes interact with our water we will use a stone object to interact with our water object since this is not the focus of the video i'll just quickly go through it [Music] our stone is just a kinematic body 2d with a sprite attached to it and a collision shape to d with a rectangular shape this is the stun script which just makes the stone fall i also attached a script to the main scene it will spawn stones at my mouth position go to your water spring object and add an area to the node then add a collision shape 2d as a child with a rectangle shape 2d shape make a reference to the collision shape to d then make a new function which we will call set collision with he receives an argument called value inside this function make an extends variable which will hold the extents of our collision shape then declare a new extent value this will be a vector 2 variable in which x is the value divided by 2 and y is the extent dot y then set the extents of the collision to the new extent variable back to the water body script call this function from the loop in the ready function like so when testing it the collision of the springs should fill the whole water surface [Music] into your spring scene select your area to d and under the node tab select signals right click on the body entered signal and connect it to your spring this should detect when something hits a spring inside the spring script declare an index variable which will be set to 0. also make a motion factor variable this is how much an object affects the water lastly declare a new custom signal called splash on your area2d body enter function make a speed variable equal to body dot motion dot y multiplied by the motion factor this body dot motion dot y is the speed of your body in my stone code i called it motion then emit your splash signal and pass index and speed as its arguments under your water spring initialize function but the index as an argument as well add the same thing to the water body script and lastly connect our water spring splash signal to our splash function like so now our stone should be interacting with our water body it looks a little weird though since sometimes the stones collide with the water springs more than once there are a lot of ways to fix that such as using timers i'll simply not let the objects collide with the water more than once add a collider with variable to our water spring this variable will hold the last objects we collided with in the area2d body entered function make an if statement so if the body is the same of what we just collided then just return if not then assign the body to the collided with variable now it should work properly there are cases where you want something to happen to the player in the case he enters the water like reduced gravity or state changing [Music] go to your water body scene and then create a new area 2d node with a collision shape 2d make references to both of them in the water body script in your ready function declare a new variable which represents the water length declare a new rectangle variable which will hold a new rectangle 2d node then declare a rectangle position variable this will be in the very center of our area the rectangle extents are the same value then assign the position and extent to our area and collision shapes now we have an area 2d which covers all of our water body if you select your area go to nodes signals and then connect your body entered signal to your script you'll be able to detect when something enters the water with that in mind i'll leave the particle creation as a homework of course the complete project will be made available to you at this point feel free to play with your water to see what results you get one more thing before we finish if you make your distance between springs too small you may see some polygon flickering to fix that go to your water body scene and click on your border there you should see a variable called spline length if the distance between springs is too small make sure to make this smaller as well i really do hope you enjoyed this video making this tutorial took me longer than expected so i could not make a dev vlog this week it should be going back to normal soon though if you like this kind of tutorial videos don't forget to like it so i know i should be making more that's all from me and cheers
Info
Channel: Pixelipy
Views: 11,391
Rating: undefined out of 5
Keywords: Godot Tutorial, Game development, Pixelipy, Tutorial Godot, Godot Tutorials, Water in godot
Id: RXIRkou021U
Channel Id: undefined
Length: 25min 52sec (1552 seconds)
Published: Tue Jun 01 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.