3
\$\begingroup\$

I'm trying to draw an arc out of dots that previews a KinematicBody2D's trajectory after a jump but said preview always falls short of the body's actual jump and I cannot figure out why. Below is an example gif of what I'm talking about.

Arc and jumping kinematic body

And this is the code I used in the example.

extends Node2D

var arcPoints = 40
var pointsPerSec = 12
var startYvelocity = -500
var velocity = Vector2(200, startYvelocity)
var gravity = 300

var startPosition = Vector2(96, 544)

func _draw():
    
    var totalY = startPosition.y
    var velY = startYvelocity
    var calcGravity = gravity/pointsPerSec
    
    for p in range(arcPoints):
        velY += calcGravity
        totalY += velY/pointsPerSec
        
        var point = Vector2(startPosition.x+(velocity.x/pointsPerSec)*(p+1), totalY)
        draw_circle(point, 2, 'fafafa')
    
func _ready():
    pass

func _physics_process(delta):
    velocity.y += gravity*delta
    get_child(0).move_and_slide_with_snap(velocity, Vector2(0.0, 1.0), Vector2(0.0, -1.0))
\$\endgroup\$
3
  • \$\begingroup\$ In trying to reproduce this in func _draw() I used var fps:float = 1.0 * Engine.iterations_per_second and overall to make more stuff more clear var startVelocity:Vector2 = Vector2(200, 100) and var startPosition:Vector2 = Vector2(96, 544) ... but could not fix it :-( \$\endgroup\$ – Clemens Tolboom Mar 22 at 14:11
  • \$\begingroup\$ @ClemensTolboom I was able to create an accurate arc based on keelhaul's answer. If you want, when I get next have the opportunity, I can add my own answer showing the working script and why it works. \$\endgroup\$ – Javlin Joslin Mar 23 at 1:00
  • \$\begingroup\$ Cool. Not sure to change the accepted answer from @Keelhaul but yeah I like an Godot answer ;-) \$\endgroup\$ – Clemens Tolboom Mar 24 at 16:15
3
\$\begingroup\$

By doing a preview with time increments different from your process, you're bound to have differences. For example, consider these extreme cases:

  • You simulate a preview with 1 point per second: on the first step, your Yvelocty becomes -500 + 300 = -200, making it as if you started your jump with a speed of only 200 while it should be 500 in reality.
  • You process the jump at 1000 frames per second: on the first step, your Yvelocty is -500 + 0.3 = -499.7, almost the "real" starting velocity.

Since actual gravity works in continuous time, you'll never have a "true" path by doing simple increments, even though the process framerate will be good enough not to notice. So you can either:

  • Do your preview with the same time increments as your process. However, you don't know it precisely unless you fix your physics framerate, and it's really inefficient.
  • Do your preview (and even your process) by applying the math on each point with a simple equation:
    \$y(t) = y + vy * t + \frac{1}{2} a t^2\$ (Here in your example with y = 544, vy = -500, a = 300)

I let you guess wich solution is the best ;)

\$\endgroup\$
1
\$\begingroup\$

I wanted to show what applying Keelhaul's answer to a godot script would look like to try and add clarity to what the issue was. The Following is my modified script.

extends Node2D

var arcPoints = 120.0 #total number of points to draw
var arcFPS = 60.0
var PPF = 2.0 #points per frame

var startYvelocity = -500.0
var velocity = Vector2(200.0, startYvelocity)
var gravity = 300.0

var startPosition = Vector2(96.0, 544.0)

func _draw():
    
    var totalY = startPosition.y
    var velY = startYvelocity
    var calcGravity = gravity/arcFPS
    var stepsCompleted = 0

    for p in range(arcPoints*PPF):
        velY += calcGravity
        totalY += velY/arcFPS
        stepsCompleted += 1

        if stepsCompleted == PPF:
            stepsCompleted = 0
            var point = Vector2(startPosition.x+(velocity.x/arcFPS)*(p+1), totalY)
            draw_circle(point, 2, 'fafafa')

func _ready():
    pass

This script is very similar to the original question but doesn't just keep track of the change in velocity and position at each of the points but also keeps track of the movement between the points. This is important because the more points of movement we skip the more cumulative effect of said movement we lose. This can easily be shown by changing the arcFPS variable, the further it is from the physics frame rate the more inaccurate our arc becomes.

enter image description here

^Script with arcFPS and physicsFPS both at 60.

enter image description here

^Script with arcFPS at 12 and physicsFPS at 60. Notice how it looks almost the same as it did in the initial question.

Keep in mind that this way of doing it is a balancing act between accuracy and efficacy, try to find a medium that is accurate enough for your liking.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.