Updated: Developing a Dynamic 2D Camera in Unity
We recently greatly optimised the dynamic camera used in Star Janitors, so an updated tutorial is in order!
We need our camera to move and zoom every frame to keep all of the important objects on screen at all times. To do this it must find the largest distance between any two objects, move to the midpoint of this distance, and scale the Orthographic size so that the two objects are just within the outer borders of the camera’s viewpoint.
We start off our cameraScript.cs file with a private reference to the camera component (to save making GetComponent calls every frame), a List of GameObjects (a dynamic array which will hold references to every important object in the scene), and a Vector3 which stores the camera’s position at the start of each frame.
The List is initialised in the header so that it’s already prepared to accept data by the time other scripts call their Start() functions.
Next come variables to hold all of the required values during later calculations, their uses will be explained individually later on.
One of the important values here is distanceUpperBound, which is the distance after which the Orthographic Size will stop scaling to prevent the level zooming out too far. To find which value to use in your project, place 2 game objects at the opposite ends of your level and calculate the distance between them.
Start camera initialisation by storing a reference to the camera component on the same game object for later use.
The camera’s current position is then saved, and the maximum and minimum Orthographic size are calculated. In this case, the maximum is set to the starting size as that’s how my scene is set up in-editor. Play around with a few values for the minimum size to find the one you like best, the smaller the value the further in the camera will be allowed to zoom. It’s best to keep this a power of 2 to keep scaling looking crisp.
The largestDistance is set to the upper distance bound value for the first frame to stop it doing anything wild since other objects may not yet exist to track.
We then set the lowerDistanceBound to a hardcoded value of 5, but again you will want to play around with what value works best in your game. Remember this is the distance between two objects after which the Orthographic Size will no longer be reduced if they get closer together, to prevent the camera zooming in too far.
Finally two important values are calculated; the orthRatio and distRatio. A ratio exists between the distance between objects, and how much the Orthographic Size must be adjusted to keep them both onscreen, and these ratios are calculated fairly simply.
The first part of each frame update is moving the camera to the midpoint of all of the onscreen objects, which is done by using a simple loop and vector division to find the average midpoint. This position is then used to create a target position for the camera to move towards, using Lerp to make the movement smoothly.
The Z value of the target position is set to a fixed value of -10 to maintain the camera’s distance away from the scene.
The second part of each frame update is calculating the new Orthographic size for the camera’s viewpoint. This done by first resetting the Orthographic Size to the chosen maximum. The largest distance between any two objects is calculated using a function described below, and this value is then used to calculate the Orthographic Size.
Checks are made to see if the distance exceeds either of the two bounds, in which case the zoom is clamped to the minimum and maximum Orthographic Size values specified earlier.
If the largest distance lies between the bounds, the Orthographic Size is calculated by applying the Orthographic ratio to the change in object distance, with a small buffer applied to keep the furthest objects neatly within the screen bounds as opposed to being pressed right against the edge.
Lerp is then used to perform a smooth zoom towards the newly chosen value.
Finding The Largest Distance
A small function is used in calcZoom() to find the largest distance between two objects in each frame. This function is kept separate to keep the code neat. It looks at each distance between every possible pairing of objects in the list and stores the largest value it finds. Using this with hundreds of game objects will likely cause performance issues, but it works perfectly for small/medium scenes.
With all of the functionality written, all that’s left is to call the calcPos() and calcZoom() in turn every frame to get the camera up and running.
Adding & Removing Objects
The List of objects to keep onscreen is easy to maintain, an object can be added by pushing it onto the list, once added its position will be assessed during subsequent updates:
Removing an object can be done by checking if the list contains the specified object, and then removing it if it does:
by Harry Ryder