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.
Method 1
This method generates good results and can be found here.
Adding Rope Joints
The first step to making rope physics is making the rope joints, this can be done with this code:
When green flag clicked delete all of [x pos v] // Delete all rope joints delete all of [y pos v] // Delete all rope joints broadcast (Setup v) and wait // Set up all of the variables forever set [Old length v] to (Length) set [ID v] to (0) repeat (Length) // create the rope joint clones change [ID v] by (1) create clone of (myself v) add [] to [x pos v] add [] to [y pos v] end repeat until <not<(Length) = (Old length)>> Rope::custom // Render rope end
Rendering the Rope
Now that the rope joints are made, they need to be rendered. The script might not take any effect for now.
define Rope erase all // Deletes all previous drawings of the rope set [counter v] to (1) // reset the counter go to x: (item (counter) of [x pos v]) y:(mouse y) pen down change [counter v] by (1) repeat ((Length)-(1)) // Iterate through the list and draw the rope. go to x: (item (counter) of [x pos v]) y:(item (counter) of [y pos v]) change [counter v] by (1) end pen up
Adding Physics to the Rope Joints
Physics needs to be added to each rope joint to make it seem realistic.
When I start as a clone // Physics for each rope joint forever if <(ID)=(1)> then go to (mouse pointer v) else change x by (((item ((ID)-(1)) of [x pos v])-(x position))*(Tension) change y by (((item ((ID)-(1)) of [y pos v])*(Density))+(((ID)-(1))*((Stiffness)*(Gravity)))) replace item (ID) of [x pos v] with (x position) replace item (ID) of [y pos v] with (y position) pen up if <not<(Length) = (Old length)>> then delete this clone end end end
Implementing Code
Now everything except for using the code has been done, all that needs to be added is setting variables and using the color and thickness variables for the rope's look.
When I receive [Setup v] erase all // erase all other drawings set [Stiffness v] to (1) // set variables. set [Length v] to (10) set [Thickness v] to (5) set [Density v] to (0.4) set [Tension v] to (0.4) set [Color v] to (62) set [Gravity v] to (-1) When green flag clicked forever // Set the pen size and color of the rope set pen size to (Thickness) set pen (color v) to (Color) end
Rope Physics Engine (1 Sprite) is an example of a project using a similar method to this.
Method 2
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. This method will represent this.
Note: | The ideas expressed in this section 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 variables for it's mass and a few properties:
- Mass
- Dampening
- Stiffness
- Gravity
The 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:
when gf clicked hide repeat (2) create clone of (myself v) end when I start as a clone . . . // 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:
when gf clicked set [# of Rope Joints Made v] to [1] hide repeat (2) create clone of (myself v) end repeat (6) add [0] to [Rope Joints v] end forever replace item (1) of [Rope Joints v] with (Mouse X) // This is how the rope will look at the mouse's position replace item ((0) + (2)) of [Rope Joints v] with (Mouse Y) erase all // Clears the screen of old rope drawings end when I start as a clone set [Rope ID v] to (# of Rope Joints Made) change [# of Rope Joints Made v] by (2) // So that we can figure out which rope joint the clone is forever render replace item ((Rope ID) + (2)) of [Rope Joints v] with (x position) // So that other joints can look at this joint's position replace item ((Rope ID) + (3)) of [Rope Joints v] with (y position) define render go to x: (item (Rope ID) of [Rope Joints v]) y: (item ((Rope ID) + (1)) of [Rope Joints v]) pen down go to x: (item ((Rope ID) + (2)) of [Rope Joints v]) y: (item ((Rope ID) + (3)) of [Rope Joints v]) pen up
Here's when the physics are added:
when I start as a clone set [Rope ID v] to (# of Rope Joints Made) change [# of Rope Joints Made v] by (2) forever physics render replace item ((Rope ID) + (2)) of [Rope Joints v] with (x position) // So that other joints can look at this joint's position replace item ((Rope ID) + (3)) of [Rope Joints v] with (y position) define render go to x: (item (Rope ID) of [Rope Joints v]) y: (item ((Rope ID) + (1)) of [Rope Joints v]) pen down go to x: (item ((Rope ID) + (2)) of [Rope Joints v]) y: (item ((Rope ID) + (3)) of [Rope Joints v]) pen up define physics set [Force X v] to (((item (Rope ID) of [Rope Joints v]) - (x position)) * (Stiffness)) set [True Force X v] to ((Force X) / (Mass)) // Distributing force over the mass set [X Velocity v] to ((Dampening) * ((X Velocity) + (True Force X))) // Friction set [Force Y v] to (((item ((Rope ID) + (1)) of [Rope Joints v]) - (y position)) * (Stiffness)) change [Force Y v] by (Gravity)//Make sure gravity is negative set [True Force Y v] to ((Force Y) / (Mass)) set [Y Velocity v] to ((Dampening) * ((Y Velocity) + (True Force Y))) change x by (X Velocity) change y by (Y Velocity)
Final Product
This should be the code to make a two-jointed rope that's connected to the mouse:
when gf clicked set [# of Rope Joints Made v] to (1) hide repeat (2) create clone of (myself v) end repeat (6) add [0] to [Rope Joints v] end forever replace item (1) of [Rope Joints v] with (Mouse X) // This is how the rope will look at the mouse's position replace item ((0) + (2)) of [Rope Joints v] with (Mouse Y) erase all end when I start as a clone set [Rope ID v] to (# of Rope Joints Made) change [# of Rope Joints Made v] by (2) forever physics render replace item ((Rope ID) + (2)) of [Rope Joints v] with (x position) // So that other joints can look at this joint's position replace item ((Rope ID) + (3)) of [Rope Joints v] with (y position) define render go to x: (item (Rope ID) of [Rope Joints v]) y: (item ((Rope ID) + (1)) of [Rope Joints v]) pen down go to x: (item ((Rope ID) + (2)) of [Rope Joints v]) y: (item ((Rope ID) + (3)) of [Rope Joints v]) pen up define physics set [Force X v] to (((item (Rope ID) of [Rope Joints v]) - (x position)) * (Stiffness)) set [True Force X v] to ((Force X) / (Mass)) set [X Velocity v] to ((Dampening) * ((X Velocity) + (True Force X))) set [Force Y v] to (((item ((Rope ID) + (1)) of [Rope Joints v]) - (y position)) * (Stiffness)) change [Force Y v] by (Gravity) set [True Force Y v] to ((Force Y) / (Mass)) set [Y Velocity v] to ((Dampening) * ((Y Velocity) + (True Force Y))) change x by (X Velocity) change y by (Y Velocity)
Pendulum-Spring Based Ropes
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 Position of Rope Joint 1
- Y Position of Rope Joint 1
- Type of Rope Joint 1
- X Position of Rope Joint 2
- Y Position 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:
define Add Rope Joint at x: (xPos) y: (yPos) Type: (Type) // Run without screen refresh! add (xPos) to [ChainCreateList v] // xPos is the xPosition of the rope joint being created. add (yPos) to [ChainCreateList v] // yPos is the yPosition of the rope joint. add (Type) to [ChainCreateList v] // Type is the type of the rope joint. define Initiate Rope Length #: (Rope Number) // Run without screen refresh! repeat (Rope Number) create clone of (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:
when gf clicked set pen color to [#C26B08] // Ropes will be drawn in this color. set [Rope Joints Made v] to (1) set [Joint Type v] to (-2) // This is here to help tell the difference between a clone and the sprite. hide when I start as a clone add (item (1) of [ChainCreateList v]) to [Rope Joints v] add (item ((0) + (2)) of [ChainCreateList v]) to [Rope Joints v] set [Joint Type v] to (item ((0) + (3)) of [ChainCreateList v]) if <(0) < (Joint Type)> then add (Joint Type) to [Rope Joints v] else add (1) to [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 set [X Velocity v] to (0) // Making the rope calm. set [Y Velocity v] to (0) . . . // The next block will figure out where the clone is in the list (Rope Joints). set [Joint ID v] to (Rope Joints Made) change [Rope Joints Made v] by (3) . . . // The next if block creates a second rope joint. . . . // Look at the large comment chain above for more information. if <(Joint Type) < (1)> then add (item (1) of [ChainCreateList v]) to [Rope Joints v] add ((item ((0) + (2)) of [ChainCreateList v]) - (Segment Length)) to [Rope Joints v] add (Joint Type) to [Rope Joints v] change [Rope Joints Made v] by (3) end . . . // The next three blocks delete the rope from (ChainCreateList) since it is already created. delete (1) of [ChainCreateList v] delete (1) of [ChainCreateList v] delete (1) of [ChainCreateList v] wait (0) seconds forever . . . // Rope Physics go in here.
The code inside of the forever block is actually surprisingly simple. All it consists of are two custom blocks and two list blocks to update (Rope Joints) with the position of the rope joint. This is what the code looks like with forever block filled in:
when gf clicked // Ropes will be drawn in this color. set pen color to [#C26B08] set [Rope Joints Made v] to (1) set [Joint Type v] to (-2) // This is here to help tell the difference between a clone and the sprite. hide when I start as a clone add (item (1) of [ChainCreateList v]) to [Rope Joints v] add (item ((0) + (2)) of [ChainCreateList v]) to [Rope Joints v] set [Joint Type v] to (item ((0) + (3)) of [ChainCreateList v]) if <(0) < (Joint Type)> then add (Joint Type) to [Rope Joints v] else add (1) to [Rope Joints v] end set [X Velocity v] to (0) // Making the rope calm. set [Y Velocity v] to (0) set [Joint ID v] to (Rope Joints Made) change [Rope Joints Made v] by (3) if <(Joint Type) < (1)> then add (item (1) of [ChainCreateList v]) to [Rope Joints v] add ((item ((0) + (2)) of [ChainCreateList v]) - (Segment Length)) to [Rope Joints v] add (Joint Type) to [Rope Joints v] change [Rope Joints Made v] by (3) end delete (1) of [ChainCreateList v] delete (1) of [ChainCreateList v] delete (1) of [ChainCreateList v] wait (0) seconds forever Apply Chain Physics of x: (item ((Joint ID) + (3)) of [Rope Joints v]) y: (item ((Joint ID) + (4)) of [Rope Joints v]) . . . // The next two blocks update (Rope Joints) with the new position of the rope joint. replace item ((Joint ID) + (3)) of [Rope Joints v] with (x position) replace item ((Joint ID) + (4)) of [Rope Joints v] with (y position) Draw Rope Segment original: (x position) (y position) define Apply Chain Physics of x: (xPosition) y: (yPosition) . . . // To be implemented in the section called "Adding Physics to the Rope Joints" define 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"
define 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. if <(Joint Type) = (0)> then set [TempX v] to ((item (Joint ID) of [Rope Joints v]) - (xPosition)) set [TempY v] to ((item ((Joint ID) + (1)) of [Rope Joints v]) - (yPosition)) else set [TempX v] to ((item (Joint ID) of [Rope Joints v]) - ([x position v] of (Player Sprite v))) set [TempY v] to ((item ((Joint ID) + (1)) of [Rope Joints v]) - ([y position v] of (Player Sprite v))) end set [Distance v] to ([sqrt v] of (((TempX) * (TempX)) + ((TempY) * (TempY)))) if <(Segment Length) < (Distance::variables)> then set [Rope Tension v] to (((Distance::variables) - (Segment Length)) / (Distance::variables)) set [ForceX v] to ((Rope Tension) * (TempX)) set [ForceY v] to ((Rope Tension) * (TempY)) else set [ForceX v] to (0) set [ForceY v] to (-1) // Applying gravity. end if <(Joint Type) = (0)> then . . . // 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. go to x: ([x position v] of (Player Sprite v)) y: ([y position v] of (Player Sprite v)) change [Player X Velocity v] by (ForceX) change [Player Y Velocity v] by ((ForceY) - (3)) end . . . // More code to be added here. if <not <(Joint Type) = (0)>> then set [X Velocity v] to (((X Velocity) + (ForceX)) * (0.65)) set [Y Velocity v] to ((((Y Velocity) + (ForceY)) * (0.61)) - (3)) change x by (X Velocity) change y by (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.
define Apply Chain Physics of x: (xPos) y: (yPos) // Run without screen refresh! if <(Joint Type) = (0)> then set [TempX v] to ((item (Joint ID) of [Rope Joints v]) - (xPosition)) set [TempY v] to ((item ((Joint ID) + (1)) of [Rope Joints v]) - (yPosition)) else set [TempX v] to ((item (Joint ID) of [Rope Joints v]) - ([x position v] of (Player Sprite v))) set [TempY v] to ((item ((Joint ID) + (1)) of [Rope Joints v]) - ([y position v] of (Player Sprite v))) end set [Distance v] to ([sqrt v] of (((TempX) * (TempX)) + ((TempY) * (TempY)))) if <(Segment Length) < (Distance:: variables)> then set [Rope Tension v] to (((Distance:: variables) - (Segment Length)) / (Distance:: variables)) set [ForceX v] to ((Rope Tension) * (TempX)) set [ForceY v] to ((Rope Tension) * (TempY)) else set [ForceX v] to [0] set [ForceY v] to [-1] // Applying gravity. end if <(Joint Type) = (0)> then go to x: ([x position v] of (Player Sprite v)) y: ([y position v] of (Player Sprite v)) change [Player X Velocity v] by (ForceX) change [Player Y Velocity v] by ((ForceY) - (3)) end if <(Joint Type) > (0)> then // New Content set [TempX v] to ((item ((Joint ID) + (6)) of [Rope Joints v]) - (xPosition)) set [TempY v] to ((item ((Joint ID) + (7)) of [Rope Joints v]) - (yPosition)) set [Distance v] to ([sqrt v] of (((TempX) * (TempX)) + ((TempY) * (TempY)))) if <(Segment Length) < (Distance:: variables)> then set [Rope Tension v] to (((Distance:: variables) - (Segment Length)) / (Distance:: variables)) set [ForceX v] to (((Rope Tension) * (TempX)) * (0.6)) set [ForceY v] to (((Rope Tension) * (TempY)) * (0.5)) end end if <not <(Joint Type) = (0)>> then set [X Velocity v] to (((X Velocity) + (ForceX)) * (0.65)) set [Y Velocity v] to ((((Y Velocity) + (ForceY)) * (0.61)) - (3)) change x by (X Velocity) change y by (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.
define Draw Rope Segment original: (xPosition) (yPosition) set pen size to (5) pen down change x by (0.4) // This block makes sure that something's drawn. set pen size to (3) go to x: (item (Joint ID) of [Rope Joints v]) y: (item ((Joint ID) + (1)) of [Rope Joints v]) . . . // Since the pen is down, simply going from one rope joint to another makes a line. pen up // This is here to make sure no extra pen marks are made. go to 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.
define Destroy Rope containing Rope Joint: (Joint Point ID) // Run without screen refresh! set [i v] to (0) repeat until <(item (Joint Point ID) of [Rope Joints v]) < (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) add (((Joint Point ID) + ((i) * (3))) - (2)) to [Removed Rope Joints v] change [i v] by (1) // Delete next rope joint. end add (((Joint Point ID) + ((i) * (3))) - (2)) to [Removed Rope Joints v] set [Joint Point ID 2 v] to ((Joint Point ID) - (3)) repeat until <<(Joint Point ID 2) < (3)> or <(item (Joint Point ID 2) of [Rope Joints v]) < (1)>> . . . // This repeat deletes all rope joints behind (Joint Point ID). Destroy Rope Joint at: (Joint Point ID 2) add ((Joint Point ID 2) - (2)) to [Removed Rope Joints v] change [Joint Point ID 2 v] by (-3) // Delete next rope joint. end change [Joint Point ID v] by (1) . . . // The next block deletes the tail of the rope. Destroy Rope Joint at: (Joint Point ID 2) broadcast (Destroy Ropes v) define Destroy Rope Joint at: (Point ID) // Run without screen refresh! delete (Point ID) of [Rope Joints v] // This deletes the first data bit of a rope joint. delete (Point ID) of [Rope Joints v] // This deletes the second data bit of a rope joint. delete (Point ID) of [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).
when I receive [Destroy Ropes v] if <[Removed Rope Joints v] contains (Joint ID)?> then . . . // This checks if the rope joint was deleted, and if it was, it completes the process. delete this clone end if <(Joint ID) > (item (1) of [Removed Rope Joints v])> then . . . // 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 change [Joint ID v] by ((-3) * (length of [Removed Rope Joints v])) end if <(Joint Type) = (-2)> then . . . // If (Joint Type) equals -2, then this is the original Rope sprite. . . . // All it does it decrease (Rope Joints Made) by the number of rope joints deleted . . . // It also resets the list (Removed Rope Joints). change [Rope Joints Made v] by ((-3) * (length of [Removed Rope Joints v])) wait (0) seconds delete all of [Removed Rope Joints v] end
Miscellaneous
To delete all ropes, the next script is needed in Ropes:
when I receive [Rope Clear v] if <(Joint Type) = (-2)> then delete all of [Rope Joints v] set [Rope Joints Made v] to (1) end delete this clone
To test if code was copied correctly, a rope can be made. 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).
when gf clicked set [Segment Length v] to [25] delete all of [Rope Joints v]//Getting ready to make a rope. Add Rope Joint at x: (0) y: (80) Type: (1) Add Rope Joint at x: (0) y: (55) Type: (1) Add Rope Joint at x: (0) y: (30) Type: (1) Add Rope Joint at x: (0) y: (5) Type: (0) Initiate Rope Length #: (4) broadcast (Rope Waver v) when I receive [Rope Waver v] wait (0.1) seconds set [Player X Velocity v] to [0] set [Y Velocity v] to [0] if <(Chain Type) = (0)> then set [X Acceleration v] to [-2.2] forever set [j v] to [0] repeat until <(j) = [40]> change [j v] by (1) change [Player X Velocity v] by (X Acceleration) change [X Acceleration v] by (0.11) // Pushing the player slightly right. end set [j v] to [0] repeat until <(j) = [40]> change [j v] by (1) change [Player X Velocity v] by (X Acceleration) change [X Acceleration v] by (-0.11) // Pushing the player slightly left. end end end define Add Rope Joint at x: (xPos) y: (yPos) Type: (Type) . . . // This blocks definition is above. define 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:
// Run without screen refresh! define Add Rope Joint at x: (xPos) y: (yPos) Type: (Type) add (xPos) to [ChainCreateList v] // xPos is the xPosition of the rope joint being created. add (yPos) to [ChainCreateList v] // yPos is the yPosition of the rope joint. add (Type) to [ChainCreateList v] // Type is the type of the rope joint. // Run without screen refresh! define Initiate Rope Length #: (Rope Number) repeat (Rope Number) create clone of (Ropes v) // Ropes is the sprite that will render the ropes. end
The next series of scripts need to be in the Ropes sprite:
when gf clicked // Ropes will be drawn in this color. set pen color to [#C26B08] set [Rope Joints Made v] to [1] set [Joint Type v] to [-2] // This is here to help tell the difference between a clone and the sprite. hide when I start as a clone add (item (1) of [ChainCreateList v]) to [Rope Joints v] add (item ((0) + (2)) of [ChainCreateList v]) to [Rope Joints v] set [Joint Type v] to (item ((0) + (3)) of [ChainCreateList v]) if <(0) < (Joint Type)> then add (Joint Type) to [Rope Joints v] else add [1] to [Rope Joints v] end set [X Velocity v] to [0] // Making the rope calm. set [Y Velocity v] to [0] set [Joint ID v] to (Rope Joints Made) change [Rope Joints Made v] by (3) if <(Joint Type) < [1]> then add (item [1 v] of [ChainCreateList v]) to [Rope Joints v] add ((item ((0) + (2)) of [ChainCreateList v]) - (Segment Length)) to [Rope Joints v] add (Joint Type) to [Rope Joints v] change [Rope Joints Made v] by (3) end delete (1) of [ChainCreateList v] delete (1) of [ChainCreateList v] delete (1) of [ChainCreateList v] wait (0) seconds forever Apply Chain Physics of x: (item ((Joint ID) + (3)) of [Rope Joints v]) y: (item ((Joint ID) + (4)) of [Rope Joints v]) replace item ((Joint ID) + (3)) of [Rope Joints v] with (x position) replace item ((Joint ID) + (4)) of [Rope Joints v] with (y position) Draw Rope Segment original: (x position) (y position) end // Run without screen refresh! define Apply Chain Physics of x: (xPos) y: (yPos) if <(Joint Type) = (0)> then set [TempX v] to ((item (Joint ID) of [Rope Joints v]) - (xPosition)) set [TempY v] to ((item ((Joint ID) + (1)) of [Rope Joints v]) - (yPosition)) else set [TempX v] to ((item (Joint ID) of [Rope Joints v]) - ([x position v] of (Player Sprite v))) set [TempY v] to ((item ((Joint ID) + (1)) of [Rope Joints v]) - ([y position v] of (Player Sprite v))) end set [Distance v] to ([sqrt v] of (((TempX) * (TempX)) + ((TempY) * (TempY)))) if <(Segment Length) < (Distance::variables)> then set [Rope Tension v] to (((Distance::variables) - (Segment Length)) / (Distance::variables)) set [ForceX v] to ((Rope Tension) * (TempX)) set [ForceY v] to ((Rope Tension) * (TempY)) else set [ForceX v] to [0] set [ForceY v] to [-1] // Applying gravity. end if <(Joint Type) = [0]> then go to x: ([x position v] of (Player Sprite v)) y: ([y position v] of (Player Sprite v)) change [Player X Velocity v] by (ForceX) change [Player Y Velocity v] by ((ForceY) - (3)) end if <(Joint Type) > [0]> then set [TempX v] to ((item ((Joint ID) + (6)) of [Rope Joints v]) - (xPosition)) set [TempY v] to ((item ((Joint ID) + (7)) of [Rope Joints v]) - (yPosition)) set [Distance v] to ([sqrt v] of (((TempX) * (TempX)) + ((TempY) * (TempY)))) if <(Segment Length) < (Distance::variables)> then set [Rope Tension v] to (((Distance::variables) - (Segment Length)) / (Distance::variables)) set [ForceX v] to (((Rope Tension) * (TempX)) * (0.6)) set [ForceY v] to (((Rope Tension) * (TempY)) * (0.5)) end end if <not <(Joint Type) = (0)>> then set [X Velocity v] to (((X Velocity) + (ForceX)) * (0.65)) set [Y Velocity v] to ((((Y Velocity) + (ForceY)) * (0.61)) - (3)) change x by (X Velocity) change y by (Y Velocity) end define Draw Rope Segment original: (xPosition) (yPosition) set pen size to (5) pen down change x by (0.4) // This block makes sure that something's drawn. set pen size to (3) go to x: (item (Joint ID) of [Rope Joints v]) y: (item ((Joint ID) + (1)) of [Rope Joints v]) pen up // This is here to make sure no extra pen marks are made. go to x: (xPosition) y: (yPosition) // Return to the original clone's position. when I receive [Destroy Ropes v] if <[Removed Rope Joints v] contains (Joint ID)?> then delete this clone end if <(Joint ID) > (item [1 v] of [Removed Rope Joints v])> then change [Joint ID v] by ((-3) * (length of [Removed Rope Joints v])) end if <(Joint Type) = (-2)> then change [Rope Joints Made v] by ((-3) * (length of [Removed Rope Joints v])) wait (0) seconds delete all of [Removed Rope Joints v] end
The following two scripts can be put in any sprite:
// Run without screen refresh! define Destroy Rope containing Rope Joint: (Joint Point ID) set [i v] to (0) repeat until <(item (Joint Point ID) of [Rope Joints v]) < (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) add (((Joint Point ID) + ((i) * (3))) - (2)) to [Removed Rope Joints v] change [i v] by (1) end add (((Joint Point ID) + ((i) * (3))) - (2)) to [Removed Rope Joints v] set [Joint Point ID 2 v] to ((Joint Point ID) - (3)) repeat until <<(Joint Point ID 2) < (3)> or <(item (Joint Point ID 2) of [Rope Joints v]) < [1]>> . . . // This repeat deletes all rope joints behind (Joint Point ID). Destroy Rope Joint at: (Joint Point ID 2) add ((Joint Point ID 2) - (2)) to [Removed Rope Joints v] change [Joint Point ID 2 v] by (-3) end change [Joint Point ID v] by (1) . . . // The next block deletes the tail of the rope. Destroy Rope Joint at: (Joint Point ID 2) broadcast (Destroy Ropes v) // Run without screen refresh! define Destroy Rope Joint at: (Point ID) delete (Point ID) of [Rope Joints v] // This deletes the first data bit of a rope joint. delete (Point ID) of [Rope Joints v] // This deletes the second data bit of a rope joint. delete (Point ID) of [Rope Joints v] // This deletes the third data bit of a rope joint, finishing the process.
Method 3
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, the sprites that represent the starting and ending position of the rope have to be positioned.
Secondly, this script needs to be added to the sprite to draw the rope.
When gf clicked set pen color to [#C26B08] set pen size to (3) pen down forever erase all go to (Rope End Left v) pen down go to (Person v) go to (Rope End Right v) pen up go to [front v] layer
Thirdly, the person needs to be scripted such that it follows the path of the rope. For example, if the rope goes horizontally, this script may be added into the person:
When gf clicked forever if <key (right arrow v) pressed> then change x by (5) if <key (left arrow v) pressed> then change x by (-5)
For a more realistic effect, the person has to be positioned just below the rope.
Rope Physics on Scratch is an example project showing this method.