Is there a reason why is_action_just_pressed does not work with lerp or linear.interpolation?

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By Grifft

I am moving an object with lerp and it activates only when clicking a mouse button (not long press). I am trying to to get aiming down the sight of a gun with just toggle instead of hold. But currently, clicking once only get the gun only up to a certain position, it does not move completely to the desired location. And in order to move it completely I have to spam the button press, which is not the intended behavior. Any idea how to fix? I tried using while loop but cant seem to get it work.

func _process(delta):
if Input.is_action_pressed("AimDownSights"):
	transform.origin = transform.origin.linear_interpolate(ADS_position, ADS_LERP * delta)
else:
	transform.origin = transform.origin.linear_interpolate(default_position, ADS_LERP * delta)
:bust_in_silhouette: Reply From: jgodfrey

The reason it can’t work when using is_action_just_pressed() is because that only fires in the single frame where the action was activated. And, since the linear_interpolate() call needs to execute repetitively to transition from the starting value to the ending value, it doesn’t accomplish much in a single frame.

To use is_action_just_pressed(), you’ll likely need to just set some flag variable when the action fires. Then, as long as that flag is set, you’d call the original linear_interpolate() code.

Finally, when you’ve reached the end of the interpolation, you’d unset the flag to stop the interpolation.

Hope that makes sense…

So far, I came up with this, ADS_state would be the flag variable, normally false.

var ADS_state = false

It’s value is changed to the opposite of it’s current value if ever Right-click is pressed.
Within the func process I made the condition that if ever the click is done the flag variable would indeed change value.

func _process(delta):
if Input.is_action_just_pressed("AimDownSights"):
	ADS_state = !ADS_state

Upon click I want the game to check the value of ADS_state. If ever it is set to true, then a while loop will be executed. This while loop is supposed to be the one that will keep calling the linear_interpolate() code.

if Input.is_action_just_pressed("AimDownSights"):
	ADS_state = !ADS_state
	
	if ADS_state == true:
		while ADS_state == true:
			transform.origin = transform.origin.linear_interpolate(ADS_position, ADS_LERP * delta)

The while loop is missing something that will stop it, I figured that that would be an if statement that would check if the position of the object I moved is equal to the position I want it where to be (ADS_position, it’s a Vector3 variable). I keep racking my head and went over tutorials but this is best what I thought of to stop the while loop from continuing if the interpolation is done

if transform.origin == ADS_position:
    ADS_state = false

This is the code I have so far:

func _process(delta):
if Input.is_action_just_pressed("AimDownSights"):
	ADS_state = !ADS_state
	
	if ADS_state == true:
		while ADS_state == true:
			transform.origin = transform.origin.linear_interpolate(ADS_position, ADS_LERP * delta)
			if transform.origin == ADS_position:
				ADS_state = false

I still did not achieve the behavior I want, instead, game gets unresponsive when I press the button that should fire all those code up. Any more help?

Grifft | 2022-04-17 03:51

I tried another thing, this solves exactly the problem as I can indeed get the object, in this case, a gun’s sight, position/move to the place I want it to from its default position. with just single button press.

var ADS_state = false
func _process(delta):
if Input.is_action_just_pressed("AimDownSights"):
		ADS_state = !ADS_state
	
	if ADS_state == true:
		transform.origin = ADS_position
		
	if ADS_state == false:
		transform.origin = default_position

Unfortunately this is also not a solution as the smooth transition from one location to another is not achieved. The gun just teleports to the location.

Grifft | 2022-04-17 05:36

You definitely don’t want (or need a while loop). That will result in “hanging” the engine until the loop completes because nothing else will be allowed to execute. The _process function is already called each frame, so that, effectively, is your loop.

This is completely untested, and may have typos as I just wrote it in this post, but I’d expect you need something like this (in it’s simplest form):

var ADS_state = false

func _process(delta):
    if !ADS_state && Input.is_action_just_pressed("AimDownSights"):
        ADS_state = true

    # If we've reached the expected position, set the state to false
    if transform.origin.is_equal_approx(ADS_position):
        ADS_state = false

    # Let the interpolation code run while the state is true
    if ADS_state:
        # do linear interpolation here

jgodfrey | 2022-04-17 14:16

Thanks a lot! I was able to implement your method and modified it a bit in order for it to also work if I want the interpolation to go the other way around and for it to only work if there was an interpolation done beforehand, for that, I have to use 2 flag variables for putting the gun up and then one for down. I have to do a bit of truth table for the ands for that :D.

I’ve noticed you used .is_equal_approx(), surprised that it exists, it’ll save me from trouble if I used == in the future. Because of it, I tried experimenting if the interpolation would be done exactly to the specified value (tried printing exactly the Vector3) but turns out its not, around 0.00-ish difference. So the approximately equal check is indeed nice.

Grifft | 2022-04-18 08:11