Almost a year ago, I wrote a short tutorial on screen shaking for a 2D game. Today, I want to follow-up on that post with a more advanced script. With this script, the camera not only shakes a lot smoother, but it can also be moved around while shaking. This was not possible with the old script.
If you want to simply download the script and start messing around with screen shake in your own game, feel free to download the Unity package here.
In order for the script to work properly, the actual Camera object needs to be nested under a few others. It should be set up like so:
CameraMainAxis controls the movement of the camera. CameraShakeAxis controls the shaking of the camera. And MainCamera is the traditional camera object, with our "CameraControl" script attached to it as well as the Camera component. The objects themselves can be named whatever you want, but they must be nested properly, and they must be dragged into the CameraControl fields properly, as shown here:
In the above image, you'll notice there are 2 other variables. Both of these are used during movement, not during shaking.
Move Speed is how quickly the camera moves from point A to point B, by default. Note that when you call Move() on the camera from elsewhere, you can pass in a speed. If no speed is passed, it defaults to MoveSpeed in the above image.
Snap Distance is also used when the camera is moving (not shaking): this is how close the camera must get to point B before considering its movement over and snapping to the final position. I always keep this number pretty small, 0.05 is small enough to not be noticed by the player. This number must be greater than 0 or the movement will break!
Now that the camera is all set up, it's simple to use. If you want to shake the camera, you just call Shake(). Shake takes the following parameters:
- intensity (float): Max distance from the center point the camera will travel.
- shakes (int): Total number of random points the camera will travel to.
- speed (float): How quickly the camera moves from point to point.
An example of calling Shake() is:
There are 2 ways to move the camera: You can do so slowly (or quickly) over time or you can do it instantly. To move the camera over time, call the Move() function. To move it instantly, call the SetPosition() function.
Move() takes the following parameters:
- x (float): Distance along the x axis to move.
- y (float): Distance along the y axis to move.
- speed (float): How quickly to move in the specified direction.
SetPosition() takes the following parameter:
- position (Vector2): Actual position to place the camera at.
Note that Move() takes in displacements, while SetPosition() takes in an actual new position.
At this point, the camera should be up and running and you should have no problem fitting it into your own games. However, if you'd like to know the logic behind how it actually works, read on! Below is the actual code taken straight from the downloadable package above, with short descriptions of what each function does and why.
The first thing we want to do is turn the script off. This still allows it to receive calls from other scripts, but it will not run through Update() repeatedly. Then we set _baseX and _baseY, which are our original positions. This way, when we are done shaking later on, we know what position to return to.
The first thing we do is turn on the script (enabled = true) and turn on shaking (_isShaking = true). Turning on the object enables the Update function to run continuously, which is detailed below. We then save all the values that were passed in and determine what our start shake point is going to be by calling DetermineNextShakePosition().
DetermineNextShakePosition() simply randomizes where we are going to shake to. It does not randomize the z position because this script was written for a 2D game, and randomizing the z position could screw a lot of things up by making sprites disappear and reappear.
The rest of the Shake logic happens in the Update function, detailed below.
Move() starts out by determining what our speed is going to be. By default, the speed parameter is 0, but any other piece of code can pass in a speed. If no speed is passed in, we use our default value, which is assigned from the Unity inspector window (see "Setup", above). If a speed is passed in, override our default speed with the one passed in.
Once the speed is figured out, all we need to do is figure out where we are moving to next. We simply take our current position and add the passed-in parameters to it and voila, we get the position to move to. We then enable the script in order to run the Update function, and let it know we are moving.
SetPosition() is much more straight-forward. It takes in a position, and simply sets our main axis' position to that. No fancy coding is necessary in this case.
Update() is where all the magic happens. The first thing it does is determine if we are moving or shaking, or both. Only then do we run our respective logic.
If we are moving, we call MoveTowards() to get closer to our destination. We set the maxDistanceDelta to Time.deltaTime * _currentMoveSpeed, so we maintain our desired speed no matter how long the frame takes to render. Then we determine if we are close enough to our desired position or not. If we are, then we snap to the position and let the script know that we are done moving. If we are also not shaking at the time, we turn the script off completely. The reason for turning the script off is to save precious CPU cycles - now we don't have to continuously check if we are moving or shaking.
If we are shaking, our logic is very similar to moving, but our destination is random and it keeps changing. We want to loop through the movements a number of times equal to _shakeCount, which was passed in during the Shake() call. Once a shake ends, we call DetermineNextShakePosition() to calculate another random destination for us. However, if we are on our final shake, instead of calling DetermineNextShakePosition() we instead set our destination to our original position. This brings the camera back to where it started smoothly, rather than jumping back to it after it's done shaking. When all of the shakes are finished, we snap to our original position and let the script know that we are done shaking. If we are also not moving at this time, we turn the script off completely, just like above.
So that's it! When all is said and done, the entire script is only about 130 lines long, and gives us a nice smooth screen shake that can happen while we move the camera around. I hope this was helpful!