0 votes

Hi guys. I've been working on something for a while and I'd appreciate some help if anyone is interested.

Example:
https://ibb.co/N1vHZ5P
Other Example:
https://ibb.co/QQJgV04

What I want to do is have a character 'jump' from a point, to another point, while moving through a point in between. I want this to be visualized as a parabola. I also want it to appear like a 'natural' jump.

I've done everything but making the jump look natural. What I'm doing so far is calculating two parabolas through code, one on the left side of the central point and one on the right, then adding both of their points to a Curve2D. I add the Curve2D as the curve property of a Path2D, slap a PathFollow2D on there and iterate over it using its unit_offset property. This causes the character to move along the parabola at a constant rate. So far so good.

But here's where I'm stuck. I want to move across this curve as if I'm jumping, meaning I want the character to slow down at the top of the curve and speed up again as he comes back down. I can't figure out a way to do this. Currently I'm just tweening from 0 to 1 and setting the PathFollow2D's unit_offset property to that. The problem is that if I interpolate using say, TRANS_QUAD and EASE_IN_OUT, the slowing takes place exactly at the x center of the curve. Which is a big problem for curves like the second one above, because the exact center is not the highest point.

What I've tried:

  • Literally split the arc in two (treating it as 2 curves rather than combining each half-parabola into one). This allows me to calculate the proper speed and do EASE_OUT on one side and EASE_IN on the other. This doesn't work though because when I interpolate the character through them there's a 'catch' at the central point, and there seems to be no way to get rid of that (removing the central point from one of the curves so it isn't 'duplicated' does the opposite of help)

I also haven't included any code because there's a decent amount. If you think it would help you to see it outside the conceptual realm, just let me know. I have separate scripts for the standard arc and the split arc I tried so let me know which you'd like to see.

Please feel free to give me any questions along the lines of "Why are you doing it that way, my guy?" This is just me working and I tend to overcomplicate things. As far as I can tell this is the only way to accomplish this.

Thank you again.

Godot version 3.3
in Engine by (27 points)
edited by

1 Answer

+1 vote

Hi,
check out this excellent tutorial:
https://www.forrestthewoods.com/blog/solving_ballistic_trajectories/

and look at his code on github:
https://github.com/forrestthewoods/lib_fts/blob/master/code/fts_ballistic_trajectory.cs

maybe one of his methods fits your needs

by (3,932 points)

I appreciate the resource!

i've found an implementation of it in one of my projects.
Haven't cleaned it from additional code but i think it gets the point

extends Spatial
class_name Grenade
var max_height:float = 6.0

var lateral_speed:float = 6.0
var divergence:Vector3 = Vector3(2,0,2)
var fire_velocity:Vector3 = Vector3()
var gravity:float = 0
var target:Vector3

var damage_group:String = ""
var damage_radius = 3
var damage:float = 5

func _ready():
    set_physics_process( false )

func _physics_process(delta):
    translate(fire_velocity * delta)
    fire_velocity.y -= gravity * delta
    if translation.y < target.y:
        print("greande reached destination")
        Main.__explosion.instance().detonate(global_transform.origin)
        damage()
        queue_free()


func target(group):
    damage_group = group
    return self

func damage():
    var units = get_tree().get_nodes_in_group(damage_group)
    for unit in units:
        if translation.distance_to(unit.global_transform.origin) < damage_radius + unit.radius:
            print("damage")
            unit.injure(damage)



func throw( origin:Vector3, nTarget:Vector3 ):
    target = nTarget + Vector3(rand_range(-divergence.x,divergence.x),rand_range(-divergence.y,divergence.y),rand_range(-divergence.z,divergence.z))
    if not solve_ballistic_arc_lateral( origin, target):
        queue_free()
        return

    translation = origin
    Main.level.add_child(self)
    set_physics_process( true )


func solve_ballistic_arc_lateral( proj_pos:Vector3, target_pos:Vector3):

    # Handling these cases is up to your project's coding standards
    if proj_pos != target_pos and lateral_speed > 0 and max_height > proj_pos.y:
        print_debug( "fts.solve_ballistic_arc called with invalid data");

    var diff:Vector3 = target_pos - proj_pos
    var diffXZ:Vector3 = Vector3(diff.x, 0, diff.z)
    var lateralDist:float = diffXZ.length()

    if (lateralDist == 0):
        return false

    var time:float = lateralDist / lateral_speed

    fire_velocity = diffXZ.normalized() * lateral_speed

    # System of equations. Hit max_height at t=.5*time. Hit target at t=time.
    #
    # peak = y0 + vertical_speed*halfTime + .5*gravity*halfTime^2
    # end = y0 + vertical_speed*time + .5*gravity*time^s
    # Wolfram Alpha: solve b = a + .5*v*t + .5*g*(.5*t)^2, c = a + vt + .5*g*t^2 for g, v
    var a:float = proj_pos.y;       # initial
    var b:float = max_height;       # peak
    var c:float = target_pos.y;     # final

    gravity = -4*(a - 2*b + c) / (time* time)
    fire_velocity.y = -(3*a - 4*b + c) / time

    return true

Thank you! I'll look this over.

Welcome to Godot Engine Q&A, where you can ask questions and receive answers from other members of the community.

Please make sure to read How to use this Q&A? before posting your first questions.
Social login is currently unavailable. If you've previously logged in with a Facebook or GitHub account, use the I forgot my password link in the login box to set a password for your account. If you still can't access your account, send an email to webmaster@godotengine.org with your username.