1

Right now I have 2 Cameras: the main camera displays the gun at its normal state and a second camera is attached to the gun (the gun is a child of the main camera) and when toggled it looks through the scope of the gun and increases the field of view.

Heres a visual for a better understanding:

enter image description here

enter image description here

Now if I were to just toggle the second camera on and turn the main camera off, this would work splendid, but it's not very ideal. You should only have 1 camera per scene.

So I want to Lerp the position of the camera to look through the scope and manually decrease the fieldofview. So I have written the following script:

[RequireComponent(typeof(Camera))]
public class Zoom : MonoBehaviour {

    private Transform CameraTransform = null;
    public Transform ZoomedTransform;

    private bool zoomed = false;

    void Start () {
        CameraTransform = Camera.main.transform;
    }

    // Update is called once per frame
    void Update () {
        if (Input.GetKey (KeyCode.LeftShift)) 
        {
            CameraTransform.position = Vector3.Lerp (
                CameraTransform.position, 
                CameraTransform.position + ZoomedTransform.position, 
                5f * Time.deltaTime
            );
            CameraTransform.Rotate(ZoomedTransform.rotation.eulerAngles);
        }
    }
}

The problem with this is that it doesn't work: when I hit the zoom button, the camera speeds through the scene at the speed of light and it's hard to tell exactly what is going on.

Could anyone give me some insight as to what I'm doing wrong? I think it is something to do with the parent-child relationship, but even when I've tried using static values, I cannot seem to replicate the correct solution.

Hierarchy:

enter image description here

nawfal
  • 70,104
  • 56
  • 326
  • 368
Kyle Jensen
  • 419
  • 9
  • 27

3 Answers3

1

Your lerp target is relative to the camera's current position, so it's constantly moving. This is the target you have:

CameraTransform.position + ZoomedTransform.position

This means that as your camera moves to get closer to this position, the camera's new position causes the destination to change. So your camera keeps moving forever.

Your destination should be ZoomedTransform.position. No addition is necessary because position is in world coordinates. (And when you actually need to convert between spaces, check out TransformPoint and similar methods.)

31eee384
  • 2,748
  • 2
  • 18
  • 27
1

