Search code examples
game-physicsgodotgdscript

Descending pendulum motion in _physics_process


I'm trying to simulate pendulum motion within _physics_process as such:

extends KinematicBody2D

var direction:Vector2
var gravity_speed=30000

onready var rod=$rod
onready var pivot:Vector2=to_global(rod.points[1])

func _physics_process(delta):
    
    var theta=Vector2(1,0).angle_to(to_local(pivot)) * -1 # angle between local x_axis & pivot point vector 
    var sin_theta=sin(theta)
    var cos_theta=cos(theta)
    
    direction.y += gravity_speed * delta
    
    direction.x= direction.x + direction.y*sin_theta*cos_theta - direction.x*cos_theta*cos_theta
    direction.y= direction.y + direction.x*sin_theta*cos_theta - direction.y*sin_theta*sin_theta
    
    direction=move_and_slide(direction,Vector2.UP)
    rod.points[1]=rod.to_local(pivot)

enter image description here

But for some reason it seems to be "falling downwards" (increased gravity so it's visible faster): enter image description here

Any idea why this might be caused?

Note:
I'm ideally looking for a fix which involves _physics_process() so the object can interact with the environment as well

Minimal

Edit:

This is how I came up with the equation:

enter image description here

(theta *-1 & other -ve signs in the code because y is flipped)


Edit 2: What was causing the error?

I ran some simulations on my windows xp super computer and found out why the problem was being caused!

Turns out it's not a bug in godot but in how we deal with circular motion in physics

In physics, if we apply a tangential force continuously it causes the object to rotate in circular motion (emphasis on the keyword "continuously")

However, In godot we are NOT applying the tangential force continuously but instead apply it from frame to frame & as such our body tends to move forward tangential between those frames

Giving us an "Err or" from the trajectory we're suppose to be following:
enter image description here

This piles up in the long run and gives the illusion that the pendulum is stretching downwards:
enter image description here

(You can think of the universe having an infinite frame rate :P)


Solution

  • I have a solution. What was missing from this simulation was the tension force from the rod, in the direction of the pivot. The rod_length is calculated onready and is set from the starting distance, but it can be set manually or changed during runtime. I did have to add air resistance (which must be negative), because once the bug was fixed the pendulum no longer lost speed.

    extends KinematicBody2D
    
    var direction:Vector2
    var gravity_speed=6000 #30000
    var air_resistance = -200
    
    onready var rod=$rod
    onready var pivot:Vector2=to_global(rod.points[1])
    onready var rod_length = get_position().distance_to(pivot)
    
    func _physics_process(delta):
        direction=move_and_slide(direction,Vector2.UP)
        
        #gravity
        direction.y += gravity_speed * delta
        
        #air resistance
        direction += (direction.normalized() * air_resistance * delta).limit_length(direction.length())
        
        #pendulum motion
        var theta=Vector2(1,0).angle_to(to_local(pivot)) * -1 # angle between local x_axis & pivot point vector 
        var sin_theta=sin(theta)
        var cos_theta=cos(theta)
        direction.x= direction.x + direction.y*sin_theta*cos_theta - direction.x*cos_theta*cos_theta
        direction.y= direction.y + direction.x*sin_theta*cos_theta - direction.y*sin_theta*sin_theta
        
        #tension
        var tension = clamp(get_position().distance_to(pivot) - rod_length, 0, 1) * gravity_speed
        direction += get_position().direction_to(pivot) * tension * delta
        
        rod.points[1]=rod.to_local(pivot)
    

    I am not sure exactly what was causing the issue, but the same issue persists if you do tension before pendulum motion, and a different issue appears if you do pendulum motion before air resistance or gravity.

    I like this code a lot, but it may be worth noting a similar effect could be created by linking a StaticBody2D and a RigidBody2D with a PinJoint2D if one just needed a swinging object.