Search Unity

How to enable Camera Noise via script?

Discussion in 'Cinemachine' started by IgorDemchuk, Sep 12, 2017.

  1. IgorDemchuk

    IgorDemchuk

    Joined:
    Mar 9, 2015
    Posts:
    21
    Hey!
    Really love CM!

    For my 2D mobile game I need to be able to turn on a camera shake for about 0.5 sec. Before changing my camera to CM I used to do that using a script.
    I know I can shake a camera using Noise, but I need to shake it for a short period of time.

    The question is how can I apply Noise settings to vcam for exact period of time?
    1. apply Noise settings via script?
    2. blend between vcam w/o noise and vcam w/ noise?
    3. change Amplitude Gain and Frequency Gain of the Noise setting from 0 to 1 via script?
    What is the best way?

    Another question is how can I do any of this via script? Scripting references don't help me a lot. ScriptingExample.cs doesn't seem to be very helpful too.

    I'm using CM 2.1.

    Thank you!



    UPD 09.15.2017

    Here's the answer:
    1. Create CM VCam
    2. Make sure to use Basic Multi Channel Perlin in Noise Component (see attached screenshot)
    3. Add VCamShake.cs on the VCam
    4. From other script call VCamShake.Shake(0.3f) to shake camera for 0.3 sec. Insert any time you want.
    Code (CSharp):
    1. using System.Collections;
    2. using UnityEngine;
    3. using Cinemachine;
    4.  
    5. public class VCamShake : MonoBehaviour {
    6.  
    7.     public static VCamShake instance;
    8.  
    9.     private CinemachineVirtualCamera vcam;
    10.     private CinemachineBasicMultiChannelPerlin noise;
    11.     private float _timeAtCurrentFrame;
    12.     private float _timeAtLastFrame;
    13.     private float _fakeDelta;
    14.  
    15.  
    16.     void Awake()
    17.     {
    18.         instance = this;
    19.         vcam = GetComponent<CinemachineVirtualCamera>();
    20.         noise = vcam.GetCinemachineComponent<CinemachineBasicMultiChannelPerlin>();
    21.     }
    22.  
    23.     void Update()
    24.     {
    25.         // Calculate a fake delta time, so we can Shake while game is paused.
    26.         _timeAtCurrentFrame = Time.realtimeSinceStartup;
    27.         _fakeDelta = _timeAtCurrentFrame - _timeAtLastFrame;
    28.         _timeAtLastFrame = _timeAtCurrentFrame;
    29.     }
    30.  
    31.     public static void Shake(float duration)
    32.     {
    33.         instance.StopAllCoroutines();
    34.         instance.StartCoroutine(instance.cShake(duration));
    35.     }
    36.  
    37.     public IEnumerator cShake(float duration)
    38.     {
    39.         while (duration > 0)
    40.         {
    41.             noise.m_AmplitudeGain = 1f;
    42.             duration -= _fakeDelta;
    43.             yield return null;
    44.         }
    45.  
    46.         noise.m_AmplitudeGain = 0f;
    47.     }  
    48. }
    49.  
     

    Attached Files:

    Last edited: Sep 15, 2017
    ayroldes likes this.
  2. Gregoryl

    Gregoryl

    Unity Technologies

    Joined:
    Dec 22, 2016
    Posts:
    7,711
    The noise settings themselves are assets, so modifying them at runtime is not recommended. The best way is to pulse the amplitude of the noise component.

    You can access the amplitude from script like this:
    CinemachineVirtualCamera vcam;
    vcam.GetCinemachineComponent<CinemachineBasicMultiChnnelPerlin>().m_Amplitude = bla;
     
    IgorDemchuk likes this.
  3. IgorDemchuk

    IgorDemchuk

    Joined:
    Mar 9, 2015
    Posts:
    21
    Hey, Gregoryl!

    Code (CSharp):
    1. vcam.GetCinemachineComponent<CinemachineBasicMultiChannelPerlin>().m_AmplitudeGain = 1f;
    This gives me a Null Reference error.
     
  4. Gregoryl

    Gregoryl

    Unity Technologies

    Joined:
    Dec 22, 2016
    Posts:
    7,711
    Like any other Unity component, you have to add it before you can reference it. vcam.AddCinemachineComponent<>() does it from script, else do it in the inspector.
     
    IgorDemchuk likes this.
  5. IgorDemchuk

    IgorDemchuk

    Joined:
    Mar 9, 2015
    Posts:
    21
    Hey, Gregoryl!

    Code (CSharp):
    1. vcam.AddCinemachineComponent<CinemachineBasicMultiChannelPerlin>();
    Gives a Null Reference too.

    I guess I should explain better what is happening.

    I have this script on CM vcam1
    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using Cinemachine;
    5.  
    6. public class VCamShake : MonoBehaviour {
    7.  
    8.     public static VCamShake instance;
    9.  
    10.     private CinemachineVirtualCamera vcam;
    11.     private float _timeAtCurrentFrame;
    12.     private float _timeAtLastFrame;
    13.     private float _fakeDelta;
    14.  
    15.  
    16.     void Awake()
    17.     {
    18.         instance = this;
    19.         vcam.AddCinemachineComponent<CinemachineBasicMultiChannelPerlin>();
    20.     }
    21.  
    22.     void Start()
    23.     {
    24.  
    25.     }
    26.  
    27.     void Update()
    28.     {
    29.         // Calculate a fake delta time, so we can Shake while game is paused.
    30.         _timeAtCurrentFrame = Time.realtimeSinceStartup;
    31.         _fakeDelta = _timeAtCurrentFrame - _timeAtLastFrame;
    32.         _timeAtLastFrame = _timeAtCurrentFrame;
    33.     }
    34.  
    35.     public static void Shake(float duration)
    36.     {
    37.         instance.StopAllCoroutines();
    38.         instance.StartCoroutine(instance.cShake(duration));
    39.     }
    40.  
    41.     public IEnumerator cShake(float duration)
    42.     {
    43.         while (duration > 0)
    44.         {
    45.             vcam.GetCinemachineComponent<CinemachineBasicMultiChannelPerlin>().m_AmplitudeGain = 1f;
    46.  
    47.             duration -= _fakeDelta;
    48.  
    49.             yield return null;
    50.         }
    51.     }
    52.  
    53. }

    In another script I'm calling
    Code (CSharp):
    1. VCamShake.Shake(0.3f);
    to shake CM vcam1 for 0.3 sec.

    Both
    Code (CSharp):
    1. vcam.AddCinemachineComponent<CinemachineBasicMultiChannelPerlin>();
    and
    Code (CSharp):
    1. vcam.GetCinemachineComponent<CinemachineBasicMultiChannelPerlin>().m_AmplitudeGain = 1f;
    are giving a Null Reference.

    What component should I add on the vcam? I prefer doing such things in inspector and I don't see nothing similar to CinemachineBasicMultiChannelPerlin (see attached screenshot).
     

    Attached Files:

  6. Gregoryl

    Gregoryl

    Unity Technologies

    Joined:
    Dec 22, 2016
    Posts:
    7,711
    Hi @HarryDemch Looks like you never initialize vcam. That's what's null.

    To set up noise via inspector, select "BasicMultiChannelPerlin" in the Noise component of the vcam. Assign it a Noise Definition from one of the preset assets included in the Cinemachine examples.

    Then from your script, get it (as described) and twiddle its amplitude.
     
    IgorDemchuk likes this.
  7. IgorDemchuk

    IgorDemchuk

    Joined:
    Mar 9, 2015
    Posts:
    21
    Hey, @Gregoryl
    Isn't it in the 10th line of VCamShake.cs?

    The "BasicMultiChannelPerlin" is turned on, using Noise Profile "Camera Shake", Amplitude Gain = 0, Frequency Gain = 1 (see the attached screenshot).

    Now VCamShake.cs looks like this

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using Cinemachine;
    5.  
    6. public class VCamShake : MonoBehaviour {
    7.  
    8.     public static VCamShake instance;
    9.  
    10.     private CinemachineVirtualCamera vcam;
    11.     private float _timeAtCurrentFrame;
    12.     private float _timeAtLastFrame;
    13.     private float _fakeDelta;
    14.  
    15.  
    16.     void Awake()
    17.     {
    18.         instance = this;
    19.     }
    20.  
    21.     void Start()
    22.     {
    23.        
    24.     }
    25.  
    26.     void Update()
    27.     {
    28.         // Calculate a fake delta time, so we can Shake while game is paused.
    29.         _timeAtCurrentFrame = Time.realtimeSinceStartup;
    30.         _fakeDelta = _timeAtCurrentFrame - _timeAtLastFrame;
    31.         _timeAtLastFrame = _timeAtCurrentFrame;
    32.     }
    33.  
    34.     public static void Shake(float duration)
    35.     {
    36.         instance.StopAllCoroutines();
    37.         instance.StartCoroutine(instance.cShake(duration));
    38.     }
    39.  
    40.     public IEnumerator cShake(float duration)
    41.     {
    42.         while (duration > 0)
    43.         {
    44.             vcam.GetCinemachineComponent<CinemachineBasicMultiChannelPerlin>().m_AmplitudeGain = 1f;
    45.  
    46.             duration -= _fakeDelta;
    47.  
    48.             yield return null;
    49.         }
    50.     }
    51.    
    52. }

    Line 44 gives Null Reference.
     

    Attached Files:

  8. JakubSmaga

    JakubSmaga

    Joined:
    Aug 5, 2015
    Posts:
    417
    You never attatched vcam because it's private and there's no "vcam = ?;" function, that's why it's null. Make your vcam public or if the VCamShake is added to your VCam in the inspector just add "vcam = GetComponent<CinemachineVirtualCamera>();" in your Awake()
     
    Gregoryl and IgorDemchuk like this.
  9. IgorDemchuk

    IgorDemchuk

    Joined:
    Mar 9, 2015
    Posts:
    21
    Thank you very much, @Gregoryl and @JacobSmaga!

    For some reason I got confused, but now everything's working.

    Here's the answer to my question for anyone who might be interested:
    1. Create CM VCam
    2. Make sure to use Basic Multi Channel Perlin in Noise Component (see attached screenshot)
    3. Add VCamShake.cs on the VCam
    4. From other script call VCamShake.Shake(0.3f) to shake camera for 0.3 sec. Insert any time you want.
    Code (CSharp):
    1. using System.Collections;
    2. using UnityEngine;
    3. using Cinemachine;
    4.  
    5. public class VCamShake : MonoBehaviour {
    6.  
    7.     public static VCamShake instance;
    8.  
    9.     private CinemachineVirtualCamera vcam;
    10.     private CinemachineBasicMultiChannelPerlin noise;
    11.     private float _timeAtCurrentFrame;
    12.     private float _timeAtLastFrame;
    13.     private float _fakeDelta;
    14.  
    15.  
    16.     void Awake()
    17.     {
    18.         instance = this;
    19.         vcam = GetComponent<CinemachineVirtualCamera>();
    20.         noise = vcam.GetCinemachineComponent<CinemachineBasicMultiChannelPerlin>();
    21.     }
    22.  
    23.     void Update()
    24.     {
    25.         // Calculate a fake delta time, so we can Shake while game is paused.
    26.         _timeAtCurrentFrame = Time.realtimeSinceStartup;
    27.         _fakeDelta = _timeAtCurrentFrame - _timeAtLastFrame;
    28.         _timeAtLastFrame = _timeAtCurrentFrame;
    29.     }
    30.  
    31.     public static void Shake(float duration)
    32.     {
    33.         instance.StopAllCoroutines();
    34.         instance.StartCoroutine(instance.cShake(duration));
    35.     }
    36.  
    37.     public IEnumerator cShake(float duration)
    38.     {
    39.         while (duration > 0)
    40.         {
    41.             noise.m_AmplitudeGain = 1f;
    42.             duration -= _fakeDelta;
    43.             yield return null;
    44.         }
    45.  
    46.         noise.m_AmplitudeGain = 0f;
    47.     }  
    48. }
    49.  
     

    Attached Files:

    zeimhall likes this.
  10. Gregoryl

    Gregoryl

    Unity Technologies

    Joined:
    Dec 22, 2016
    Posts:
    7,711
    @zeimhall My best guess is that you have more than one object in your scene with the cameraShake script. The way it's designed (with the static instance) it will not tolerate more than one at a time.
     
    zeimhall likes this.
  11. Keepabee

    Keepabee

    Joined:
    Jul 12, 2012
    Posts:
    58
    @Gregoryl You suggest to use the BasicMultiChannelPerlinNoise extension component, which is fine in this kind of setup where you have a Scene with one VirtualCamera that is always shaking.

    I'm looking for more of game-event driven camera shake to make short-duration events during play more impactful, like player getting damage, things breaking, final hits when defeating enemies, explosions happening.

    The thing is that there is a vast amount of Scenes, each with similarly large number of VirtualCameras, and some VirtualCameras are actually already using the noise component for something else that they need to emphasize.

    I'd like to setup a system where I can pretty much call CameraShake.Shake(someShakeTemplateParameter) where someShakeTemplateParameter is probably a ScriptableObject with various intensity and duration variables set up so I can call "CameraShake.Shake(playerHitShake);" or "CameraShake.Shake(hugeExplosion);" and such in code where enemies hit or bombs go off.

    I already threw together a class that tries to do a lot of things, like:
    • look for a CameraBrain to get the active VirtualCamera from
    • tracks Scene changes to re-look for a new CameraBrain and check for all other current references
    • tracks current CameraBrains VirtualCameraActivation event to check if it needs to Add or Get a new BasicMultiChannelPerlinNoise component for the current VirtualCamera
    • Hijacks the BasicMultiChannelPerlinNoise component and forces a new noise profile, new amplitude and new frequency values, storing the old values
    • restores the old values after a duration
    • etc.
    The system is not perfect by a long shot. It's easy to lose track of a VirtualCamera before restoring something and my quickly implemented system already managed to fail a couple of tests for tracking the VirtualCamera changes, so there's somewhere a on-standby VirtualCamera shaking wildly, while the active one no longer shakes when expected to.

    Yeah, I will probably try to improve my system to use your recommendation of VirtualCamera noise for camera shake, but it's a lot of work, breaks using the noise for other purposes, requires constantly adding/getting a ton of component references and I feel there has to be a better way.
     
  12. Gregoryl

    Gregoryl

    Unity Technologies

    Joined:
    Dec 22, 2016
    Posts:
    7,711
    See my reply here: https://forum.unity.com/threads/how-to-shake-camera-with-cinemachine.485724/#post-3416266