Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Screen Shake Effect

Discussion in 'Scripting' started by liquidgraph, May 12, 2009.

  1. liquidgraph

    liquidgraph

    Joined:
    Feb 26, 2009
    Posts:
    324
    Has anyone out there made a screen shake effect for the camera and know the best way to implement it? I want this effect for something like an earthquake, a large explosion, or a heavy impact. Ideas?
     
  2. Jacob-Williams

    Jacob-Williams

    Joined:
    Jan 30, 2009
    Posts:
    267
    I accomplished this in a very easy way. If you have access to a 3D Animation app, just animated an object (a simple cube, for instance) shaking and moving a bit randomly. In unity, make the camera a child of the object you animated and turn off the renderer. Whenever you want a camera shake, just play the animation.

    -cD
     
  3. Eric5h5

    Eric5h5

    Volunteer Moderator Moderator

    Joined:
    Jul 19, 2006
    Posts:
    32,401
    This project has a camera shake routine in it. Using code instead of an animation means you can dynamically change the intensity and so on.

    --Eric
     
  4. liquidgraph

    liquidgraph

    Joined:
    Feb 26, 2009
    Posts:
    324
    Thanks guys, two unique solutions! I would prefer code if it produced an effect to my liking. I'll have to test this out.
     
    TheExGenesis likes this.
  5. Martin-Schultz

    Martin-Schultz

    Joined:
    Jan 10, 2006
    Posts:
    1,377
    I'm using a quite simple approach. I linked the main camera to an empty game object and if wanted to shake the camera, I set in each update a random local position of the attached camera within a unit sphere:

    http://unity3d.com/support/documentation/ScriptReference/Random-insideUnitSphere.html

    So it could look like this (semi pseudo code):

    Code (csharp):
    1. var camera : Camera; // set this via inspector
    2. var shake : float = 0;
    3. var shakeAmount : float = 0.7;
    4. var decreaseFactor : float = 1.0;
    5.  
    6. function Update() {
    7.   if (shake > 0) {
    8.     Camera.transform.localPosition = Random.insideUnitSphere * shakeAmount;
    9.     shake -= Time.deltaTime * decreaseFactor;
    10.  
    11.   } else {
    12.     shake = 0.0;
    13.   }
    14. }
    (all off head, have the complete function at home if you want it)

    That function then shakes the camera locally around in each frame and decreases over time. So if you want to shake it, simply set "shake" to 1.0 or 2.0 or something like that and it starts to shake. I use the effect in my new "Decane intro" for all my games when the car crashes into my Decane logo. See the intro here:

    http://www.wooglie.com/playgame.php?gameID=28

    - Martin
     
  6. Srbhunter

    Srbhunter

    Joined:
    Feb 25, 2014
    Posts:
    3
    Thanks Martin, very simple solution and it's working great!
     
  7. theLucre

    theLucre

    Joined:
    Jan 20, 2014
    Posts:
    20
    Awesome solution, Martin. Thank you!
     
  8. cmcpasserby

    cmcpasserby

    Joined:
    Jul 18, 2014
    Posts:
    315
    also a other good option is doing something similar to what martin did, but using source that provides a smooth random like a perlin to get your random values, which i find looks a little less glitch and more like a shaky came in real-life than using truly random values.
     
  9. J_Troher

    J_Troher

    Joined:
    Dec 2, 2013
    Posts:
    42
    Nice script Martin. Probably wouldn't be too bad to throw it into a Coroutine ? And possibly lerp the camera between the random positions rather than assigning the generated position immediately? I will give it a try ;)
     
  10. Narattitude

    Narattitude

    Joined:
    Mar 27, 2014
    Posts:
    1
    Thanks Martin.
     
  11. bigpants

    bigpants

    Joined:
    Dec 9, 2009
    Posts:
    49
    camera.transform.localPosition=Random.insideUnitSphere* shakeAmount;
    Martin's approach is pretty good, but can have the following problems:

    1. If most of your objects are far away from the camera, position screenshake won't be noticeable.
    Things in the distance simply won't move, so there's less/no screenshake.
    Worse, if this is a 3rd person camera, and the player is the only thing close to the camera,
    only the player will appear to shake - NOT the screen.
    Solution: Change ROTATION of camera in addition to position.
    Think the camera shake in Star Trek - they weren't modifying position :)
    You need to experiment with BOTH position & rotation to figure out what's right for your game​

    2. Random.insideUnitSphere (and my Quaternion) can produce similar positions which is NOTICEABLE
    Turns out that minor changes in camera, unlike a GameObject, are extremely noticeable.
    The screenshake will have less shake than it should, and changing shakeAmount won't help.
    Even perlin or better random won't solve this. You need a PREDICTABLE/REPEATABLE shake.
    Solution: Shake by an angle that increases in large predictable increments (0.7f)
    Example combining 1 & 2:

    shakeAngle = (shakeAngle + Mathf.PI * 0.7f) % (Mathf.PI * 2f);
    camera.transform.rotation *= Quaternion.Euler(
    Mathf.Cos(shakeAngle) * shakeLength, Mathf.Sin(shakeAngle) * shakeLength, 0f​
    )
    (you can also apply something to position)

    While I haven't used it, asset store "Camera Shake" seems to take into account the above problems
    Might be worth the $10
    https://www.assetstore.unity3d.com/en/#!/content/3563

    Note
    If you mess with Time.timeScale the screenshake effect is RUINED.
    A screenshake at 60fps is awesome, at 10fps it no longer feels like a screenshake.
    I had to stop using Time.timeScale
     
  12. Ash-Blue

    Ash-Blue

    Joined:
    Aug 18, 2013
    Posts:
    102
    Going off of @Martin Schultz answer. I was able to extend the code to have a nice ease in and out as people above mentioned. Psuedo code snippet below of a coroutine I wrote.


    Code (CSharp):
    1.                
    2.                 fracJourney = countdown / duration;
    3.  
    4.                 if (move == CameraMove.EaseInOut) {
    5.                     // Go up half way in and then reduce on the way out
    6.                     if (fracJourney < 0.5f) {
    7.                         intensity = Mathf.SmoothStep(0f, intensityMax, fracJourney);
    8.                     } else {
    9.                         intensity = Mathf.SmoothStep(intensityMax, 0f, fracJourney);
    10.                     }
    11.                 } else {
    12.                     intensity = intensityMax;
    13.                 }
    14.  
    15.                 offset = intensity * Random.insideUnitCircle;
    16.  
     
  13. inko

    inko

    Joined:
    Nov 29, 2013
    Posts:
    143
    A really neat trick is to use a perlin function to give it a very smooth but random feel.
    This code is untested but should work. The values are arbitrary.

    Code (CSharp):
    1. public AnimationCurve curve;
    2.  
    3. float t=0f;
    4. void Update () {
    5. //...clever code
    6. float shake = Shake( 10f, 2f, shakeCurve);
    7. //apply this shake to whatever you want, it's 1d right now but can be easily expanded to 2d
    8. }
    9.  
    10. float Shake (float shakeDamper, float shakeTime, AnimationCurve curve) {
    11. t += Time.deltaTime;
    12. return Mathf.PerlinNoise(t / shakeDamper, 0f) * curve.Eval(t);
    13. }
     
    puppeteer, LMan and MaddHatt like this.
  14. Komizart

    Komizart

    Joined:
    Nov 21, 2015
    Posts:
    6
    this is shake for Y
    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public class CameraShake : MonoBehaviour {
    5.    
    6.     private float ShakeY = 0.8f;
    7.     private float ShakeYSpeed = 0.8f;
    8.  
    9.    
    10.     public void setShake(float someY)
    11.     {
    12.         ShakeY = someY;
    13.     }
    14.    
    15.     void Update()
    16.     {
    17.         Vector2 _newPosition = new Vector2(0, ShakeY);
    18.         if (ShakeY < 0)
    19.         {
    20.             ShakeY *= ShakeYSpeed;
    21.         }
    22.         ShakeY = -ShakeY;
    23.         transform.Translate(_newPosition, Space.Self);
    24.     }
    25. }
    26.  
     
    tokar_dev likes this.
  15. Vishwa001

    Vishwa001

    Joined:
    Nov 24, 2016
    Posts:
    2
    A really calculated approach to shake camera when player are move in fast forword..

    IEnumerator Shake(float time)
    {
    yield return new WaitForSeconds (time);

    float elapsed = 0.0f;
    float magnitude = 0.5f;

    Vector3 originalCamPos =new Vector3(Player.transform.position.x,Player.transform.position.y,Camera.main.transform.position.z);

    while (elapsed < duration) {

    elapsed += Time.deltaTime;

    float calPerc= elapsed / duration;
    float zigzag= 1.0f - Mathf.Clamp(4.0f * calPerc- 3.0f, 0.0f, 1.0f);

    /

    // camera position near about player transform position

    float FX = Random.Range (-1.0f, 1.0f);
    float x = Random.Range (Player.transform.position.x,Player.transform.position.x+FX);

    float FY=Random.Range (-1.0f, 1.0f);
    float y = Random.Range (Player.transform.position.y,Player.transform.position.y + FY);

    x += magnitude + zigzag;
    y += magnitude + zigzag;

    Camera.main.transform.position = new Vector3(x, y, originalCamPos.z);

    yield return null;
    }

    Camera.main.transform.position = new Vector3(Player.transform.position.x,Player.transform.position.y,Camera.main.transform.position.z);


    }