Hey, I’m using a cinemachine free look camera for my character. In the game I can select an item, look around and then press a button to refocus the camera to the selected item. Now, this seemed really easy when I initially looked at it but the results are not great. My approach is to find the rotation the camera should have and use the euler degrees to set the m_xAxis field of the free look camera. When I do this via code it results in a weirdly snapping movement (can be seen in the video).
At the end the camera does not like this and I have no clue on how to get y axis value (that needs to be between 0 and 1). Our camera uses a screen x offset to 0.2 that complicates the calculations.
My question is: is there a built in method I’m missing? Is there a simpler way (maybe with camera transitions) to do it? Any help would be appreciated, thanks.
I’m using CM version 2.3.4.
Attached a package to reproduce the problem in a sandbox scene and a video to quickly see the issue.
5348892–540186–camera snap package.unitypackage (6.67 KB)
It’s possible I’m not understanding your problem correctly, but might it work to simply change the LookAt target?
private void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
m_freeLookCamera.m_LookAt = m_target;
}
if (Input.GetKeyUp(KeyCode.Space))
{
m_freeLookCamera.m_LookAt = transform;
}
}
@marc_tanenbaum Firstly thanks for your answer. I did try that solution but it just makes the camera rotate to look at the target. What I would like to achieve is to have the camera always behind the player as shown in the video and have the camera moving around the player (and rotating to keep the look at target of course) until the position and rotation makes the camera look at the target (the center of the screen should point to the target’s position). Basically I want the same behaviour as it is driven by an axis, but as input I have a direction vector, or a rotation.
Hey @MattiaPez thanks for packaging that up, you saved a lot of back-and-forth.
What you’re trying to do is a little tricky, but possible. You can use the delta between current camera forward and desired camera forward to deduce the direction in which you need to move the axes, and move them a bit in that direction each frame until you get there.
Here is your Example.cs script, modified to get the effect that (I think) you want:
using System.Collections;
using Cinemachine;
using Cinemachine.Utility;
using UnityEngine;
public class Example : MonoBehaviour
{
public Transform m_target;
public CinemachineFreeLook m_freeLookCamera;
private bool m_rotating;
// Update is called once per frame
private void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
StartCoroutine(LookAtTarget(GetFreeLookYRange()));
}
}
/// <summary>
/// Call this to cancel the rotation, e.g. if the user rotates the FreeLook
/// </summary>
public void CancelRotation()
{
m_rotating = false;
}
/// <summary>
/// This is supposed to snap (or animate) the camera axis to look at the target
/// </summary>
private IEnumerator LookAtTarget(float yRange)
{
// This constant controls the speed of the lerp. 1 is fastest.
const float lerpAmount = 0.3f;
m_rotating = true;
while (m_rotating && m_target != null && m_freeLookCamera != null)
{
// How far away from center is the target?
var state = m_freeLookCamera.State;
var dir = m_target.position - state.CorrectedPosition;
var rot = state.CorrectedOrientation.GetCameraRotationToTarget(dir, state.ReferenceUp);
// Record the current settings
var current = new Vector2(m_freeLookCamera.m_YAxis.Value, m_freeLookCamera.m_XAxis.Value);
// First do the yaw
m_freeLookCamera.m_XAxis.Value = (current.y + rot.y * lerpAmount) % 360;
// Then do the pitch
m_freeLookCamera.m_YAxis.Value = Mathf.Clamp01(current.x + lerpAmount * rot.x / yRange);
// Are we there yet?
if (Mathf.Abs(m_freeLookCamera.m_YAxis.Value - current.x) < 0.01f
&& Mathf.Abs(m_freeLookCamera.m_XAxis.Value - current.y) < 0.01f)
{
m_rotating = false; // yes, close enough
}
yield return null;
}
}
/// <summary>
/// Deduces the Y axis angle range from the freeLook orbits
/// </summary>
private float GetFreeLookYRange()
{
if (m_freeLookCamera == null)
return 1;
var orbit = m_freeLookCamera.m_Orbits[2];
var d0 = new Vector3(0, orbit.m_Height, orbit.m_Radius);
orbit = m_freeLookCamera.m_Orbits[0];
var d1 = new Vector3(0, orbit.m_Height, orbit.m_Radius);
return Vector3.Angle(d0, d1);
}
}
1 Like
@Gregoryl That worked quite good for us, thank you very much for your answer! Would be great to have something like this built in, I guess this is a common feature. Amazing work anyway!
1 Like