camera follow script

Create A Camera Follow Script In Unity! FREE Tutorial #8

Unity 3D MMO Tutorial Series

Welcome to the Camera Follow Script tutorial #8 for the Unity MMO series. In this tutorial, we will create the script to create a custom follow camera for the player character. If you are looking to start this series, go here. If you have not finished tutorial #7, click here.

Why Code a Custom Camera Script Instead of Using Cinemachine in Unity?

Unity’s Cinemachine is a powerful tool that can adapt to a wide variety of gaming conditions. Nonetheless, sometimes, coming up with an original camera follow script on your own, rather than using Cinemachine, is the better option.

To have complete say and flexibility, a custom-made script is written. Writing your own camera code provides complete control and customization.

When deciding whether to write your own script or use Cinemachine, performance is an essential factor. Cinemachine may not be suitable for advanced camera configurations with high-performance needs. In such circumstances, a tailor-made script may work better.

Lastly, a script you write could be a great educational opportunity. Making a custom script to address your camera requirements is a great way to learn more about cameras in Unity and how to use them successfully in future projects.

Cinemachine’s adaptability to a wide range of camera setups is unquestionable. There are, however, circumstances in which a tailor-made script is the better option to meet unique requirements and preferences.

Create a New Camera Follow Script

Let’s begin by creating a new script in the script folder. Name this script “CameraController.” Delete the code blocks from the class. We will code everything from scratch.

using UnityEngine;
public class CameraController : MonoBehaviour
{
}

Transform That Target

Tracking player position in Unity is necessary for camera movement. The “[SerializeField] Transform followTarget;” script serves this purpose by storing a reference to the player character’s Transform component. “LateUpdate” enables smooth object tracking in the virtual world.

The Transform component holds valuable information, illuminating the player’s location, orientation, and size. This attribute is crucial for creating dynamic in-game elements, providing updated data on the player’s movements.

The “[SerializeField]” attribute streamlines the process of modifying the script’s behavior in the inspector while running the game. This simplifies the development process, enabling more time and resources dedicated to game design’s creative aspects.

Add the following code to provide a link to the player character. “Transform” provides more information about the location of objects than “GameObject.”

using UnityEngine;
public class CameraController : MonoBehaviour
{
    [SerializeField] Transform followTarget; //the player character
}

LateUpdate for Dinner

In Unity game development, it is paramount to grasp the distinction between the “Update” And “LateUpdate” methods. These methods dictate the timing of when modifications made to your in-game objects will become visible on the screen.

The “Update” method shows changes in real time. The outcome can significantly change based on the order in which you update multiple objects.

The “LateUpdate” method is called once per frame, only after the execution of all “Update” methods. This method is particularly useful when you wish to guarantee that specific updates precede others. In your game, you will want to ensure that the camera always follows the player, unrestricted by any other updates.

When the camera’s position needs to be updated to follow the player, “LateUpdate” is the optimal choice. Updating the camera’s position in the “Update” method creates a lag in movement. “LateUpdate” ensures smooth camera movement for the player.

In your camera follow script, add a “LateUpdate” method in the code editor:

using UnityEngine;
public class CameraController : MonoBehaviour
{
    [SerializeField] Transform followTarget; //the player character
    private void LateUpdate()
    {
        //This is where the heart of the camera follow system is coded
    } 
}

What Position is This?

Let’s add another variable to the code to store how far we want the camera from the Player character. Add the following code under the “followTarget” variable:

 [SerializeField] float distance = 5f;

The variable “distance” is how far our camera will be from the player character. We will set the initial value to 5 units. The [SerializeField] attribute allows changing the variable to fine-tune in the Unity inspector.

The next bit of code will be inside the “LateUpdate” method. Code this line, and we will talk about it.

transform.position = followTarget.position - followTarget.forward * distance;

This line of code allows for smooth tracking of one object by another. The “followTarget.position” is the tracked object’s position, while “followTarget.forward” sets the direction. “distance” determines the space between the two objects.

This line of code creates believable movement using the camera for a smooth follow. This code is reasonably understandable.

The following line of code is nothing complicated, either. Drop this code under the last line of code:

transform.LookAt(followTarget.position);

This vital code keeps your hero in sight during gameplay. It tells the camera to stay fixed on the “followTarget” character, keeping them at the center of the screen. The result is a seamless, dynamic experience where your hero is always front and center.

Adding the Camera Follow Script Component

Before moving on, we must add the script to the camera. However, the Main Camera needs to be moved out of the prefab and into its place in the hierarchy. The camera cannot be removed from the prefab by just moving it out or deleting it. Give it try. You will receive a message saying so.

