Is there a way to simulate a pulley in 2D? (without simulating a full rope/chain)

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By Sean

example

Just like in this drawing. I will have 2 RigidBody2D’s and I would like a way to simulate them being connected together by a rope and pulley.

I thought of simulating a chain by connecting a bunch of bodies together in a link with joints but that seems over the top and expensive.

:bust_in_silhouette: Reply From: FortunePilot

It depends on what you want to do with it.
If you want the platforms to eventually roll over the pulleys, you’ll have to work with the link/joints method you mentioned.

Otherwise, if you want the two platforms to just move up and down and stay on the same vertical path, you can simulate something like this using basic physics.

Step 1:
Write a function that determines the force applied on one platform, and have both platforms inherit this function (so you know what force is applied on a platform without the other platform present).

Step 2:
Link the two platforms together: Within the function you just wrote, write code that requests the current force that is applied on the other platform, then apply the negative of that force on the own platform.

Do this every physics tick, and you should have a working pully.

If there are any questions, don’t hesitate to comment!

Kind regards,

~FortunePilot

Then you could also put each pulley on a vertical slider to, to keep it straight up and down.

Merlin1846 | 2020-04-23 14:04

First, thank you so much for taking the time to reply, I’ve been toying with it for over a day now and it’s proving to be much harder than it sounded.

…that determines the force applied on one platform

Force is acceleration * mass right? I’m struggling to find a way to get this out of the physics engine. When I hard code acceleration to the global gravity I can kind of get something that starts to look like a pulley system, but what I need to find is the current force on the body, so I can get the added weight of other bodies on it and any collision under it.

Here is what I got as a POC using a hard coded force. Before I got into making a script for each body I have one on the parent to control both.

onready var a: RigidBody2D = $A
onready var b: RigidBody2D = $B

onready var gravity_vector : Vector2 = ProjectSettings.get_setting("physics/2d/default_gravity_vector")
onready var gravity_magnitude : int = ProjectSettings.get_setting("physics/2d/default_gravity")
onready var g = gravity_vector * gravity_magnitude

func get_force(body: RigidBody2D) -> Vector2:
	var delta = get_physics_process_delta_time()
	# assuming the only acceleration is gravity
	return g * body.mass * delta

func _physics_process(delta: float) -> void:
	a.apply_impulse(Vector2(0, 0), -get_force(b))
	b.apply_impulse(Vector2(0, 0), -get_force(a))

This does look ok, except for what I mentioned above. Also, each body slowly creeps down and the higher the gravity the faster the creep. I think the physics engine is applying forces a step ahead of me?

Any hints on how to calculate a bodies current force?

Sean | 2020-04-26 23:53

@FortunePilot @Merlin1846

After many more hours I’m giving up. Calculating all applied forces isn’t available. And even if it was there is the issue where each body needs to counter the forces of the other BEFORE their own forces are actually applied. I see no way of doing this.

Is there some kind of bounty program for plugin development, I’m at point where I just want to see this work lol.

Sean | 2020-04-27 05:49

I’m not going to lie, I also toyed around with an idea of a pully…
And I might have found a way easier solution than the one I initially thought of!
Here’s how it goes:

Instead of treating both platforms seperately, combine them into one single system. What got me thinking is: a pulley is basically a tug-o-war, but with corners.
If you treat your pully as a tug of war, coding it will be a lot less problematic, and like you said, would eliminate that annoying loop-hole of applying forces of the other platform first.

Have one central point, on which both pulleys apply a force, one to the left, and one to the right. This way, the pulleys don’t have to ask eachother for their current forces.

Something along the lines of this:

const GRAVITY = 10
const STATIC_FRICTION = 5
var force_platform_one # Pulls central point to the LEFT.
var force_platform_two # Pulls central point to the RIGHT.
var force_centrally # Overall force applied on the central point.

# You'll have to code your own way of applying mass on the platforms
# themselves, but for sake of visualization, these variables represent
# the masses of the platforms themselves, and masses on both platforms.
const MASS_ONE
const MASS_TWO
var mass_on_platform_one
var mass_on_platform_two

func move_platform(platform, force, delta):
    # Apply your impulse on a platform here. I have no knowledge of
    # this, so I won't add specific code here to prevent confusing you.

# Again, positive force is to the RIGHT, negative to the LEFT.
func move_system(force, delta):
    if force < 0 and force < -STATIC_FRICTION:
        move_platform(1, force, delta)
        move_platform(2, -force, delta)
    elif force > 0 and force > STATIC_FRICTION:
        move_platform(1, -force, delta)
        move_platform(2, force, delta)
    else:
        print("not move")

func _physics_process(delta):
    force_pully_one = GRAVITY * (MASS_ONE + mass_on_platform_one)
    force_pully_two = GRAVITY * -(MASS_TWO + mass_on_platform_two)

    force_centrally = force_pully_two + force_pully_one
    move_system(force_centrally, delta)

This should be the general code needed for it to work.

Also, each body slowly creeps down and the higher the gravity the faster the creep.

This is something in-real-life-physics related, and not engine related.
In real life, there’s always some form of FRICTION involved, whether it be tension, movement, air. In this case, it’s most likely static friction. Static friction prevents two non-moving objects that touch eachother from moving.

Example: when you try to push a heavy box along the floor, you have to push with a high enough force to get it moving. Concrete has a higher static friction coefficient than ice, which makes it easier to start moving objects that are on ice than it is to start moving objects that are on concrete.

Because you haven’t coded in a way of representing said friction, the game does not know how to correctly handle the movement, and as such will make the platforms creep.

The easiest way to implement this is to have a small threshold that has to be crossed in order to actually exert the force. I included it in the code above, in the function move_central_point(force).

If you have any more questions, feel free to ask them!

Kind regards,

~FortunePilot

FortunePilot | 2020-04-27 13:08