0
\$\begingroup\$

I'm trying to zoom in on an object once the raycast hits it in the Update method, I am using the Lerp method to make the camera movement smoother but it's not working properly. The object only lerps for a single frame.

I also tried using a coroutine but the result is still the same. I can't simply just place it outside the if statement because I only need to change the field of view once the raycast hits an object.

Here's a sample of my code :

if (Input.GetMouseButtonDown(0))
        {
            ray = mainCam.ScreenPointToRay(Input.mousePosition);
            if (Physics.Raycast(ray, out hit))
            {

                if (hit.collider.tag.Equals("Building")) 
                {

                    SetTarget(hit);
                    mainCam.fieldOfView = Mathf.Lerp(mainCam.fieldOfView, newFOV, Time.deltaTime * smooth);
                }
            }
         }
\$\endgroup\$
  • 1
    \$\begingroup\$ Can you elaborate a bit? "Not working properly" can mean a lot of things. What behaviour are you expecting, what happens instead? What have you tried? \$\endgroup\$ – Christian Aug 6 '18 at 9:21
  • \$\begingroup\$ if I use the Lerp method outside the if statement the zoom works properly (the fieldofview goes from 60 to another value that i previously calculated) however when it's in the if statement the fieldofview property barely changes. \$\endgroup\$ – Safa Aug 6 '18 at 9:28
  • \$\begingroup\$ There are three if statements. Which one are you referring to? \$\endgroup\$ – sam hocevar Aug 6 '18 at 9:33
  • \$\begingroup\$ All of them, I tried placing it in different places but the result is still the same. I would like to know why exactly isn't it working. \$\endgroup\$ – Safa Aug 6 '18 at 9:41
3
\$\begingroup\$

Your problem is very similar to this previous question "Why is Vector3.MoveTowards only working on a single frame?"

Like this other user, you're testing for a fleeting event: GetMouseButtonDown(0) is true only for one frame when the mouse button is pressed. If the mouse button is held down or released, it will be false on subsequent frames, so the outermost if block will be skipped entirely.

Inside this fleeting event response, you're calling on a math function as though you were kicking off a fire-and-forget tweening library - assuming that once you've called Lerp once, it will somehow remember to keep calling itself every frame afterward until the transition you're trying to complete is done.

But Lerp is not a coroutine that says "continue modifying this variable a little each frame until the destination value is reached" - it's just a math expression, like + or *, that says "blend these two numbers by this amount." It has no knowledge of transitions or frames. Every time you want to get new numbers out of it, you need to call it again.

But, we can easily use it to make the fire-and-forget tweening function you seem to want, like so:

// Store a reference to our coroutine so we can interrupt it if needed.
Coroutine _zooming;

// "IEnumerator" marks this as a routine that can pause & automatically 
// resume at a later time (say, advancing a little each frame)
IEnumerator ZoomTo(float newFov, float duration) {

    // Remember where we started from, so we can smoothly control the curve.
    float originalFov = mainCam.fieldOfView;
    float speed = 1f/duration;
    float t = 1.0f;

    // Loop multiple times until we've used up our full duration.
    do {
       // Blend one more step toward our destination value.
       t = Mathf.Clamp01(t - speed * Time.deltaTime);
       mainCam.fieldOfView = Mathf.Lerp(newFov, originalFov, t*t);

       // Return control to the main game thread for one frame, 
       // then resume next frame.
       yield return null;
    } while(t > 0f);

    // We've finished our work, so clear the stored coroutine to signal this.
    _zooming = null;
}

You'll note I'm doing something a little funny here and lerping from a weight of 1 down to 0, instead of 0 to 1. That's because I noticed you were using a formula that gives an ease-out blend, where the change starts fast and slows down toward the end (although your formula was implemented incorrectly - see here for how to fix that). So here I've mimicked that by using t*t as the blending weight, which levels off as it approaches zero.

Now when you want to kick off your zoom, you can write:

if(_zooming == null)
    _zooming = StartCoroutine(ZoomTo(newFov, zoomDuration));

The if above ensures the previous zoom has to finish before you can start a new one. Or, if you want to interrupt a zoom-in-progress instead, you can write

if(_zooming != null)
    StopCoroutine(_zooming);
_zooming = StartCoroutine(ZoomTo(newFov, zoomDuration));
\$\endgroup\$
0
\$\begingroup\$

The problem in your method is that lerp interpolates a value between 0 and 1 as a third parameter. In your method, Mathf.lerp, your third parameter is "constant" (it is not but close to constant). And because of that, your zoom only lerps one time. You need a time counter that goes from 0 to 1 to properly work.

if (Input.GetMouseButtonDown(0))
    {
        ray = mainCam.ScreenPointToRay(Input.mousePosition);
        if (Physics.Raycast(ray, out hit))
        {

            if (hit.collider.tag.Equals("Building")) 
            {

                SetTarget(hit);
                mainCam.fieldOfView = Mathf.Lerp(mainCam.fieldOfView, newFOV, timeCounter);
                timeCounter += Time.deltaTime*smooth; //<-- Declare and initialize this variable
            }
        }
     }

You will need to add some logic and play with the smooth variable to achieve some other effects. (Sorry about my english, i don't usually write :D)

\$\endgroup\$
  • \$\begingroup\$ Thank you for your help but it still lerps only once and the smoothing effect is gone (the transition is instantaneous) \$\endgroup\$ – Safa Aug 6 '18 at 11:16
  • \$\begingroup\$ The code you've shown above will exhibit the same problem as OP's, because you're still applying your change inside an if (Input.GetMouseButtonDown(0)) block that will be called only once for each click. So you zoom in for one frame, then halt on subsequent frames because Input.GetMouseButtonDown(0) returns false and the rest of your code is skipped. You're also presenting a mix of two styles of Lerp which might not behave the way you expect. \$\endgroup\$ – DMGregory Aug 6 '18 at 11:17
  • \$\begingroup\$ Oh, that's true, i thought it was Input.GetMouseButton(0) and not GetMouseButtonDown(0), it only triggers one time when clicking, nice point DMGregory \$\endgroup\$ – m5m7n5 Aug 6 '18 at 12:15
0
\$\begingroup\$

This solution seemed to work for me; I used lerp outside the if (Input.GetMouseButtonDown(0)) statement and created a variable "shouldLerp" that tracks when the camera is in the new position (ie the field of view property has changed).

\$\endgroup\$

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

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