Camera Follow Script
The camera cannot be moved. Yet

The pop-up says we can open the Prefab in Prefab Mode to restructure the asset. Thats what we will do. Click on the Player in the Hierarchy and then click the arrow to the right (circle) to enter Prefab Mode. Then click the arrow to the left (arrow) to open the Player structure. Find the Main Camera and delete it. Exit the Prefab Mode to get back into the Hierarchy by clicking the arrow left of Player.

Camera Follow Script
Delete Main Camera

In the Hierarchy tab, right-click and click “Camera” to create a new camera for the scene. Rename the “Camera” to “Follow Camera.” Like before, add the “CameraController” script as a component and drag the Player into the “Follow Target” slot.

Now you can run the game and run the player around again.

Follow Cam in Action

Awesome! The camera follows the Player around as we wanted it to. However, the distance between the camera and the player character is too far. Let’s change that to 2.5 in the Inspector. Rerun the game if you aren’t already running. As you notice, the camera focuses on the Player character’s feet. That’s not good for our scenario.

Camera Follow Script
Camera Focus

Ready, Offset, Go!

To see all of the Player character in the game window, we need to frame the character in the camera just like if we were shooting a movie. We need a vector offset that can be adjusted to our liking in the inspector. Let’s make a “Vector2” variable and name it “framingOffset.”

[SerializeField] Vector2 framingOffset;

Put the above code at the top of the class with the other variables. This code initializes the x and y values of a vector. We will use the y value to move the camera up in the frame to see all of the Player character.

We need one more line of code to frame the Player character in the camera continuously. In “LateUpdate(),” put the following code:

 transform.position += new Vector3(framingOffset.x, framingOffset.y, 0);

“transform.position” refers to the camera. The “+=” operator means that we are adding a value to the current value of “transform.position” every frame. “new Vector3(framingOffset.x, framingOffset.y, 0f)” creates a “new Vector3” with the x and y values of the “Vector2” variable called “framingOffset.” The third component (z) is set to 0.

Run the game and see the whole character run at a comfortable distance! Great Job so far!

Around and Around We Go!

In this section, I want to add some code to make the camera go around the Player character in a circular pattern while holding the left mouse button and moving the mouse. That way, we can briefly see our character’s face in the game window.

When the left mouse button is released, the camera will reset to the original position behind the Player character.

Let’s add a couple of variables for the X and Y values to save from the player character. Add: “float rotationX; float rotationY;”

public class CameraController : MonoBehaviour
{
    [SerializeField] Transform followTarget; //the player character
    [SerializeField] float distance = 5f;
    [SerializeField] Vector2 framingOffset;
    float rotationX;
    float rotationY;
    private void LateUpdate()
    {
        transform.position = followTarget.position - followTarget.forward * distance;
        transform.LookAt(followTarget.position);
        transform.position += new Vector3(framingOffset.x, framingOffset.y, 0);
    } 
}

These variables store rotations applied to an object in 3D graphics along the X and Y axes. These values calculate the full rotation of an object in the virtual world.

Note that these variables only get declared, not initialized. We will assign values to these variables before using them, as they currently don’t have any values.

Quaternions in 3D Graphics and Game Development

Include the line of code “var targetRotation = Quaternion.Euler(rotationX, rotationY, 0f);” into your LateUpdate method.

public class CameraController : MonoBehaviour
{
    [SerializeField] Transform followTarget; //the player character
    [SerializeField] float distance = 5f;
    [SerializeField] Vector2 framingOffset;
    float rotationX;
    float rotationY;
    private void LateUpdate()
    {
        transform.position = followTarget.position - followTarget.forward * distance;
        transform.LookAt(followTarget.position); 
        transform.position += new Vector3(framingOffset.x, framingOffset.y, 0); 
      
        var targetRotation = Quaternion.Euler(rotationX, rotationY, 0);
    } 
}

About Var

In programming, the keyword “var” is used to declare a variable in many programming languages, including C#, JavaScript, and others. The var keyword allows the compiler to infer the variable type, making the code more concise. For example:

var name = “John Doe”;

In this case, the type “name” is inferred to be “string” based on the value assigned to it. The same declaration using an explicit type would look like this:

string name = “John Doe”;

The advantage of using “var” in Unity, it can make the code more concise and reduce the amount of code needed to declare a variable.

In C#, while other variable types can represent a Quaternion, developers commonly use “var” because they know the type of object returned by the “Quaternion.Euler” method is a Quaternion. In this case, using “var” makes the code more concise and reduces the amount of boilerplate code needed.

