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)
But for some reason it seems to be "falling downwards" (increased gravity so it's visible faster):
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
Edit:
This is how I came up with the equation:
(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:
This piles up in the long run and gives the illusion that the pendulum is stretching downwards:
(You can think of the universe having an infinite frame rate :P)
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.