From Test-Scratch-Wiki
Rope Physics are the physics of a real rope, or the way one acts. It is very difficult to accomplish in Scratch, but it is possible.
Spring Based Ropes
Ropes can be thought of as a series of springs, with each point trying to get to each other. It does not allow for swinging, but it does allow for a simple representation of ropes.
![]() | The ideas expressed here can also be used to make a spring instead of a rope. |
Variables and Lists
First, each rope joint is going to need two velocity variables to keep track of its speed:
- X Velocity
- Y Velocity
As well as two it's mass and a few properties:
- Mass
- Dampening
- Stiffness
- Gravity
Stiffness makes the rope shorter and stiffer. Dampening makes forces travel more slowly.
For computational reasons, a few more variables are needed:
- Force X
- Force Y
- True Force X
- True Force Y
- # of Rope Joints Made
- Joint ID
As well as a list to keep track of spring joints:
- Rope Joints
Some Background
In a spring, the end of the spring is always trying to get to the top of the spring. It can not though because of gravity or because it's running into another section of spring:
Here, the top of the spring will be at (x1, y1) with the bottom being at (x2, y2). Since the bottom is trying to get to the top, the force that the bottom of the spring experiences becomes:
Force along the x-axis = x2-x1
Force along the y-axis = y2-y1
Since object's do not automatically go where they want to, some stiffness is at play:
Force X = (x2-x1) * stiffness
Force Y = (y2-y1) * stiffness
Since energy needs to be distributed over an object's mass, that needs to be factored in:
Force X = ((x2-x1) * stiffness) / mass
Force Y = ((y2-y1) * stiffness) / mass
That then is added to the bottom spring's velocity. The last factor that needs to be added is friction, which is done by:
X Velocity = (X Velocity + Force X) * dampening
Y Velocity = (Y Velocity + Force Y) * dampening
Coding
A two-jointed roped will be created connected to the mouse. To start, one needs a cloning base:
當 @greenflag 被點擊 隱藏 重複 (2) 次 分身 [自己 v] 建立 end 當分身產生 . . . // The physics will go here, as well as rendering.
Next, the ropes need to be rendered and they need to add themselves to Rope Joints so that other clones can look at their position:
當 @greenflag 被點擊 變數 [# of Rope Joints Made v] 設為 (1) 隱藏 重複 (2) 次 分身 [自己 v] 建立 end 重複 (6) 次 新增項目 (0) \( [Rope Joints v] \) end 重複無限次 替換第 [1 v] 項於 [Rope Joints v] 成 (滑鼠游標的 x) // This is how the rope will look at the mouse's position 替換第 ((0) + (2)) 項於 [Rope Joints v] 成 (滑鼠游標的 y) 筆跡清除 // Clears the screen of old rope drawings end 當分身產生 變數 [Rope ID v] 設為 (# of Rope Joints Made) 變數 [# of Rope Joints Made v] 改變 (2) // So that we can figure out which rope joint the clone is 重複無限次 render :: custom 替換第 ((Rope ID) + (2)) 項於 [Rope Joints v] 成 (x 座標) // So that other joints can look at this joint's position 替換第 ((Rope ID) + (3)) 項於 [Rope Joints v] 成 (y 座標) end 定義 render 定位到 x: (清單第 (Rope ID) 項項目\( [Rope Joints v] \) :: list) y: (清單第 ((Rope ID) + (1)) 項項目\( [Rope Joints v] \) :: list) 下筆 定位到 x: (清單第 ((Rope ID) + (2)) 項項目\( [Rope Joints v] \) :: list) y: (清單第 ((Rope ID) + (3)) 項項目\( [Rope Joints v] \) :: list) 停筆
Here's when the physics are added:
當分身產生 變數 [Rope ID v] 設為 (# of Rope Joints Made) 變數 [# of Rope Joints Made v] 改變 (2) 重複無限次 physics :: custom render :: custom 替換第 ((Rope ID) + (2)) 項於 [Rope Joints v] 成 (x 座標) // So that other joints can look at this joint's position 替換第 ((Rope ID) + (3)) 項於 [Rope Joints v] 成 (y 座標) end 定義 render 定位到 x: (清單第 (Rope ID) 項項目\( [Rope Joints v] \) :: list) y: (清單第 ((Rope ID) + (1)) 項項目\( [Rope Joints v] \) :: list) 下筆 定位到 x: (清單第 ((Rope ID) + (2)) 項項目\( [Rope Joints v] \) :: list) y: (清單第 ((Rope ID) + (3)) 項項目\( [Rope Joints v] \) :: list) 停筆 定義 physics 變數 [Force X v] 設為 (((清單第 (Rope ID) 項項目\( [Rope Joints v] \) :: list) - (x座標)) * (Stiffness)) 變數 [True Force X v] 設為 ((Force X) / (Mass)) // Distributing force over the mass 變數 [X Velocity v] 設為 ((Dampening) * ((X Velocity) + (True Force X))) // Friction 變數 [Force Y v] 設為 (((清單第 (Rope ID) 項項目\( [Rope Joints v] \) :: list) - (y 座標)) * (Stiffness)) 變數 [Force Y v] 改變 (Gravity) // Make sure gravity is negative 變數 [True Force Y v] 設為 ((Force Y) / (Mass)) 變數 [Y Velocity v] 設為 ((Dampening) * ((Y Velocity) + (True Force Y))) x 改變 (X Velocity) y 改變 (Y Velocity)
Final Product
This should be the code to make a two-jointed rope that's connected to the mouse:
當 @greenflag 被點擊 變數 [# of Rope Joints Made v] 設為 (1) 隱藏 重複 (2) 次 分身 [自己 v] 建立 end 重複 (6) 次 新增項目 (0) \( [Rope Joints v] \) end 重複無限次 替換第 [1 v] 項於 [Rope Joints v] 成 (滑鼠游標的 x) // This is how the rope will look at the mouse's position 替換第 ((0) + (2)) 項於 [Rope Joints v] 成 (滑鼠游標的 y) 筆跡清除 end 當分身產生 變數 [Rope ID v] 設為 (# of Rope Joints Made) 變數 [# of Rope Joints Made v] 改變 (2) 重複無限次 physics :: custom render :: custom 替換第 ((Rope ID) + (2)) 項於 [Rope Joints v] 成 (x 座標) // So that other joints can look at this joint's position 替換第 ((Rope ID) + (3)) 項於 [Rope Joints v] 成 (y 座標) end 定義 render 定位到 x: (清單第 (Rope ID) 項項目\( [Rope Joints v] \) :: list) y: (清單第 ((Rope ID) + (1)) 項項目\( [Rope Joints v] \) :: list) 下筆 定位到 x: (清單第 ((Rope ID) + (2)) 項項目\( [Rope Joints v] \) :: list) y: (清單第 ((Rope ID) + (3)) 項項目\( [Rope Joints v] \) :: list) 停筆 定義 physics 變數 [Force X v] 設為 (((清單第 (Rope ID) 項項目\( [Rope Joints v] \) :: list) - (x座標)) * (Stiffness)) 變數 [True Force X v] 設為 ((Force X) / (Mass)) 變數 [X Velocity v] 設為 ((Dampening) * ((X Velocity) + (True Force X))) 變數 [Force Y v] 設為 (((清單第 (Rope ID) 項項目\( [Rope Joints v] \) :: list) - (y 座標)) * (Stiffness)) 變數 [Force Y v] 改變 (Gravity) 變數 [True Force Y v] 設為 ((Force Y) / (Mass)) 變數 [Y Velocity v] 設為 ((Dampening) * ((Y Velocity) + (True Force Y))) x 改變 (X Velocity) y 改變 (Y Velocity)
Pendulum-Spring Based Ropes
Note: This tutorial uses (distance) as a variable name, so whenever the variable (distance) is used, it will appear purple. Do not confuse it with the LEGO WeDo block version of (distance).
Variables and Lists
First, each rope joint is going to need two velocity variables to keep track of its speed:
- X Velocity
- Y Velocity
Next, a list will be needed to keep track of every rope:
- Rope Joints
As well as two variables so that ropes can figure out where they are in order:
- Rope Joints Made
- Joint ID
And the length of each rope segment:
- Segment Length
Another list will be needed to keep track of the quality of ropes being created:
- ChainCreateList
As well as a list to keep track of deleted rope joints:
- Removed Rope Joints
And a variable is needed to keep track of if a rope joint is at the end of a rope or not:
- Joint Type
And the gravity:
- Gravity
As well as several computational variables:
- Distance
- Difference in X
- Difference in Y
- Rope Tension
- Force X
- Force Y
- i
- Joint Point ID 2
And two variables for the player's sprite's velocity:
- Player X Velocity
- Player Y Velocity
Some Background
In this rope simulation, the spring, unlike with a Spring-based rope, heads for a minimum distance away from its swing point. This leads to some pendulum-like characteristics:
The top will be labeled x1, and y1. The bottom (and free part) of the spring will be labeled x2, and y2. A few variables need to be initiated first:
Difference in X = x2 - x1
Difference in Y = y2 - y1
Distance = √((Difference in X)^2 + (Difference in Y)^2)
Then, an intermediate variable is used to find the ratio of the force to the rope length:
Rope Tension = (((Distance) - (Segment Length))/(Distance))
Which is then backtracked to find the individual forces themselves:
ForceX = ((-1) * ((Rope Tension) * (Difference in X)))
ForceY = ((-1) * ((Rope Tension) * (Difference in Y)))
Coding
ChainCreateList
For the purpose of the code, there shall be three types of rope joints:
- 1 - A rope joint with rope joint above and below.
- 0 - A rope joint that attaches to an object. For this tutorial, it will be the player sprite.
- -1 - A rope joint that only attaches to one other rope joint.
An example rope might be constructed from the following rope joints:
1, 1, 1, 0
This rope is four lengths long, and the player is attached at the end.
Meanwhile, ChainCreateList will be styled in this order:
- x座標 of Rope Joint 1
- y 座標 of Rope Joint 1
- Type of Rope Joint 1
- x座標 of Rope Joint 2
- y 座標 of Rope Joint 2
- Type of Rope Joint 2
- Etc.
Rope Creation (Custom Blocks)
To create a rope, two custom blocks will be used. One custom block records the information for each rope joint being created, and the other custom block actually creates the rope. The blocks are shown below:
定義 Add Rope Joint at x: (xPos) y: (yPos) Type: (Type) //Run without screen refresh! 新增項目 (xPos) \( [ChainCreateList v] \) // xPos is the xPosition of the rope joint being created. 新增項目 (yPos) \( [ChainCreateList v] \) // yPos is the yPosition of the rope joint. 新增項目 (Type) \( [ChainCreateList v] \) // Type is the type of the rope joint. 定義 Initiate Rope Length #: (Rope Number) //Run without screen refresh! 重複 (Rope Number) 次 分身 [Ropes v] 建立 // Ropes is the sprite that will render the ropes. end
Creating Rope Joints
To make things easy to handle, each rope joint will be a clone of Ropes. The following scripts should be added to Ropes:
當 @greenflag 被點擊 筆跡顏色設為 [#C26B08] // Ropes will be drawn in this color. 變數 [Rope Joints Made v] 設為 (1) 變數 [Joint Type v] 設為 (-2) // This is here to help tell the difference between a clone and the sprite. 隱藏 當分身產生 新增項目 (清單第 [1 v] 項項目\( [ChainCreateList v] \) :: list) \( [Rope Joints v] \) 新增項目 (清單第 ((0) + (2)) 項項目\( [ChainCreateList v] \) :: list) \( [Rope Joints v] \) 變數 [Joint Type v] 設為 (清單第 ((0) + (3)) 項項目\( [ChainCreateList v] \) :: list) 如果 <(0) < (Joint Type)> 那麼 新增項目 (Joint Type) \( [Rope Joints v] \) 新增項目 (1) \( [Rope Joints v] \) . . . // If the Joint Type is 0 or -1, then two rope joints will be made. . . . // This is the first rope joint. It isn't the end of the rope, so its type is 1. . . . // The second rope joint though is the end of the rope. . . . // Remember, each clone draws a line between joints. . . . // So if there are 5 rope joints, only 4 clones are needed. . . . // Because of that, the last rope clone add two rope joints to (Rope Joints) instead of one. end 變數 [X Velocity v] 設為 (0) // Making the rope calm. 變數 [Y Velocity v] 設為 (0) . . . // The next block will figure out where the clone is in the list (Rope Joints). 變數 [Joint ID v] 設為 (Rope Joints Made) 變數 [Rope Joints Made v] 改變 (3) . . . // The next if block creates a second rope joint. . . . // Look at the large comment chain above for more information. 如果 <(Joint Type) < (1)> 那麼 新增項目 (清單第 [1 v] 項項目\( [ChainCreateList v] \) :: list) \( [Rope Joints v] \) 新增項目 ((清單第 ((0) + (2)) 項項目\( [ChainCreateList v] \) :: list) - (Segment Length)) \( [Rope Joints v] \) 新增項目 (Joint Type) \( [Rope Joints v] \) 變數 [Rope Joints Made v] 改變 (3) end . . . // The next three blocks delete the rope from (ChainCreateList) since it is already created. 刪除第 [1 v] 項 \( [ChainCreateList v] \) 刪除第 [1 v] 項 \( [ChainCreateList v] \) 刪除第 [1 v] 項 \( [ChainCreateList v] \) 等待 (0) 秒 重複無限次 . . . // Rope Physics go in here. end
The code inside of the forever block is actually surprisingly simple. All it consist of are two custom blocks and two 清单类积木 to update (Rope Joints) with the position of the rope joint. This is what the code looks like with forever block filled in:
當 @greenflag 被點擊 // Ropes will be drawn in this color. 筆跡顏色設為 [#C26B08] 變數 [Rope Joints Made v] 設為 (1) 變數 [Joint Type v] 設為 (-2) // This is here to help tell the difference between a clone and the sprite. 隱藏 當分身產生 新增項目 (清單第 [1 v] 項項目\( [ChainCreateList v] \) :: list) \( [Rope Joints v] \) 新增項目 (清單第 ((0) + (2)) 項項目\( [ChainCreateList v] \) :: list) \( [Rope Joints v] \) 變數 [Joint Type v] 設為 (清單第 ((0) + (3)) 項項目\( [ChainCreateList v] \) :: list) 如果 <(0) < (Joint Type)> 那麼 新增項目 (Joint Type) \( [Rope Joints v] \) 新增項目 (1) \( [Rope Joints v] \) end 變數 [X Velocity v] 設為 (0) // Making the rope calm. 變數 [Y Velocity v] 設為 (0) 變數 [Joint ID v] 設為 (Rope Joints Made) 變數 [Rope Joints Made v] 改變 (3) 如果 <(Joint Type) < (1)> 那麼 新增項目 (清單第 [1 v] 項項目\( [ChainCreateList v] \) :: list) \( [Rope Joints v] \) 新增項目 ((清單第 ((0) + (2)) 項項目\( [ChainCreateList v] \) :: list) - (Segment Length)) \( [Rope Joints v] \) 新增項目 (Joint Type) \( [Rope Joints v] \) 變數 [Rope Joints Made v] 改變 (3) end 刪除第 [1 v] 項 \( [ChainCreateList v] \) 刪除第 [1 v] 項 \( [ChainCreateList v] \) 刪除第 [1 v] 項 \( [ChainCreateList v] \) 等待 (0) 秒 重複無限次 Apply Chain Physics of x: (清單第 ((Joint ID) + (3)) 項項目\( [Rope Joints v] \) :: list) y: (清單第 ((Joint ID) + (4)) 項項目\( [Rope Joints v] \) :: list) :: custom . . . // The next two blocks update (Rope Joints) with the new position of the rope joint. 替換第 ((Joint ID) + (3)) 項於 [Rope Joints v] 成 (x 座標) 替換第 ((Joint ID) + (4)) 項於 [Rope Joints v] 成 (y 座標) Draw Rope Segment original: (x座標) (y 座標) :: custom end 定義 Apply Chain Physics of x: (xPosition) y: (yPosition) . . . // To be implemented in the section called "Adding Physics to the Rope Joints" 定義 Draw Rope Segment original: (xPosition) (yPosition) . . . // To be implemented in the section called "Rendering a Rope"
Adding Physics to the Rope Joints
The first half of the physics is just like the section above called "Some Background"
定義 Apply Chain Physics of x: (xPos) y: (yPos) //Run without screen refresh! . . . // Look above at the section "Some Background" for an explanation of the physics. 如果 <(Joint Type) = (0)> 那麼 變數 [TempX v] 設為 ((清單第 (Joint ID) 項項目\( [Rope Joints v] \) :: list) - (xPosition)) 變數 [TempY v] 設為 ((清單第 ((Joint ID) + (1)) 項項目\( [Rope Joints v] \) :: list) - (yPosition)) 變數 [TempX v] 設為 ((清單第 (Joint ID) 項項目\( [Rope Joints v] \) :: list) - ([x 座標 v]\([Player Sprite v]\)) 變數 [TempY v] 設為 ((清單第 ((Joint ID) + (1)) 項項目\( [Rope Joints v] \) :: list) - ([y 座標 v]\([Player Sprite v]\))) end 變數 [Distance v] 設為 (([sqrt v] 數值(((TempX) * (TempX)) + ((TempY) * (TempY)))) 如果 <(Segment Length) < (Distance)> 那麼 變數 [Rope Tension v] 設為 (((Distance) - (Segment Length)) / (Distance)) 變數 [ForceX v] 設為 ((Rope Tension) * (TempX)) 變數 [ForceY v] 設為 ((Rope Tension) * (TempY)) 變數 [ForceX v] 設為 (0) 變數 [ForceY v] 設為 (-1) // Applying gravity. end 如果 <(Joint Type) = (0)> 那麼 . . . // Since rope joints of type 0 are attached to the player, it makes sense to apply forces to the player. . . . // That's what the three blocks below do. 定位到 x: ([x座標 v] \( [Player Sprite v] \)) y: ([y 座標 v] \( [Player Sprite v] \)) 變數 [Player X Velocity v] 改變 (ForceX) 變數 [Player Y Velocity v] 改變 ((ForceY) - (3)) end . . . // More code to be added here. 如果 <<(Joint Type) = (0)> 不成立> 那麼 變數 [X Velocity v] 設為 (((X Velocity) + (ForceX)) * (0.65)) 變數 [Y Velocity v] 設為 ((((Y Velocity) + (ForceY)) * (0.61)) - (3)) x 改變 (X Velocity) y 改變 (Y Velocity) end
The problem with this model of physics is that if an entire rope was completely still, but it's end was twitching around frantically, then the only part of the rope affected would be the end. In reality, some of the twitching end's force would travel up the rope. To combat this, the script will reapply the physics, but instead of having a rope joint try to get to the one above it, it will try to get to the one below it. The reason why this works is because gravity always keeps a rope joint just below the threshold of a segment length.
定義 Apply Chain Physics of x: (xPos) y: (yPos) //Run without screen refresh! 如果 <(Joint Type) = (0)> 那麼 變數 [TempX v] 設為 ((清單第 (Joint ID) 項項目\( [Rope Joints v] \) :: list) - (xPosition)) 變數 [TempY v] 設為 ((清單第 ((Joint ID) + (1)) 項項目\( [Rope Joints v] \) :: list) - (yPosition)) 變數 [TempX v] 設為 ((清單第 (Joint ID) 項項目\( [Rope Joints v] \) :: list) - ([x 座標 v] \( [Player Sprite v] \))) 變數 [TempY v] 設為 ((清單第 ((Joint ID) + (1)) 項項目\( [Rope Joints v] \) :: list) - ([y 座標 v] \( [Player Sprite v] \))) end 變數 [Distance v] 設為 ([sqrt v] 數值 (((TempX) * (TempX)) + ((TempY) * (TempY)))) 如果 <(Segment Length) < (Distance)> 那麼 變數 [Rope Tension v] 設為 (((Distance) - (Segment Length)) / (Distance)) 變數 [ForceX v] 設為 ((Rope Tension) * (TempX)) 變數 [ForceY v] 設為 ((Rope Tension) * (TempY)) 變數 [ForceX v] 設為 (0) 變數 [ForceY v] 設為 (-1) // Applying gravity. end 如果 <(Joint Type) = (0)> 那麼 定位到 x: ([x 座標 v] \( [Player Sprite v] \)) y: ([y 座標 v] \( [Player Sprite v] \)) 變數 [Player X Velocity v] 改變 (ForceX) 變數 [Player Y Velocity v] 改變 ((ForceY) - (3)) end 如果 <(Joint Type) > (0)> 那麼 變數 [TempX v] 設為 ((清單第 ((Joint ID) + (6)) 項項目\( [Rope Joints v] \) :: list) - (xPosition)) 變數 [TempY v] 設為 ((清單第 ((Joint ID) + (7)) 項項目\( [Rope Joints v] \) :: list) - (yPosition)) 變數 [Distance v] 設為 (([sqrt v] 數值 (((TempX) * (TempX)) + ((TempY) * (TempY))))) 如果 <(Segment Length) < (Distance)> 那麼 變數 [Rope Tension v] 設為 (((Distance) - (Segment Length)) / (Distance)) 變數 [ForceX v] 設為 (((Rope Tension) * (TempX)) * (0.6)) 變數 [ForceY v] 設為 (((Rope Tension) * (TempY)) * (0.5)) end end // New Content 如果 <<(Joint Type) = (0)> 不成立> 那麼 變數 [X Velocity v] 設為 (((X Velocity) + (ForceX)) * (0.65)) 變數 [Y Velocity v] 設為 ((((Y Velocity) + (ForceY)) * (0.61)) - (3)) x 改變 (X Velocity) y 改變 (Y Velocity) end
Rendering a Rope
Rendering a rope is pretty simple. It draws a line between two rope joints and adds a dot at each rope joint to look like a knot.
定義 Draw Rope Segment original: (xPosition) (yPosition) 筆跡寬度設為 (5) 下筆 x 改變 (0.4) // This block makes sure that something's drawn. 筆跡寬度設為 (3) 定位到 x: (清單第 (Joint ID) 項項目\( [Rope Joints v] \) :: list) y: (清單第 ((Joint ID) + (1)) 項項目\( [Rope Joints v] \) :: list) . . . // Since the pen is down, simply going from one rope joint to another makes a line. 停筆 // This is here to make sure no extra pen marks are made. 定位到 x: (xPosition) y: (yPosition) // Return to the original clone's position.
Deleting a Rope
Deleting a rope requires three scripts. The first two scripts remove all deleted rope data from the list (Rope Joints), and the other script is for deleting those rope joint clones, and if needed, altering other rope joint's data. The first two scripts are shown below.
The custom block's variable (Joint Point ID) should be a multiple of three and will not take any other values.
Also, remember that the list Removed Rope Joints is used to keep track of the (Clone ID)'s of the rope joints deleted.
定義 Destroy Rope containing Rope Joint: (Joint Point ID) //Run without screen refresh! 變數 [i v] 設為 (0) 重複直到 <(清單第 (Joint Point ID) 項項目\( [Rope Joints v] \) :: list) < (1)> . . . // This repeat deletes all rope joints ahead of (Joint Point ID) until it meets the end of the rope. Destroy Rope Joint at: (Joint Point ID) 新增項目 (((Joint Point ID) + ((i) * (3))) - (2)) \( [Removed Rope Joints v] \) 變數 [i v] 改變 (1) // Delete next rope joint. end 新增項目 (((Joint Point ID) + ((i) * (3))) - (2)) \( [Removed Rope Joints v] \) 變數 [Joint Point ID 2 v] 設為 ((Joint Point ID) - (3)) 重複直到 <<(Joint Point ID 2) < (3)> 或 <(item (Joint Point ID 2) of [Rope Joints v] :: list) < (1)>> . . . // This repeat deletes all rope joints behind (Joint Point ID). Destroy Rope Joint at: (Joint Point ID 2) 新增項目 ((Joint Point ID 2) - (2)) \( [Removed Rope Joints v] \) 變數 [Joint Point ID 2 v] 改變 (-3) // Delete next rope joint. end 變數 [Joint Point ID v] 改變 (1) . . . // The next block deletes the tail of the rope. Destroy Rope Joint at: (Joint Point ID 2) 廣播訊息 [Destroy Ropes v] 定義 Destroy Rope Joint at: (Point ID) //Run without screen refresh! 刪除第 (Point ID) 項 \( [Rope Joints v] \) // This deletes the first data bit of a rope joint. 刪除第 (Point ID) 項 \( [Rope Joints v] \) // This deletes the second data bit of a rope joint. 刪除第 (Point ID) 項 \( [Rope Joints v] \) // This deletes the third data bit of a rope joint, finishing the process.
The third script, as said above, just deletes the deleted rope joint clones, and alters other rope joint clone's data so that they point towards the right spot in (Rope Joints).
當收到訊息 [Destroy Ropes v] 如果 <清單 [Removed Rope Joints v] 包含 (Joint ID) ?> 那麼 . . . // This checks if the rope joint was deleted, and if it was, it completes the process. 分身刪除 end 如果 <(Joint ID) > (清單第 [1 v] 項項目\( [Removed Rope Joints v] \) :: list)> 那麼 . . . // This checks if the rope joint is in a higher spot in the list (Rope Joints) then the remove joints. . . . // If that is true, then the rope joints needed 變數 [Joint ID v] 改變 ((-3) * (清單 [Removed Rope Joints v] 的項目數 :: list)) end 如果 <(Joint Type) = (-2)> 那麼 . . . // If (Joint Type) equals -2, then this is the original Rope sprite. . . . // All it does it decrease (Rope Joints Made) by the amount of rope joints deleted . . . // It also resets the list (Removed Rope Joints). 變數 [Rope Joints Made v] 改變 ((-3) * (清單 [Removed Rope Joints v] 的項目數 :: list)) 等待 (0) 秒 刪除第 [全部 v] 項 \( [Removed Rope Joints v] \) end
Miscellaneous
To delete all ropes, the next script is needed in Ropes:
當收到訊息 [Rope Clear v] 如果 <(Joint Type) = (-2)> 那麼 刪除第 [全部 v] 項 \( [Rope Joints v] \) 變數 [Rope Joints Made v] 設為 (1) end 分身刪除
To test if code was copied correctly, it would be helpful to make a rope. Below is some code to make a wavering rope with a player attached to the end. Note, it needs a variable called (X Acceleration) and another variable called (j).
當 @greenflag 被點擊 變數 [Segment Length v] 設為 (25) 刪除第 [全部 v] 項 \( [Rope Joints v] \) // Getting ready to make a rope. Add Rope Joint at x: (0) y: (80) Type: (1) :: custom Add Rope Joint at x: (0) y: (55) Type: (1) :: custom Add Rope Joint at x: (0) y: (30) Type: (1) :: custom Add Rope Joint at x: (0) y: (5) Type: (0) :: custom Initiate Rope Length #: (4) :: custom 廣播訊息 [Rope Waver v] 當收到訊息 [Rope Waver v] 等待 (0.1) 秒 變數 [Player X Velocity v] 設為 (0) 變數 [Y Velocity v] 設為 (0) 如果 <(Chain Type) = (0)> 那麼 變數 [X Acceleration v] 設為 (-2.2) 重複無限次 變數 [j v] 設為 (0) 重複直到 <(j) = (40)> 變數 [j v] 改變 (1) 變數 [Player X Velocity v] 改變 (X Acceleration) 變數 [X Acceleration v] 改變 (0.11) // Pushing the player slightly right. end 變數 [j v] 設為 (0) 重複直到 <(j) = (40)> 變數 [j v] 改變 (1) 變數 [Player X Velocity v] 改變 (X Acceleration) 變數 [X Acceleration v] 改變 (-0.11) // Pushing the player slightly left. end end end 定義 Add Rope Joint at x: (xPos) y: (yPos) Type: (Type) . . . // This blocks definition is above. 定義 Initiate Rope Length #: (Rope Number) . . . // This blocks definition is above.
The Player Sprite
The player sprite, like in a regular platformer, needs the following scripts run constantly:
- Gravity
- A script to change x by (Player X Velocity)
- A script to change y by (Player Y Velocity)
- A script to decrease (Player X Velocity) because of friction. Multiplying (Player X Velocity) by 0.65 is good.
- A script to decrease (Player Y Velocity) because of friction. Multiplying (Player Y Velocity) by 0.65 is good.
Final Product
Ignoring the player sprite and the test code, all of the required code is shown below.
The next two scripts can be put in any sprite:
定義 Add Rope Joint at x: (xPos) y: (yPos) Type: (Type) //Run without screen refresh! 新增項目 (xPos) \( [ChainCreateList v] \) // xPos is the xPosition of the rope joint being created. 新增項目 (yPos) \( [ChainCreateList v] \) // yPos is the yPosition of the rope joint. 新增項目 (Type) \( [ChainCreateList v] \) // Type is the type of the rope joint. 定義 Initiate Rope Length #: (Rope Number) //Run without screen refresh! 重複 (Rope Number) 次 分身 [Ropes v] 建立 // Ropes is the sprite that will render the ropes. end
The next series of scripts need to be in the Ropes sprite:
當 @greenflag 被點擊 // Ropes will be drawn in this color. 筆跡顏色設為 [#C26B08] 變數 [Rope Joints Made v] 設為 (1) 變數 [Joint Type v] 設為 (-2) // This is here to help tell the difference between a clone and the sprite. 隱藏 當分身產生 新增項目 (清單第 [1 v] 項項目\( [ChainCreateList v] \) :: list) \( [Rope Joints v] \) 新增項目 (清單第 ((0) + (2)) 項項目\( [ChainCreateList v] \) :: list) \( [Rope Joints v] \) 變數 [Joint Type v] 設為 (清單第 ((0) + (3)) 項項目\( [ChainCreateList v] \) :: list) 如果 <(0) < (Joint Type)> 那麼 新增項目 (Joint Type) \( [Rope Joints v] \) 新增項目 (1) \( [Rope Joints v] \) end 變數 [X Velocity v] 設為 (0) // Making the rope calm. 變數 [Y Velocity v] 設為 (0) 變數 [Joint ID v] 設為 (Rope Joints Made) 變數 [Rope Joints Made v] 改變 (3) 如果 <(Joint Type) < (1)> 那麼 新增項目 (清單第 [1 v] 項項目\( [ChainCreateList v] \) :: list) \( [Rope Joints v] \) 新增項目 ((item ((0) + (2)) of [ChainCreateList v] :: list) - (Segment Length)) \( [Rope Joints v] \) 新增項目 (Joint Type) \( [Rope Joints v] \) 變數 [Rope Joints Made v] 改變 (3) end 刪除第 [1 v] 項 \( [ChainCreateList v] \) 刪除第 [1 v] 項 \( [ChainCreateList v] \) 刪除第 [1 v] 項 \( [ChainCreateList v] \) 等待 (0) 秒 重複無限次 Apply Chain Physics of x: (清單第 ((Joint ID) + (3)) 項項目\( [Rope Joints v] \) :: list) y: (清單第 ((Joint ID) + (4)) 項項目\( [Rope Joints v] \) :: list) 替換第 ((Joint ID) + (3)) 項於 [Rope Joints v] 成 (x 座標) 替換第 ((Joint ID) + (4)) 項於 [Rope Joints v] 成 (y 座標) Draw Rope Segment original: (x座標) (y 座標) end 定義 Apply Chain Physics of x: (xPos) y: (yPos) //Run without screen refresh! 如果 <(Joint Type) = (0)> 那麼 變數 [TempX v] 設為 ((清單第 (Joint ID) 項項目\( [Rope Joints v] \) :: list) - (xPosition)) 變數 [TempY v] 設為 ((清單第 ((Joint ID) + (1)) 項項目\( [Rope Joints v] \) :: list) - (yPosition)) 變數 [TempX v] 設為 ((清單第 (Joint ID) 項項目\( [Rope Joints v] \) :: list) - ([x座標 v] of [Player Sprite v])) 變數 [TempY v] 設為 ((清單第 ((Joint ID) + (1)) 項項目\( [Rope Joints v] \) :: list) - ([y 座標 v] of [Player Sprite v])) end 變數 [Distance v] 設為 ([sqrt v] 數值 (((TempX) * (TempX)) + ((TempY) * (TempY)))) 如果 <(Segment Length) < (Distance)> 那麼 變數 [Rope Tension v] 設為 (((Distance) - (Segment Length)) / (Distance)) 變數 [ForceX v] 設為 ((Rope Tension) * (TempX)) 變數 [ForceY v] 設為 ((Rope Tension) * (TempY)) 變數 [ForceX v] 設為 (0) 變數 [ForceY v] 設為 (-1) // Applying gravity. end 如果 <(Joint Type) = (0)> 那麼 定位到 x: ([x座標 v] \( [Player Sprite v] \)) y: ([y 座標 v] \( [Player Sprite v] \)) 變數 [Player X Velocity v] 改變 (ForceX) 變數 [Player Y Velocity v] 改變 ((ForceY) - (3)) end 如果 <(Joint Type) > (0)> 那麼 變數 [TempX v] 設為 ((清單第 ((Joint ID) + (6)) 項項目\( [Rope Joints v] \) :: list) - (xPosition)) 變數 [TempY v] 設為 ((清單第 ((Joint ID) + (7)) 項項目\( [Rope Joints v] \) :: list) - (yPosition)) 變數 [Distance v] 設為 ([sqrt v] \( (((TempX) * (TempX)) + ((TempY) * (TempY))) \)) 如果 <(Segment Length) < (Distance)> 那麼 變數 [Rope Tension v] 設為 (((Distance) - (Segment Length)) / (Distance)) 變數 [ForceX v] 設為 (((Rope Tension) * (TempX)) * (0.6)) 變數 [ForceY v] 設為 (((Rope Tension) * (TempY)) * (0.5)) end end 如果 <<(Joint Type) = (0)> 不成立> 那麼 變數 [X Velocity v] 設為 (((X Velocity) + (ForceX)) * (0.65)) 變數 [Y Velocity v] 設為 ((((Y Velocity) + (ForceY)) * (0.61)) - (3)) x 改變 (X Velocity) y 改變 (Y Velocity) end 定義 Draw Rope Segment original: (xPosition) (yPosition) 筆跡寬度設為 (5) 下筆 x 改變 (0.4) // This block makes sure that something's drawn. 筆跡寬度設為 (3) 定位到 x: (清單第 (Joint ID) 項項目\( [Rope Joints v] \) :: list) y: (清單第 ((Joint ID) + (1)) 項項目\( [Rope Joints v] \) :: list) 停筆 // This is here to make sure no extra pen marks are made. 定位到 x: (xPosition) y: (yPosition) // Return to the original clone's position. 當收到訊息 [Destroy Ropes v] 如果 <清單 [Removed Rope Joints v] 包含 (Joint ID) ?> 那麼 分身刪除 end 如果 <(Joint ID) > (清單第 [1 v] 項項目\( [Removed Rope Joints v] \) :: list)> 那麼 變數 [Joint ID v] 改變 ((-3) * (清單 [Removed Rope Joints v] 的項目數 :: list)) end 如果 <(Joint Type) = (-2)> 那麼 變數 [Rope Joints Made v] 改變 ((-3) * (length of [Removed Rope Joints v] :: list)) 等待 (0) 秒 刪除第 [全部 v] 項 \( [Removed Rope Joints v] \) end
The following two scripts can be put in any sprite:
定義 Destroy Rope containing Rope Joint: (Joint Point ID) //Run without screen refresh! 變數 [i v] 設為 (0) 重複直到 <(清單第 (Joint Point ID) 項項目\( [Rope Joints v] \) :: list) < (1)> . . . // This repeat deletes all rope joints ahead of (Joint Point ID) until it meets the end of the rope. Destroy Rope Joint at: (Joint Point ID) 新增項目 (((Joint Point ID) + ((i) * (3))) - (2)) \( [Removed Rope Joints v] \) 變數 [i v] 改變 (1) end 新增項目 (((Joint Point ID) + ((i) * (3))) - (2)) \( [Removed Rope Joints v] \) 變數 [Joint Point ID 2 v] 設為 ((Joint Point ID) - (3)) 重複直到 <<(Joint Point ID 2) < (3)> 或 <(清單第 (Joint Point ID 2) 項項目\( [Rope Joints v] \) :: list) < (1)>> . . . // This repeat deletes all rope joints behind (Joint Point ID). Destroy Rope Joint at: (Joint Point ID 2) 新增項目 ((Joint Point ID 2) - (2)) \( [Removed Rope Joints v] \) 變數 [Joint Point ID 2 v] 改變 (-3) end 變數 [Joint Point ID v] 改變 (1) . . . // The next block deletes the tail of the rope. Destroy Rope Joint at: (Joint Point ID 2) 廣播訊息 [Destroy Ropes v] 定義 Destroy Rope Joint at: (Point ID) //Run without screen refresh! 刪除第 (Point ID) 項 \( [Rope Joints v] \) // This deletes the first data bit of a rope joint. 刪除第 (Point ID) 項 \( [Rope Joints v] \) // This deletes the second data bit of a rope joint. 刪除第 (Point ID) 項 \( [Rope Joints v] \) // This deletes the third data bit of a rope joint, finishing the process.
Simple rope physics
A restricted, but simpler method of rope physics is also available. It consists of the rope's starting location, the ending location, a sprite representing a person, and a sprite used to draw the rope.
This method represents a person ziplining to either side of the rope, and the rope sagging to the person's weight.
Firstly, position the sprites that represent the starting and ending position of the rope.
Secondly, add this script to the sprite in charge of drawing the rope.
當 @greenflag 被點擊 筆跡顏色設為 [#C26B08] 筆跡寬度設為 (3) 下筆 重複無限次 筆跡清除 定位到 [Rope End Left v] 位置 下筆 定位到 [Person v] 位置 定位到 [Rope End RIght v] 位置 停筆 圖層上移至頂層 end
Thirdly, script the person such that it follows the path of the rope. For example, if the rope goes horizontally, you may wish to add this script into the person:
當 @greenflag 被點擊 重複無限次 如果 <[向右 v] 鍵被按下?> 那麼 x 改變 (5) 如果 <[向左 v] 鍵被按下?> 那麼 x 改變 (-5) end end end
Be sure to position the person just below the rope for a more realistic effect.
Rope Physics on Scratch is an example project showing this method.