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.

Note Note: 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:

Spring.png

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 v] 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)
  clear//Clears the screen of old rope drawings

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 v] 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)
  clear

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

Note 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:

Spring with Blue Outline.png

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 v] 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 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
. . .//The next three blocks delete the rope from (ChainCreateList) since it is already created.
delete [1 v] of [ChainCreateList v]
delete [1 v] of [ChainCreateList v]
delete [1 v] of [ChainCreateList v]
wait (0) secs
forever
  . . .//Rope Physics go in here.

The code inside of the forever block is actually surprisingly simple. All it consist 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 v] 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 v] of [ChainCreateList v]
delete [1 v] of [ChainCreateList v]
delete [1 v] of [ChainCreateList v]
wait (0) secs
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)> then
  set [Rope Tension v] to (((Distance) - (Segment Length)) / (Distance))
  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)> then
  set [Rope Tension v] to (((Distance) - (Segment Length)) / (Distance))
  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)> then
    set [Rope Tension v] to (((Distance) - (Segment Length)) / (Distance))
    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 v] 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 amount 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) secs
  delete [all v] 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 v] of [Rope Joints v]
  set [Rope Joints Made v] to (1)
end
delete this clone

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).

when gf clicked
set [Segment Length v] to (25)
delete [all v] 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) secs
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:

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

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 v] 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 v] of [ChainCreateList v]
delete [1 v] of [ChainCreateList v]
delete [1 v] of [ChainCreateList v]
wait (0) secs
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

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)> then
  set [Rope Tension v] to (((Distance) - (Segment Length)) / (Distance))
  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)> then
    set [Rope Tension v] to (((Distance) - (Segment Length)) / (Distance))
    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) secs
  delete [all v] of [Removed Rope Joints v]
end

The following two scripts can be put in any sprite:

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)
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]

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.

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.

When gf clicked 
set pen color to [#C26B08]
set pen size to (3)
pen down
forever
clear
go to [Rope End Left v]
pen down
go to [Person v]
go to [Rope End RIght v]
pen up
go to front

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:

When gf clicked
forever
if <key [right arrow v] pressed>
change x by (5)
if <key [left arrow v] pressed> 
change x by (-5)

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.

See Also

Cookies help us deliver our services. By using our services, you agree to our use of cookies.