CAMERAHello!
Today we will be tackling a more technical post, although this specific one is really related to design. We will talk about the
first version of the camera we have implemented for the game. This will probably be improved and we will add more effects to it, but we will start from the basic. When we planned how the camera for Dynasty Feud should be, we discussed two completely different options:
Static: The whole stage fits in the screen and no movement is required at all.
Dynamic: The camera follows players and adjusts the zoom depending on the distance between them.
Below we can see the differences between them in
Dynasty Feud.
As you probably have guessed since we are writing a post about the implementation of a camera we did, the option we finally selected is the
dynamic one. Our main reason to go for that option is that the dynamic one fits better with the idea of levels that
constantly transform.
So, let's start with the programming part:
LookAt position: To get the position the camera needs to look at, we basically need the center of every
point of interest that is registered to the camera. This part is simple because it only requires for us to create a bounding box that includes all the points and the center of that box is where our camera should be looking at.
// Computes the center of interest between all the objects of interest
private Vector3 ComputeCenterOfInterenst()
{
// If there are no objects of interest skip
if (objectsOfInterest.Count == 0)
{
centerOfInterest = Vector3.zero;
return centerOfInterest;
}
// Find the corners of the points of interest (create an AABB around them)
Vector3 maxPoint = objectsOfInterest[0].transform.position;
Vector3 minPoint = objectsOfInterest[0].transform.position;
for (int i = 1; i < objectsOfInterest.Count; i++)
{
float xPos = objectsOfInterest[i].transform.position.x;
float yPos = objectsOfInterest[i].transform.position.y;
if (maxPoint.x < xPos)
maxPoint.x = xPos;
if (minPoint.x > xPos)
minPoint.x = xPos;
if (maxPoint.y < yPos)
maxPoint.y = yPos;
if (minPoint.y > yPos)
minPoint.y = yPos;
}
objectsBounds.max = maxPoint;
objectsBounds.min = minPoint;
// the center of the AABB is the center of interest
centerOfInterest = objectsBounds.center;
return centerOfInterest;
}
Zoom adjustment: The proper zoom adjustment implies a little more work (not much more). To know if the actual zoom is correct, we have to compute the percentage of the actual screen that is filled by the bounding box that surrounds all the points of interest (the bounding box we computed for the center of interest), we call this
screen occupancy. Actually the screen occupancy will be computed separately for the different axes, X and Y. The way to find this values its pretty straight forward, get the farthest point from the center of interest along that axis and convert that point from World coordinates to NDC (Non-device coordinate). That's it! You have a value that is 1 when your Points of interest are just in the corners of the camera and close to 0 when they are really close. Once this value is computed the only thing missing is to change the actual
zoom, which is as simple as deciding the screen occupancy fits better at each moment and zoom in when the occupancy is smaller than the selected and zoom out when its bigger.
// Find distance between farthest point of interest and center of interest in X
Vector2 viewFarthesX = cam.WorldToViewportPoint(farthestXPoint);
Vector2 NDCFarthesX = Transformations.ViewportToNDC(viewFarthesX);
float screenOccupancyX = Mathf.Abs(NDCFarthesX.x - NDCCenterOfInterest.x);
// Find distance between farthest point of interest and center of interest in Y
Vector2 viewFarthesY = cam.WorldToViewportPoint(farthestYPoint);
Vector2 NDCFarthesY = Transformations.ViewportToNDC(viewFarthesY);
float screenOccupancyY = Mathf.Abs(NDCFarthesY.y - NDCCenterOfInterest.y);
// Get maximum occupancy
screenOccupancy = Mathf.Max(screenOccupancyX, screenOccupancyY);
| |
Screen Occupancy = 0.1 | Screen Occupancy = 0.3 |
| |
Screen Occupancy = 0.6 | Screen Occupancy = 0.9 |
Hope the explanation was somehow clear enough. Have a nice day and see you next time!
Camera Controls have always been the hardest thing for me to program this looks great! Good Job!