I have a game I’m working on where I’d like to adjust the FOV to get tighter as the enemy approaches, and it technically works. However, there is always a ‘popping’ in and out (even if I Lerp). I know I’m doing something wrong, but I can’t quite figure it out. Please let me know what I should be doing differently. I’m relatively new to Unity, so please bear with me. Thank you.
My script:
using Cinemachine;
using UnityEngine;
public class FOVChanger : MonoBehaviour
{
[SerializeField] private NoiseSettings idleCameraNoiseSettings;
[SerializeField] private NoiseSettings escapeCameraNoiseSettings;
[SerializeField] private float desiredFOV;
[SerializeField] private float initialFOV;
private CinemachineVirtualCamera cine;
private bool outOfRange = true;
private CinemachineBasicMultiChannelPerlin cineNoise;
private It enemy;
// Start is called before the first frame update
void Start()
{
cine = GetComponent<CinemachineVirtualCamera>();
enemy = FindObjectOfType<It>();
initialFOV = cine.m_Lens.FieldOfView;
desiredFOV = initialFOV;
cineNoise = cine.GetCinemachineComponent<CinemachineBasicMultiChannelPerlin>();
}
private void Update()
{
// Get distance between camera and player
float currentDistance = Vector3.Distance(enemy.transform.position, transform.position);
// Check if enemy is close
if (currentDistance <= 15)
{
print("In range block running");
outOfRange = false;
// Animate FOV based on distance
desiredFOV = (currentDistance + 15) / 15 * (initialFOV / 2);
cineNoise.m_NoiseProfile = escapeCameraNoiseSettings;
cineNoise.m_AmplitudeGain = (initialFOV / desiredFOV) / 3;
}
else if (!outOfRange)
{
print("out of range block running");
outOfRange = true;
// TODO - reset camera to initialFOV
desiredFOV = initialFOV;
cineNoise.m_NoiseProfile = idleCameraNoiseSettings;
cineNoise.m_AmplitudeGain = 1f;
}
UpdateFOV();
}
void UpdateFOV()
{
if (desiredFOV == initialFOV && cine.m_Lens.FieldOfView == initialFOV) return;
print("FOV mismatch");
cine.m_Lens.FieldOfView = Mathf.Lerp(cine.m_Lens.FieldOfView, desiredFOV, Time.deltaTime * 5);
}
}
So - I was able to ascertain that it actually wasn’t the FOV being set that was causing the issue, but the setting of the noise profiles that was causing it to shift. How would I switch profiles programmatically while animating the amplitude back down to 1? A coroutine?
You should not switch profiles dynamically unless you first lerp the amplitude gradually down to 0, and then lerp it back up afterwards. Otherwise, you will likely get a camera pop.
Normally, one doesn’t dynamically change profiles. Instead, set up 2 vcams, one with each profile, and activate one or the other allowing the CMBrain to blend smoothly between them.
Would creating the 2nd virtual camera be the ‘best practice?’ Or would hooking into the script to modify the amplitude be the better (read: optimal) solution?
I just want to be able to switch between the standard handicam movement of the basic noise profile, versus the explosion movement (to convey shock). Can you edit a profile to have one start at one basic loop and switch to another? How are these different camera movement mechanics usually handled in Unity?
I meant the code in your Update method in FOVChanger class.
Best practise would be to use one vcam for one concept. In your case, having two virtual cameras. One for standard handicam and one for explosive movement.
Thank you so much! One final question - is it bad practice to continuously set values that may be the same between updates? For instance, setting the amplitude based on distance. If they’re outside the range it would keep setting the amplitude to 1. Is that bad? Should I set a flag?
Is that broadly applicable? I find myself doing checks on values before setting them. Is it a relatively safe practice for any value to just keep setting it? Does Unity do some optimization under the hood to maximize performance?
In the case of Cinemachine, it’s just pure c#. You can look at the code. It’s possible that in other cases there is logic that gets triggered by setting - you have to check the code in each case.