Getting FreeLook Camera to jump behind player

Hello, I’m trying to set up a feature where a camera will instantly jump to behind the player, like a lot of 3rd person games do when you press the right control stick, for example.

I came up with something that basically works, but it doesn’t provide for a lot of control. Basically the goal is to reposition the camera so that its transform.forward, projected onto the ground plane, is the same as the player’s transform.forward, also projected onto the ground plane.

https://vimeo.com/366877479

(You can see this work when the camera rotates but the mouse is not moving)

The difficulty I’m having is that it seems that the only way I’ve found to swing the camera around is by overriding CinemachineCore.AxisInputDelegate. This works, but the output is not predictable - or at least I don’t know how to predict it.

What I currently do is, get the signed angle between the camera and the desired position, which determines if I need to swing left or right. Then I override the axis input with my custom function. Then I start a coroutine, which tests whether the camera is inside a tolerance, or if the dot product of camera.transform.forward and player.transform.forward is less than the previous frame (which means we’ve overshot) or if it’s greater than a tolerance value, say 0.95f. Then I return the axis input function to its original reference.

The custom input function basically just multiplies a speed value times the sign of the original angle (left or right). It ignores the Y axis.

Like I said, this does work, but some problems with this:

  • I can’t set a duration. Normally I would let the user set a duration, but because I am rotating some amount that I don’t seem to be able to control per frame, I don’t know how long it’s going to take. I can set a speed multiplier, but this is still not optimal.

  • The motion is robotic. I have introduced an animation curve, but the problem is determining the input value of the curve because I don’t have the total duration. What I’m doing instead is using the progress from the starting dot product to 1f, but this makes it hard to control because small values will cause very little change between frames, and curving sections are more abrupt.

  • Eventually I would like to override the Y axis too, to get the camera to a specified distance behind the player, or at least a specified distance on the curve that bridges the three rigs. However, because the method starts and stops based on guessing and checking the X value, I can’t couple the end time of the Y adjustment to the end time of the X adjustment. In other words, maybe I’ll be at the right angle well before or well after I’m at the right distance.

  • Its unclear how the output value is consumed. I haven’t been able to determine what value corresponds to one degree of movement, for example, and I think it is not intended to enable this - probably for smoothing purposes.

Any thoughts on how to remedy this? Any possibility this functionality will be introduced into the controller? Or am I missing some silver bullet?

FreeLook does have this built-in. Look at the “Recentering” portions of the Axis Control section of the FreeLook inspector:

5077928--499466--upload_2019-10-17_10-5-44.png

Provided that your binding mode isn’t SimpleFollow, you can set it up to recenter to be behind the target’s forward axis, which I think is what you want. That’s what “Target Forward” heading definition means. You can set the recentering time to take as long as you want.

You can manually trigger a recentering by calling the FreeLook’s m_XAxis (or m_YAxis)'s RecenterNow() method. EDIT: instead of m_XAxis and m_YAxis, use FreeLook.m_RecenterToTargetHeading and FreeLook.m_YAxisRecentering.

What recentering does is to smoothly lerp the Axis.Value member to the “centered” value, which is chosen depending on the heading. If you don’t use the built-in recentering, you can write your own code to do the same thing. No need to go through CinemachineCore.AxisInputDelegate.

For SimpleFollow or if your heading definition is not TargetForward, the “recentered” value will not necessarily be 0. In that case you will have to manually calculate an appropriate value to move towards, as you have done. But then all you need to do is lerp the axis.Value member directly. To make it less mechanical, you can use this function: Unity - Scripting API: Mathf.SmoothDamp

2 Likes

Thanks for wading through my post. That was the issue - I did have it set to SimpleFollow binding mode. Actually, the player’s control scheme is sometimes set to camera space, and so most of the other schemes cause a feedback loop that causes the camera to spin uncontrollably for most of these modes. But World Space does work! Thanks again.

Just a follow-up, if you happen to know. I’ve tried this code:

                Debug.Log("Recenter!");
                if (centerX)
                {
                    cam.m_XAxis.m_Recentering.m_RecenteringTime = 0.1f;
                    cam.m_XAxis.m_Recentering.m_WaitTime = 0f;
                    cam.m_XAxis.m_Recentering.m_enabled = true;
                    cam.m_XAxis.m_Recentering.RecenterNow();
                    Debug.Log("\tX");
                }
                if (centerY)
                {
                    cam.m_YAxis.m_Recentering.m_RecenteringTime = 0.1f;
                    cam.m_YAxis.m_Recentering.m_WaitTime = 0f;
                    cam.m_YAxis.m_Recentering.m_enabled = true;
                    cam.m_XAxis.m_Recentering.RecenterNow();
                    Debug.Log("\tY");
                }
                //cam.m_RecenterToTargetHeading.m_enabled = true;
                //cam.m_RecenterToTargetHeading.RecenterNow();

And it appears to do nothing, However, if I uncomment the last two lines I do get the camera swinging back around (although the Y axis doesn’t seem to change). Even though I do get the Debug logs. The inspector values (including for enabled) don’t seem to change either. Am I missing something?

Yes, I think I misled you, sorry about that. While the axes do have Recentering members, in the FreeLook, for historical reasons, they’re not used. That’s why setting them has no effect. Instead, use FreeLook.m_RecenterToTargetHeading and FreeLook.m_YAxisRecentering. Then it should work.

3 Likes

Ah, got it. That works, thanks.

1 Like