(This answer operates under the assumption that ZoomedTransform is a relative transformation, and not the absolute position of the camera as suspected by 31eee384's answer.)

I think there are a couple issues with your code. I'll tackle them individually so they're easier to understand, but they both relate to the following line:

CameraTransform.position = Vector3.Lerp (CameraTransform.position, CameraTransform.position + ZoomedTransform.position, 5f * Time.deltaTime);

First, let's look at how you're using Vector3.Lerp(). For the third argument of Vector3.Lerp(), you're supplying 5f * Time.deltaTime. What exactly does this value work out to? Well, the standard framerate is about 60 FPS, so Time.deltaTime = ~1/60. Hence, 5f * Time.deltaTime = 5/60 = ~0.0833.

What is Vector3.Lerp() expecting for the third argument, though? According to the documentation, that third argument should be between 0 and 1, and determines whether the returned Vector3 should be closer to the first or second given Vector3. So yes, 5f * Time.deltaTime falls within this range, but no interpolation will occur - because it will always be around ~0.0833, rather than progressing from 0 to 1 (or 1 to 0). Each frame, you're basically always getting back cameraPos + zoomTransform * 0.0833.

The other notable problem is how you're updating the value of CameraTransform.position every frame, but then using that new (increased) value as an argument for Vector3.Lerp() the next frame. (This is a bit like doing int i = i + 1; in a loop.) This is the reason why your camera is flying across the map so fast. Here is what is happening each frame, using the hypothetical result of your Vector3.Lerp() that I calculated earlier (pseudocode):

// Frame 1
cameraPosFrame_1 = cameraPosFrame_0 + zoomTransform * 0.0833;
// Frame 2
cameraPosFrame_2 = cameraPosFrame_1 + zoomTransform * 0.0833;
// Frame 3
cameraPosFrame_3 = cameraPosFrame_2 + zoomTransform * 0.0833;
// etc...

Every frame, zoomTransform * 0.0833 gets added to the camera's position. Which ends up being a really, really fast, and non-stop increase in value - so your camera flies across the map.

One way to address these problems is to have variables that stores your camera's initial local position, zoom progress, and speed of zoom. This way, we never lose the original position of the camera, and we can both keep track of how far the zoom has progressed and when to stop it.

[RequireComponent(typeof(Camera))]
public class Zoom : MonoBehaviour {

    private Transform CameraTransform = null;
    public Transform ZoomedTransform;
    private Vector3 startLocalPos;
    private float zoomProgress = 0;
    private float zoomLength = 2; // Number of seconds zoom will take

    private bool zoomed = false;

    void Start () {
        CameraTransform = Camera.main.transform;
        startLocalPos = CameraTransform.localPosition;
    }

    // Update is called once per frame
    void Update () {
        if (Input.GetKey (KeyCode.LeftShift)) 
        {
            zoomProgress += Time.deltaTime;

            CameraTransform.localPosition = Vector3.Lerp (startLocalPos, startLocalPos + ZoomedTransform.position, zoomProgress / zoomLength);
            CameraTransform.Rotate(ZoomedTransform.rotation.eulerAngles);
        }
    }
}

Hope this helps! Let me know if you have any questions. This answer does ramble a little, so I hope you don't have any trouble getting the important points from it.

Serlite
  • 12,130
  • 5
  • 38
  • 49
  • I have implemented this and the lerp does stop correctly but it still lerps waaay too far away, puts my hands and gun on the other side of the map almost. Is it something to do with the hierarchy and the parent-child relationships? Or the local positioning vs global positioning? – Kyle Jensen Oct 09 '15 at 22:14
  • @KyleJensen Oh sorry! You're absolutely right, it is due to local vs global positioning. I was still modifying `CameraTransform.position`, where I should have actually been modifying `CameraTransform.localPosition`. Let me know if the updated answer helps. – Serlite Oct 10 '15 at 04:00
  • Ive changed everything to localPosition and its now not moving at all.. :S – Kyle Jensen Oct 10 '15 at 18:47
  • Hm...before we go further, I just want to verify - what exactly is `ZoomedTransform.position`? – Serlite Oct 10 '15 at 18:59
  • If you look at the hierarchy that I included with the question, I had two cameras and I originally just switched between the two cameras but it wasn't ideal. Instead I want to lerp the one camera position to the position of the other camera position. I removed the camera so that it was only a transform. I think the problem is that its a child of the main camera, but I don't know how else I can do it and make it always relative to the gun – Kyle Jensen Oct 11 '15 at 00:17
  • I am open to different Idea's.. Im not sure if the camera should be relative to the gun or if the gun should be relative to the camera or what.. Ive tried moving the gun up to the camera and just nothing seems to be ideal – Kyle Jensen Oct 11 '15 at 00:59
  • Oh uuuh...huh, I see. Let's see, maybe using `ZoomedTransform.localPosition` rather than `ZoomedTransform.position` would also be a necessary change. An alternative approach would be to make the zoom an animation rather than driven through code, and have the animation run forward/backward. (But that's a very different approach, let's focus on the current one for now.) – Serlite Oct 11 '15 at 03:34
  • I actually figured it out last night! The way I was going about the hierarchy was all wrong. Instead of moving the camera I moved the gun up to the camera and everything works fine and dandy! The code you provided was very helpful in coming to this conclusion. Thanks – Kyle Jensen Oct 11 '15 at 15:53
  • Great! I'm glad I could help you out. – Serlite Oct 11 '15 at 19:59
0

It has been a while since I have done anything in Unity, but I think it is processing the Lerp function at frame time and not at actual time. You will need to call it in another function that is not being processed at frame time.

jagler
  • 185
  • 1
  • 6
  • This isn't the cause of the problem, it should be stopping at the position – Kyle Jensen Oct 09 '15 at 18:06
  • Sorry, I think I misunderstood what was happening. `CameraTransform.position = Vector3.Lerp (CameraTransform.position, CameraTransform.position + ZoomedTransform.position, 5f * Time.deltaTime);` you are constantly adding `ZoomedTransform.position `to `CamerTransform.position` when you hit the shift. I think you need an If statement to check if its at the correct position, if not then Lerp – jagler Oct 09 '15 at 18:22
  • Nevermind, just read the Unity api for it. I think all you need to do is change out `CameraTransform.position + ZoomedTransform.position` for just `ZoomedTransform.position` [Unity Lerp api](http://docs.unity3d.com/ScriptReference/Vector3.Lerp.html) I think it is just over shooting where the other camera is located. – jagler Oct 09 '15 at 18:29
  • When I do this the camera physically moves off the players body – Kyle Jensen Oct 09 '15 at 19:09
  • I edited the question to show the hierarchy of the player and cameras – Kyle Jensen Oct 09 '15 at 19:11
  • I'm not sure if you want to use 2 camera's for this. The Lerp will move the Main camera to the location of the zoom camera. Maybe to get the desired effect you could use a temp 3rd camera to do the movement between each. I think a better solution would be to just have an empty object hold the position of the zoomed camera and another empty object hold the position of the main camera. then lerp between the 2 points when shift is held down. when its not down then check to make sure the camera is back to the starting location. – jagler Oct 09 '15 at 19:23
  • Ive tried this but I must not be going about it right because I always get this sort of problem where the camera does weird movements. – Kyle Jensen Oct 09 '15 at 19:36
  • That maybe because the `5f * Time.deltaTime` should be between 0 and 1 according to the documentation. – jagler Oct 09 '15 at 19:42
  • @jagler `Time.deltaTime` is a very small number, so that *will* be between 0 and 1. – 31eee384 Oct 09 '15 at 19:45