Search code examples
inputtimedeltagodotlinear-interpolation

How to use linear interpolation inside GODOT


I'm trying to make a smooth camera movement using linear interpolation, but looks like I cannot pass Delta with it.. not sure if that is the problem. Instead it just snaps its position

CameraBase is only a spatial 3D, and the actual camera Node is his child node

func _unhandled_input(event):
    if event.is_action_released("ui_focus_next"):
        i += 1
        if i >= allies.size():
            i = 0
        pointer.translation = allies[i].translation + Vector3(0,5,0)
        focus_camera(allies[i])


func focus_camera(target):
    var move = $CameraBase.translation.linear_interpolate(target.translation, CAMERA_SPEED)
    $CameraBase.set_translation(move)

It would work fine if was using Input instead of unhandle, but I read that is not the proper way to do it


Solution

  • Let us have a look at the documentation for linear_interpolate:

    Vector3 linear_interpolate ( Vector3 to, float weight )

    Returns the result of the linear interpolation between this vector and to by amount t. weight is on the range of 0.0 to 1.0, representing the amount of interpolation.

    As you can see the second parameter is a weight. If it is 0.0, you get a vector without modification. If it is 1.0, you get the vector you pass as first parameter. Any value between 0.0 and 1.0 will give a point in between.

    We can confirm this in in "Vector Interpolation":

    Here is simple pseudo-code for going from point A to B using interpolation:

    func _physics_process(delta):
       t += delta * 0.4
    
       $Sprite.position = $A.position.linear_interpolate($B.position, t)
    

    It will produce the following motion:

    Circle moving from pint A to point B, as t goes from 0 to 1

    To reiterate, a weight of 0.0 give you the first position and a weight 1.0 gives you the second position. For the motion you are meant to pass a value that varies from 0.0 to 1.0 over time.

    Which is why the example uses _physics_process and delta (which is in in seconds, by the way). Every physics frame, Godot calls _physics_process, where the code computes the new value for the weight (the t variable in this case) using the elapsed time since the last time Godot called _physics_process (that is delta).

    If you want to use linear_interpolate to create motion, you must use it similar fashion. Calling it from _process, _physics_process, or from the handler the timeout signal of a Timer or similar solution.


    However, I'm going to suggest an entirely different approach: use a Tween node.

    You can add a Tween node and use it for your interpolation. You can add the node in the editor, and take a reference like this:

    onready var tween:Tween = $Tween
    

    Or you can create it from code like this:

    var tween:Tween
    
    func _ready():
        tween = Tween.new()
        add_child(tween)
    

    Either way, once you have a variable with the Tween, we can use it like this:

        tween.interpolate_property($CameraBase, "translation",
            $CameraBase.translation, target.translation, duration)
    

    That can be in _input or _unhandled_input or anywhere, it does not matter. Nor does it need delta. And you don't need to call it multiple times (which is what you have to do to get motion with linear_interpolate, in case that wasn't clear).

    The code is telling the Tween node to interpolate the translation property of $CameraBase. The values will go from the current value of $CameraBase.translation to target.translation. And the motion will take duration seconds.

    We can tweak this to use speed:

        var distance = $CameraBase.translation.distance_to(target.translation)
        var duration = distance / CAMERA_SPEED
        tween.interpolate_property($CameraBase, "translation",
            $CameraBase.translation, target.translation, duration)
    

    This assumes CAMERA_SPEED is units per second.

    Also let us not forget to call start:

        tween.start()
    

    And, as as bonus, interpolate_property and the other interpolation Tween methods allow you to specify the kind of interpolation (it is linear by default), by passing a TransitionType and a EaseType.

    By the way, you can check is_active to find out if the Tween is running, and there are also stop and stop_all methods.