Early on in the development of Demons with Shotguns, one major design decision that had to be made was "how big should the game maps be (single screen or multi-screen)?" Small maps allow for focused, more intense battles while limiting game mode possibilities, while larger maps allow for more gameplay and game mode varieties. Small, single screen maps would require only one camera to capture all players and playing environment. Larger, multi-screen maps would require multiple cameras (one for each player) that can follow a target. Since Demons with Shotguns is a local-multiplayer game only, this also means split-screen cameras.
Why not have both? It's pretty trivial to have maps that anchor the camera to a single spot for small maps, while allowing split-screen cameras for larger maps. After some playtesting, I found the split-screen cameras pretty annoying due to the small screen real estate they provided for each player. I didn't want to scrap the idea of big maps entirely. So, is it possible to create a single camera that can follow up to four different targets? I soon realized that the type of camera I ended up needing is a camera in the style of a fighting game.
Fighting games, such as Super Smash Bros, or even wrestling games, feature single screen cameras that track multiple targets, zooming in and out as the targets get closer and farther away from each other, respectively. This is done by some vector math magic (it's not really magic, as you'll see).
So let's go through the requirements of the camera system
- Follow up to four targets, always having them within screen view at all times.
- The camera should always be focused on the relative center of all four targets.
- As targets move farther away from each other, zoom camera out an appropriate amount of distance.
- As targets move closer to each other, zoom camera in, clamping the zoom factor to a specified amount.
- Based on all targets current positions, what are the minimum and maximum positions.
- What is the center point between the minimum and maximum positions.
- How far do we need to zoom to keep all targets within view.
Let's add some diagrams to help visualize this better (the scale is all wrong, I know, but bare with me!).
The smiley faces represent our three players, and their positions. Following the above pseudocode, we come up with a min of (8, 7) and a max of (31, 14). This gives us the outermost coordinates of the area our players are in.
To find the center of these two positions is a trivial step. Simply add the Min and Max vectors, and multiply by 0.5.
((8, 7) + (31, 14)) * 0.5 = (19.5, 10.5)
Great! We now have the target position that our camera will use to follow. This position will update as our players move, ensuring we're always at the relative center of them. But we're not done just yet. We need to determine the zoom factor.
Quick side note about the zoom factor. When developing a 2D game, you normally use 2D vectors (as we've been doing so far) and an orthographic camera, which ignores the z-axis (in Unity, not really, but the depth is used differently as objects don't change size as the z-axis changes). If you were developing a 3D game, you'd be using 3D Vectors and a perspective camera. Perspective cameras have depth according to their z-axis position. However, determining the zoom factor for both 2D and 3D is quite similar, just how you apply the value differs.
We've already determined that the X and Y coordinates of our camera needs to be (19.5, 10.5), as that's the relative center of all targets on the X and Y axes. What you need now is a vector that's perpendicular to the X and Y coordinates we calculated above. That's where the cross product formula comes in. The more astute reader may be screaming "you can't perform cross product on 2D vectors!" right now. Yes, you're absolutely correct, but bear with me.
The cross product of two vectors give us a vector that's perpendicular (at a right angle) to the two.
The diagram above shows the cross product of the red and blue vectors as the red vector changes direction, with the resulting perpendicular green vector. Notice how the magnitude of the green vector changes, getting longer and shorter based on the magnitude of the red vector. This is exactly what we need, a vector that's perpendicular to our camera's (X, Y) target position coordinates, whose magnitude changes appropriately based on the angle.
As mentioned before, you can't perform the cross product of 2D vectors. So instead, we'll pad our 2D vectors with a z coordinate of 0.
(19.5, 10.5, 0) x (0, 1, 0) = (0, 0, 19.5)
x is the symbol for cross product. We use a normalized up vector as our second argument so that the resulting vector is of maximum distance. Using the Z value of 19.5, we can now set the zoom factor. Since orthographic cameras don't technically zoom in the same sense as a perspective camera, we instead change the orthographic size, which provides the same effect.
Now let's assume that the perspective camera of your 3D game needs to act very much like a 2D platformer (always facing the side, never directly above or below). Instead of altering the orthographic size (because that doesn't make sense for a perspective camera ;) ), we use the results of the cross product to set the z-axis directly. This will move the perspective camera accordingly, give us our desired zoom effect.
With this camera system, Demons with Shotguns is able to have larger arena maps (within limit) that's still accessible to the player. Here's a gif of the system in action (apologies for the large size).
Demons with Shotguns is a fast paced competitive 2D arena platformer for 2-4 players from MindShaft Games where you battle for the souls of your friends, currently on Steam Greenlight.