Book Image

Creating E-Learning Games with Unity

By : David Horachek
Book Image

Creating E-Learning Games with Unity

By: David Horachek

Overview of this book

Table of Contents (17 chapters)
Creating E-Learning Games with Unity
About the Author
About the Reviewers

Developing the camera code

In our 3D game, the main camera mode will follow a third-person algorithm. This means that it will follow the player from behind, trying to keep the player on screen and centered in view at all times. Before we start developing the camera, we need to think about the basic requirements of our game in order to be able to program the camera to achieve good cinematographic results. This list of requirements will grow over time; however, by considering the requirements early on, we build an extensible system throughout the course of this book by applying good system design in our software. In no particular order, we list the requirements of a good camera system as follows:

  • It needs to be able to track the hero at a pleasing distance and speed and in an organic way

  • It needs to be able to transition in an appealing way, from tracking various objects

  • It needs to be able to frame objects in the field of view, in a cinematic and pleasing way

Starting with an initial camera and motion system based on the Unity3D examples, we will extend these over time. We do this not only because it is instructive but also with the aim of extending them and making them our own over time. With these requirements in mind, let's build the camera code. Before we do, let's consider some pseudocode for the algorithm.

Implementing GameCam.cs

The GameCam script is the class that we will attach our MainCamera object to; it will be responsible for the motion of our in-game camera and for tracking the player on screen. The following five steps describe our GameCam camera algorithm:

  1. For every frame that our camera updates, if we have a valid trackObj GameObject reference, do the following:

    1. Cache the facing angle and the height of the object we are tracking.

    2. Cache the current facing angle and height of the camera (the GameObject that this script is attached to).

  2. Linearly interpolate from current facing to desired facing according to a dampening factor.

  3. Linearly interpolate from current height to desired height according to another dampening factor.

  4. Place the camera behind the track object, at the interpolated angle, facing the track object so that the object of interest can be seen in view, as shown in the following screenshot:

Now let's implement this algorithm in C# code by performing the following steps:

  1. Right click on the Chapter1 assets folder and select Create New C# Script. Name it GameCam and add it to the Main Camera object.

  2. Create a public GameObject reference called TrackObj with the following code. This will point to the GameObject that this camera is tracking at any given time, as shown in the following code:

    public GameObject trackObj;
  3. Create the following four public float variables that will allow adjustment of the camera behavior in the object inspector. We will leave these uninitialized and then find working default values with the inspector, as shown in the following code:

    Public float height; 
    Public float desiredDistance;
    Public float heightDamp; 
    Public float rotDamp; 
  4. Recall that the Update() loop of any GameObject gets called repeatedly while the game simulation is running, which makes this method a great candidate in which we can put our main camera logic. Hence, inside the Update() loop of this script, we will call a UpdateRotAndTrans() custom method, which will contain the actual camera logic. We will place this logic inside the UpdateRotAndTrans() method. This method will update the rotation (facing angle) and translation (position) of the camera in the world; this is how GameCam will accomplish the stated goal of moving in the world and tracking the player:

    void Update() { 
  5. Above the update loop, let's implement the UpdateRotAndTrans() method as follows:

    void UpdateRotAndTrans () { 
      // to be filled in
  6. Inside this method, step 1 of our algorithm is accomplished with a sanity check on trackObj. By checking for null and reporting an error to debugLog, we can make catching bugs much easier by looking at the console. This is shown in the following code:

    if (trackObj) { 
    else { 
      Debug.Log("GameCamera:Error,trackObj invalid");
  7. Step 2 of our algorithm is to store the desired rotation and height in two local float variables. In the case of the height, we offset the height of trackObj by an exposed global variable height so that we can adjust the specifics of the object as shown in the following code (sometimes an object may have its transform 0 at the ground plane, which would not look pleasing, we need numbers to tweak):

    DesiredRotationAngle = trackObj.transform.eulerAngles.y; 
    DesiredHeight = trackObj.transform.position.y + height; 
  8. We also need to store the local variants of the preceding code for processing in our algorithm. Note the simplified but similar code compared to the code in the previous step. Remember that the this pointer is implied if we don't explicitly place it in front of a component (such as transform):

    float RotAngle = transform.eulerAngles.y; 
    float Height = transform.position.y; 
  9. Step 3 of our algorithm is where we do the actual LERP (linear interpolation) of the current and destination values for y-axis rotation and height. Remember that making use of the LERP method between two values means having to calculate a series of new values between the start and end that differs between one another by a constant amount.

    Remember that Euler angles are the rotation about the cardinal axes, and Euler y indicates the horizontal angle of the object. Since these values change, we smooth out the current rotation and height more with a smaller dampening value, and we tighten the interpolation with a larger value.

    Also note that we multiply heightDamp by Time.deltaTime in order to make the height interpolation frame rate independent, and instead dependent on elapsed time, as follows:

    RotAngle = Mathf.LerpAngle 
    (RotAngle, DesiredRotationAngle, rotDamp); 
    Height = Mathf.Lerp 
    (Height, DesiredHeight, heightDamp * Time.deltaTime); 
  10. The fourth and last step in our GameCam algorithm is to compute the position of the camera.

    Now that we have an interpolated rotation and height, we will place the camera behind trackObject at the interpolated height and angle. To do this, we will take the facing vector of trackObject and scale it by the negative value of desiredDistance to find a vector pointing in the opposite direction to trackObject; doing this requires us to convert eulerAngles to Quaternion to simplify the math (we can do it with one API function!).

    Adding this to the trackObject position and setting the height gives the desired offset behind the object, as shown in the following code:

    Quaternion CurrentRotation = Quaternion.Euler 
    (0.0f, RotAngle, 0.0f);
    Vector3 pos = trackObj.transform.position; 
    pos -= 
    CurrentRotation * Vector3.forward * desiredDistance; 
    pos.y = Height; 
    transform.position = pos;
  11. As a final step, we point the LookAt GameObject reference of the camera to the center of trackObject so that it is always precisely in the middle of the field of view. It is most important to never lose the object you are tracking in a 3D game. This is critical!

    transform.LookAt (trackObj.transform.position);

Congratulations! We have now written our first camera class that can smoothly track a rotating and translating object. To test this class, let's set the following default values in the Inspector pane as seen in the previous screenshot:

  • TrackObj: Set this to the Player1 object by dragging-and-dropping the object reference from the Hierarchy tab to the trackObj reference in the object inspector.

  • Height: Set this to 0.25. In general, the lower the camera, the more dramatic the effect but the less playable the game will be (because the user can see less of the world on screen).

  • Desired Distance: Set this to 4. At this setting, we can see the character framed nicely on screen when it is both moving and standing still.

  • Rot Damp: Set this to 0.01. The smaller this value, the looser and more interesting the rotation effect. The larger this value, the more tense the spring in the interpolation.

  • Height Damp: Set this to 0.5. The smaller this value, the looser and more interesting the height blending effect.

Once the player controls are developed (refer to the next section), try experimenting with these values and see what happens.


Downloading the example code

You can download the example code files for all Packt books you have purchased via your account at If you purchased this book elsewhere, you can visit and register to have the files e-mailed directly to you.