Today I would like to analyze an interesting project. It mimics the effect of water fountain vividly. The player could drag the mouse to change the sprouting direction and height of water streams following music rhythm. Just take a look at the video.
The original project could be accessed at: https://scratch.mit.edu/projects/15337146
The code of the project looks super simple, but it is not easy to understand how the code realizes the amazing effect. There are three sprites in this project, but “Pond” and “Thumbnail” sprites just show the static costumes and are super straightforward, so I will not introduce them in this post. I will only focus on the key sprite “Fountain”.
When Start clicked
There are two parallel code segments of “when Start clicked”. The first one is used to check the behavior of mouse pressing down. Each time the mouse is pressed down, the program changes the value of count variable by 1. When count value > 3, set its value to 1 again.
The second code segment is used to create fountain clone continuously. When count value is equal to 1, only create one clone each time. If count = 2, create three clones. If count = 3, create five clones in one loop iteration.
Please also pay attention there is an “erase all” block at the end of the loop iteration. That means, when new clones are created, all the previous pen strokes will be removed. It is very important to remove the old pen strokes. I will explain it soon in the below sections.
In the above “when Start clicked” code segment, it calls the self defined block “clone(x)”. The definition of “clone (x)” is shown below. First, it moves the fountain sprite to a position (go to x: x, y: -130). Its x position is decided by the input parameter “x” which could be -100, -50, 0, 50, 100. Its y position is always -130. This position is where the water starts to squirt out.
The “x vel” and “y vel” define the step along x and y direction respectively. Take the example if “x vel”, the first part (mouse x – x position) * 0.08 means that each step will move 8 percent of the horizontal distance between the mouse and this clone. In another word. If the clone keeps moving at the current pace, it will take 1/0.08 = 12.5 times to move to the same horizontal position of the mouse.
The second part of equation uses a random value, so the movement path is not totally the same for different clones. It actually simulates the real water flowing effect. When water is squirted out, water drops move in the same direction roughly, but have minor difference in their individual movement path.
The mode variable is not implemented in this project, yet. Basically, different modes have minor difference in the value of “x vel”, but the real effect is similar.
At the end of this code segment, it creates a fountain clone.
When I start as a clone
Once the fountain clone is created in the above “clone (x)” method, the event “when I start as a clone” will be invoked. It first calls “pen down”, so all of the clone’s movement path will be recorded on the stage with pen size of 5 and pen color of purple (which is defined in “when Start clicked” segment).
The clone will move toward the current mouse position until it touches edge or its y position < -135. In horizontal direction, it will move with the step interval of “x vel”.
In vertical direction, “y vel” decreases by 1 in each iteration and becomes zero and then negative. When “y vel” is zero, this clone reaches the peak point and then later on, it will drop down until touching edge or falling down to the bottom.
The last block sets pen shades based on the y position. It generates an effect of the water steams scattering when they are squirted up.
Here, I would like to explain the purpose of “erase all” block in the above “when Start clicked” segment. After creating new batch of clones, all the previous pen strokes are removed by “erase all” block. However, some clones are still alive and the pen is still down. They will draw from their current position. When all the clones draw again from the current position, the overall water fountain forms. “erase all” is kind of like redrawing the whole picture. Since the computer runs very fast, we will not see the redrawing process, but a continuously flowing water fountain.
Till now, all the code of this project have been introduced, but I will explain another two questions, which confused me for a while, so I expect that you might have the same questions as me.
Why do the water streams always intersect to a point?
If you watch the above video, you will notice no matter there are 3 streams or 5 streams, all of them will intersect at one point and then scatter again to go to different routes.
This phenomenon is caused by the design of the clone’s movement. In the definition of “clone (x)” as shown below, “x position” and “y position” is local variables so they are special for each clone. Its horizontal step length is always (mouse x – x position) * 0.08 (random value is much smaller, so ignore it here). Although x position is different for each clone, the clones of the same batch will all move to the mouse x position after 13 loop iterations (1/0.08 = 12.5 -> round up to 13).
Since y position for all water streams is always -130 initially, “y vel” is same for each clone. Therefore, after around 13 loop iterations, the clones created in one batch will go to the same position vertically. Please note that because “y vel” decreases by 1 each time, the fountain clones could not reach the mouse y position. Its peak point is located below the mouse y position, as shown in the above diagram.
How could each clone follow different path?
Through the above analysis, it is clear that the continuous water steams actually consist of lots of fountain clones, but how could each clone follow different path?
To answer this question, we should understand the special functionality of local variable. When we create variables, a dialog will pop up asking if we want to create a variable. If we create a variable “for all the sprites”, all the sprites in this project could read/write this variable. Most of times, we will use this default option.
However, if we create a variable “for this sprite only”, it is only visible to this sprite. Moreover, this type of variable has a special capability! It will create separate copy for each cloned sprite. Therefore, if we use the local variables in “when I start as a clone”, it could keep different values for different clones.
In this project, “x vel” and “y vel” are local variables. After setting their initial values and then create clones, each clone keeps an independent copy of “x vel” and “y vel”. You could identify local variable by making them show on stage. If there is prefix of sprite name, it is a local variable, as shown in the below diagram.
Since the water drop clones are moving very fast, you might not be able to see the effect of local variable “x vel” and “y vel”. In the following video, you could understand better because I add wait() block in both “when Start clicked” and “when I start as a clone” code segments to slow down the motion.
That is all for the analysis of this project. The code is super concise, but not so easy to understand at first. However, once you know how the code works, you could apply this method to other scenarios to create fantastic drawing effect. Anyway, don’t forget to enjoy the coding and have fun!
Note: All the analysis articles are copyright products of http://www.thecodingfun.com. Anyone re-posting them should credit author and original source. Anyone using them for commercial purposes or translating them into other languages should notify TheCodingFun and get confirmation first. All Rights Reserved.