gaming keyboard

What are Quaternions?

Quaternions offer a unique solution to the complex challenge of representing 3D rotations. Quaternions offer a more convenient and efficient way to represent rotations, an alternative to Euler angles or rotation matrices.

Made up of a scalar component and three vector components, quaternions allow for easy manipulation of 3D objects. They are famous for solving gimbal lock, a problem with Euler angles in rotational representation.

Not only do quaternions stand out for avoiding gimbal lock, but also for their compact and expressive representation of rotations. Easy and effective manipulation of 3D objects with quaternions makes them crucial in computer graphics and game development.

Quaternions are a versatile and powerful tool, providing a convenient and efficient way to represent 3D rotations. Quaternions overcome the limitations of traditional methods, making them essential for creating stunning graphics and immersive gaming experiences.

In short, a Quaternion is a computer representation of 360 degrees.

targetRotation

An instance of the Quaternion data type is instantiated, and a new variable named targetRotation is given the value 360 degrees. Using the Quaternion.Euler method, which requires three arguments (rotationX, rotationY, and 0f), the Quaternion is constructed.

Euler angles can be converted to a Quaternion using the Euler technique for rotations about X, Y, and Z axes. RotationX and rotationY represent rotations about X and Y axes, while 0f represents rotation about the Z axis.

The “targetRotation” variable can describe a 3D object’s rotation based on the returned Quaternion object. Quaternions are widely used in 3D graphics as they offer a more intuitive representation of rotations and require less effort.

Add Some Mouse Flare to the Camera Follow Script

To make the left mouse button relevant to the “follow camera,” add an “if” statement to the code. In English, we can read this statement as follows: If we press down the left mouse button (Input.GetMouseButton(0)) AND we are not pressing down the right mouse button (!Input.GetMouseButton(1)), then perform an action.

In the “LateUpdate()” method, below the “var” method, put an “if” statement like this:

if (Input.GetMouseButton(0) && !Input.GetMouseButton(1))
{
}

After the empty “if” statement, create an “else” statement. Let’s use the camera-following code that we have already written. To do this, we can copy and paste the three lines of code responsible for the camera-following behavior into the “else” statement.

else
{
     transform.position = followTarget.position - followTarget.forward * distance;
     transform.LookAt(followTarget.position);
     transform.position += new Vector3(framingOffset.x, framingOffset.y, 0);
}

Run the game to make sure it still behaves the same as it did before the change. If not, check your code to make sure it matches what I have:

using UnityEngine;
public class CameraController : MonoBehaviour
{
    [SerializeField] Transform followTarget; //the player character
    [SerializeField] float distance = 5f;
    [SerializeField] Vector2 framingOffset;
    float rotationX;
    float rotationY;
    private void LateUpdate()
    {
        var targetRotation = Quaternion.Euler(rotationX, rotationY, 0);
        if (Input.GetMouseButton(0) && !Input.GetMouseButton(1))
        {
        }
        else
        {
            transform.position = followTarget.position - followTarget.forward * distance;
            transform.LookAt(followTarget.position);
            transform.position += new Vector3(framingOffset.x, framingOffset.y, 0);
        }
    }
}

Going to the Store

First, let’s create a variable that calculates and stores the camera’s position as we move it around the Player. This code block requires us to calculate the “framingOffset” along with the camera’s position to ensure it is always at the right height.

To that end, place the following code under the declaration of “targetRotation:”

var focusPosition = followTarget.position + new Vector3(framingOffset.x, framingOffset.y);

We need to set a speed at which the camera can move around the Y-axis. Let’s make this adjustable in the inspector. At the top of the class, put this code:

[SerializeField] float rotationSpeed = 2f;

Inside the “if” statement, we need to make the code that will use the left mouse button and the movement of the mouse itself. Let’s use the “rotationX” and “rotationY” variables.

 if (Input.GetMouseButton(0) && !Input.GetMouseButton(1))
 {
     rotationX += Input.GetAxis("Mouse Y") * rotationSpeed;
     rotationY += Input.GetAxis("Mouse X") * rotationSpeed;
 }

Now let’s calculate the rotation around the Player and store it in the Quaternion we discussed earlier.

 transform.rotation = targetRotation;

One more line of code, then we will test it out.

The line of code “transform.position = focusPosition - targetRotation * new Vector3(0, 0, distance);” This line of code moves the camera to a different position relative to the Player character and changes the direction the camera faces based on how it’s rotated.

transform.position = focusPosition - targetRotation * new Vector3(0, 0, distance);

The camera will rotate around the Player character on the Y-axis and the X-axis. Try it out. Run the game and hold the left mouse button down. Move your mouse left and right, up and down. It works as intended. So far.

Falsely Executed!

If you notice, after you release the left mouse button, it no longer functions properly. That’s because we need to reset some things after releasing the mouse. Let’s do that now.

Create a “bool” called “executed” and set it to “false” at the top of the class:

private bool executed = false;

Make another “if” statement at the top of the “if” statement we already have.

if (!executed)
{
    executed = true;
    rotationX = 0;
    rotationY = followTarget.eulerAngles.y;
}

The “if (!executed)” block initializes the camera’s rotation values “rotationX” and “rotationY” when the left mouse button is clicked, and the right mouse button is not clicked.

The purpose of the “executed” variable ensures that the initialization code is executed only once rather than every time the left mouse button is held down.

When “executed” is “false,” the block sets the “executed” variable to “true” and initializes the “rotationX” to 0 and the “rotationY” to the y-axis rotation of the “followTarget.” This ensures that the camera starts at the correct angle when the player begins to rotate the camera with the mouse.

Subsequent mouse movements will update the “rotationX” and “rotationY” values, but the initialization code will not be executed again since the “executed” variable has been set to “true.”

Check it out! Now it kinda does what we want. There are a couple of more tiny things that need to be done to finish this tutorial.

Clamp It!

Since we don’t want the rotation on the x-axis to run amuck, we need to add what is known as a “clamp” to the “rotationX” value. At the top of the class with the other “[SerializedField],” type or paste the code:

[SerializeField] float minVertAngle = 0;
[SerializeField] float maxVertAngle = 85f;

This code represents the minimum and maximum angles that the camera can rotate on the x-axis. These are good angles to start at to keep our camera from going beyond the top of the plane to the bottom. In the “if” statement, under the “rotationX” line, add the code:

rotationX = Mathf.Clamp(rotationX, minVertAngle, maxVertAngle);

Rerun the game and press and hold the left mouse button. Rotate the camera above the Player character and try to move the camera below. Play with the values in the inspector if you wish. Before, the camera would circle completely around the x-axis, but now we have clamped the rotation at the ground and top-down levels.

Camera Clamped

One More Teenie Tiny Thing…..

If you have noticed, the camera doesn’t reset properly when letting go of the left mouse button while circling the y-axis. One very short line of code can fix this in an instant. In the “else” statement, add the code:

executed = false;

Setting “executed” to “false” resets the camera when the left mouse button is pressed again!

Congratulations! You have made it through another Unity 3D MMO Tutorial!

Here is the code in full:

using UnityEngine;
public class CameraController : MonoBehaviour
{
    [SerializeField] Transform followTarget; //the player character
    [SerializeField] float distance = 5f;
    [SerializeField] float rotationSpeed = 2f;
    [SerializeField] Vector2 framingOffset;
    [SerializeField] float minVertAngle = 0;
    [SerializeField] float maxVertAngle = 85f;
    private bool executed = false;
    float rotationX;
    float rotationY;
    private void LateUpdate()
    {
        var targetRotation = Quaternion.Euler(rotationX, rotationY, 0);
        var focusPosition = followTarget.position + new Vector3(framingOffset.x, framingOffset.y);
        if (Input.GetMouseButton(0) && !Input.GetMouseButton(1))
        {
            if (!executed)
            {
                executed = true;
                rotationX = 0;
                rotationY = followTarget.eulerAngles.y;
            }
            rotationX += Input.GetAxis("Mouse Y") * rotationSpeed;
            rotationX = Mathf.Clamp(rotationX, minVertAngle, maxVertAngle);
            rotationY += Input.GetAxis("Mouse X") * rotationSpeed;
            transform.rotation = targetRotation;
            transform.position = focusPosition - targetRotation * new Vector3(0, 0, distance);
        }
        else
        {
            executed = false;
            transform.position = followTarget.position - followTarget.forward * distance;
            transform.LookAt(followTarget.position);
            transform.position += new Vector3(framingOffset.x, framingOffset.y, 0);   
        }
    }
}

This tutorial has been the longest one yet. This was a fun one. There is a lot more fun to be had, so buckle up!

Until next time, May The Force Be With You, Always. – Dragon

Tagged

1 thought on “Create A Camera Follow Script In Unity! FREE Tutorial #8

Leave a Reply

Your email address will not be published. Required fields are marked *