Like the other new components I was preparing for the new standard assets, a key part of this setup is modularity and versatility. This is why I from the start chose to have camera handling values maintained via the transform tree – as opposed to maintaining internal values to overwrite the transform values when updated.
This way, not only can camera logic be broken down into several components which can be optional or replaced, but co-operation between the camera system, animations and any other systems which could apply relative transformation, is much easier.
As this approach would be placing a certain requirement on the transform tree structure surrounding the camera in any case, I opted to go for greatest amount of possible detail vs. smallest impact. The structure therefore requires a three layers deep transform tree.
The root of the tree is designated the horizontal hinge. It takes care of movement in all three dimensions as well as rotation on the horizontal plane. The immediate child of this transform is referred to as the vertical hinge. This transform is expected to not move and will have its position reset every frame to enforce this. In stead, the vertical hinge is only responsible for rotation on the vertical plane.
The final transform, the child of the vertical hinge, is expected to not rotate and only move on its relative z-axis. This effectively makes it the zoom component of the rig. The GameObject of this transform is the one holding the camera component.
Maintaining and abstracting this setup is the DualHingeCamera component. With reference to the different transforms of the rig, this component maintains the setup by apply three targets: targetOffset, targetHorizontalRotation and targetVerticalRotation. These are set by controller scripts and applied via constrained speed and offset variables.
Vertical rotation is clamped and damped by the verticalSpan animation curve variable. As explained in the presentation, the time value of the most extreme animation keys specify the vertical angles (relative to a vector pointing backwards from the rig root) at which vertical rotation is clamped.
The height of the curve at any point specifies the scale of the update speed – as the camera moves through that angle and further towards the end of the half of the curve it is on. As in: If the angle is above the middle of the vertical span and the camera is moving further up, speed is scaled according to the curve, but if it is moving down then no scale is applied. The inverse if of-course true as well. The effect, assuming the curve is not flat, is a smooth limit rather than a hard clamp.
The base for any of the example camera behaviours supplied with the project is the CameraBehaviour component. This supplies wrapping of access to the DualHingeCamera, as well as a bit of default handling – such as automatically updating the camera activity tracking when the camera moves. It also offers up the MoveCamera method as a common interface for input scripts to apply movement instructions.
One of the most simple examples of a camera behaviour is the OrbitCamera. This camera simply takes the movement instructions (a Vector2) and apply them to the target horizontal and vertical rotation. The result is the familiar behaviour of being able to rotate around a subject, while rotating up and down within the clamped span.
There are several more camera behaviours in the project (which you should take a look at) and they are demonstrated in an example scene as well as within the presentation. However as a last example, I want to point out the TravelCamera component. Unlike the others, this does not inherit from CameraBehaviour – nor does it directly affect its rotation or position. In stead, it holds a simple piece of logic which enables and disables other behaviours, based on input activity. The result should be a behaviour somewhere between that seen in Journey and that of the Assassins Creed games.
I tried to keep this post short, but I hope you still find this useful and decide to build on the system. In any case, here are direct links to the resources covered in the post: