The camera is one of the most important elements in a 3D game. It acts as the player's eyes, letting them see the game world from different points of view. In Unity3D, a 3D camera works just like a film camera. It can be panned, tilted, and zoomed to frame scenes. This tutorial will teach you how to create multiple third person camera perspectives.
Project Setup
We need a simple project to test our camera scripts in. We'll need a scene with a ground plane that has a texture on it. This will make it easy to see how each camera moves and reacts to player inputs. When we're done, it should look like this:
Follow these steps to setup the project:
- Click File > New Project
- Name your project folder 3rd Person Cameras
- Click Create
- Click GameObject > Create Other > Directional Light
- Click GameObject > Create Other > Plane
- In the Inspector, find the Transform component and change the position X to 0, Y to -0.5 and Z to 0 so it will sit below the cube
- Download grid.png
- Drag grid.png into the Project panel
- Drag the grid texture from the project panel onto the Plane in the Hierarchy panel
- Select the Main Camera and move and rotate it into a position above and behind the cube
- Save your Scene (File > Save Scene) and name it Main
Create a Player
All of the cameras we're going to create will need a target: something to look at or follow. So, let's create a basic player we can move around with the arrow keys.
- Click GameObject > Create Other > Cube
- Rename it Player
- In the inspector, make sure its X, Y, and Z coordinates are all set to zero so we're sure the player is at the center of the 3D world
- Click Assets > Create > C# Script
- Name the script Player
- Drag the Player script from the Project panel onto the Player in the Hierarchy panel
In the Player script, add two public properties for the movement and turning speed. Then add the following code to your Update() method:
01 02 03 04 05 06 07 08 09 10 11 12 | public class Player : MonoBehaviour { public float movementSpeed = 10; public float turningSpeed = 60; void Update() { float horizontal = Input.GetAxis( "Horizontal" ) * turningSpeed * Time.deltaTime; transform.Rotate(0, horizontal, 0); float vertical = Input.GetAxis( "Vertical" ) * movementSpeed * Time.deltaTime; transform.Translate(0, 0, vertical); } } |
This gives the player controls similar to those of a tank. The horizontal axis (the left-or-right keys) turn the player around, while the vertical axis (up-or-down keys) move the player forward and backward.
The Look At Camera
This is the most basic 3rd person camera. It sits in a fixed location in the 3D world and tracks its target like a turret.
- Click Assets > Create > C# Script
- Name the script LookAtCamera
- Drag the LookAtCamera script from the Project panel onto the Main Camera in the Hierarchy panel
In the LookAtCamera script, create a public attribute for our camera's target at the top of the class. Public attributes are exposed in the Inspector and will allow us to assign the Player as the camera's target:
1 2 | public class LookAtCamera : MonoBehaviour { public GameObject target; |
Next, we need to tell our camera's transform to look at the target object. Thankfully, transform objects have a convenient LookAt() method we can use to do just that. We could do this in the Update() method, but instead we create a LateUpdate() method. As a rule of thumb, you should always use LateUpdate() instead of the Update() method in all camera scripts. LateUpdate() happens after Update() has finished, so the Player script has a chance to finish calculating the player's position before the camera calculates its position. This results in smoother camera motion:
1 2 3 | void LateUpdate() { transform.LookAt(target.transform); } |
The final script should look like:
1 2 3 4 5 6 7 | public class LookAtCamera : MonoBehaviour { public GameObject target; void LateUpdate() { transform.LookAt(target.transform); } } |
If you try to run the game now, you'll get errors complaining about UnassignedReferenceException. To prevent this, drag the Player from the Hierarchy panel and drop it on the script's Target property in the Inspector. The camera now has a valid target to look at.
The Dungeon Crawler Camera
This is the type of camera you'd typically find in games like Diablo, also known as a "dungeon crawler" game. The camera sits above the player and moves relative to the character, but never rotating.
- Click Assets > Create > C# Script
- Name the script DungeonCamera
- Drag the DungeonCamera script from the Project panel onto the Main Camera in the Hierarchy panel
In the DungeonCamera script, we once again need to create a public attribute for our camera's target. We also need to create a variable to store the offset between the camera and its target. The offset is represented as a Vector3 and will be used maintain the relative distance as the player moves around. You may notice that we don't give the offset a value when we first declare it. This is because we'll be calculating the value the first time the script is run. We can use the Start() method to do this:
1 2 3 4 5 6 7 | public class DungeonCamera : MonoBehaviour { public GameObject target; Vector3 offset; void Start() { offset = transform.position - target.transform.position; } |
In each frame we need to update the camera's position based on the player's position by applying the offset. As usual, this should be done in the LateUpdate() method:
1 2 3 4 | void LateUpdate() { Vector3 desiredPosition = target.transform.position + offset; tranform.position = desiredPosition; } |
Drag the Player from the Hierarchy panel to the script's Target property in the Inspector.
Optional Enhancements
You may notice that the camera movement is a bit stiff. It would be nice to dampen the movement slightly so that it takes some time to catch up to the player. We can do this using the Vector3.Lerp() method. Lerp linearly interpolates between two points, meaning it smoothly transitions from one point to another in a straight line.
In order to control how much damping is applied, we can create another public attribute called, what else, damping!
1 | public float damping = 1; |
The two points we can lerp between are the current position of the camera with damping applied, and the desired position with no damping.
1 2 3 4 | void LateUpdate() { Vector3 desiredPosition = target.transform.position + offset; Vector3 position = Vector3.Lerp(transform.position, desiredPosition, Time.deltaTime * damping); transform.position = position; |
Finally, we want the camera to keep looking at the player:
1 | transform.LookAt(target.transform.position); |
The final script looks like this:
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 | public class DungeonCamera : MonoBehaviour { public GameObject target; public float damping = 1; Vector3 offset; void Start() { offset = transform.position - target.transform.position; } void LateUpdate() { Vector3 desiredPosition = target.transform.position + offset; Vector3 position = Vector3.Lerp(transform.position, desiredPosition, Time.deltaTime * damping); transform.position = position; transform.LookAt(target.transform.position); } } |
The Follow Camera
This type of camera is commonly used in platforming games like Mario Galaxy. The camera sits behind and above the player and rotates around the character as they turn.
- Click Assets > Create > C# Script
- Name the script FollowCamera
- Drag the FollowCamera script from the Project panel onto the Main Camera in the Hierarchy panel
Like the Dungeon Crawler camera, the Follow camera is going to need a public attribute for a target, as well as an offset. The offset should be set in the Start() method:
1 2 3 4 5 6 7 | public class FollowCamera : MonoBehaviour { public GameObject target; Vector3 offset; void Start() { offset = target.transform.position - transform.position; } |
To orient the camera behind the target, we first need to get the angle of the target and turn it into a rotation in the LateUpdate() method:
1 2 3 | void LateUpdate() { float desiredAngle = target.transform.eulerAngles.y; Quaternion rotation = Quaternion.Euler(0, desiredAngle, 0); |
We can then multiply the offset by the rotation to orient the offset the same as the target. We then subtract the result from the position of the target.
1 | transform.position = target.transform.position - (rotation * offset); |
To keep looking at the player:
1 | transform.LookAt(target.transform); |
Drag the Player from the Hierarchy panel to the script's Target property in the Inspector.
Optional Enhancements
The same damping motion we applied to the Dungeon camera can be applied to the Follow camera. First, we add a damping attribute to make it easier to adjust the damping:
1 | public float damping = 1; |
Instead of lerping between two points like we did with the Dungeon camera, we'll be lerping between the angle of the camera and the angle of the target. So, rather thanVector3.Lerp(), we use the Mathf.LerpAngle() method. We replace the original angle code with:
1 2 3 4 | float currentAngle = transform.eulerAngles.y; float desiredAngle = target.transform.eulerAngles.y; float angle = Mathf.LerpAngle(currentAngle, desiredAngle, Time.deltaTime * damping); Quaternion rotation = Quaternion.Euler(0, angle, 0); |
The final script should look like:
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 | public class FollowCamera : MonoBehaviour { public GameObject target; public float damping = 1; Vector3 offset; void Start() { offset = target.transform.position - transform.position; } void LateUpdate() { float currentAngle = transform.eulerAngles.y; float desiredAngle = target.transform.eulerAngles.y; float angle = Mathf.LerpAngle(currentAngle, desiredAngle, Time.deltaTime * damping); Quaternion rotation = Quaternion.Euler(0, angle, 0); transform.position = target.transform.position - (rotation * offset); transform.LookAt(target.transform); } } |
The Mouse Aim Camera
This type of camera is similar to the Follow camera, except the rotation is controlled by the mouse, which then points the character in whatever direction the camera is facing.
- Click Assets > Create > C# Script
- Name the script DungeonCamera
- Drag the DungeonCamera script from the Project panel onto the Main Camera in the Hierarchy panel
Like the Follow camera, the Mouse Aim camera is going to need a public attribute for a target and rotation speed, as well as well as an offset. The offset should be set in the Start() method:
1 2 3 4 5 6 7 8 | public class MouseAimCamera : MonoBehaviour { public GameObject target; public float rotateSpeed = 5; Vector3 offset; void Start() { offset = target.transform.position - transform.position; } |
We can access the horizontal axis of the mouse (aka: Mouse X) and use it to rotate the target.
1 2 | float horizontal = Input.GetAxis( "Mouse X" ) * rotateSpeed; target.transform.Rotate(0, horizontal, 0); |
We then orient the offset in the same direction and subtract it from the target's position to keep the camera behind the target.
1 2 3 4 5 | float desiredAngle = target.transform.eulerAngles.y; Quaternion rotation = Quaternion.Euler(0, desiredAngle, 0); transform.position = target.transform.position - (rotation * offset); transform.LookAt(target.transform); |
Drag the Player from the Hierarchy panel to the script's Target property in the Inspector.
Unlike the other scripts, we're not going to add any damping to the camera's movement. Because of the precise nature of the mouse, it can often lead to motion sickness.
The final script should look like this:
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 | public class MouseAimCamera : MonoBehaviour { public GameObject target; public float rotateSpeed = 5; Vector3 offset; void Start() { offset = target.transform.position - transform.position; } void LateUpdate() { float horizontal = Input.GetAxis( "Mouse X" ) * rotateSpeed; target.transform.Rotate(0, horizontal, 0); float desiredAngle = target.transform.eulerAngles.y; Quaternion rotation = Quaternion.Euler(0, desiredAngle, 0); transform.position = target.transform.position - (rotation * offset); transform.LookAt(target.transform); } } |
Final Thoughts
More than one camera script can be applied to a single camera at the same time. To switch between the different scripts, enable the script you want by checking it and unchecking all the others. This could be useful for switching to a different camera style for establishing shots or cut scenes.
Unity also comes with several camera scripts you can use right out of the box. The scripts are well documented, easy to customize and make great guides for building and improving your own camera scripts.
- Click Assets > Import Package > Scripts
- Uncheck everything except Camera Scripts
Conclusion
Unity makes it easy to build a wide variety of cameras for any type of game. With just a few lines of code, the most important element in your game is ready to go. While some may find the math a little intimidating, Unity provides so many useful convenience functions that most of the heavy calculations are already done for you.
Click here to download the complete Unity project.
No comments:
Post a Comment