Skip to main content

RTS Style Pan and Zoom Camera using Cinemachine

This lesson will have you create a camera that can pan and zoom using a Cinemachine Virtual Camera and the Input System.

This is similar to RTS Style Cameras in Command and Conquer, Star Craft, and League of Legends.

You can download an example build of this here.

Create a scene and add the terrain or map to it.

Install the Input System if it is not already.

Create new Input Actions. Save them with an appropriate name such as MouseRTSControls.

Click + to add an action map. This will handle the pan and zoom controls.

Add two actions Pan and Zoom.

Pan will have the action type Pass Through and the Control Type Vector2. This is for the x and y of the mouse.

Add a binding for Zoom and set it to Mouse > Position.

Zoom will have the type as Pass Through and the control type to axis as this is for the scroll wheel.

Set the binding for Zoom to Mouse > Scroll > Y.

Also click the + next to processors and select Normalize. Set the min to -1 and the max to 1.

Save the Input Actions

If you haven't installed the Cinemachine Package installit with the Package Manager.

Add a Cinemachine Virtual Camera.

Position the camera above the map / terrain.

On the Virtual Camera (named here CM Pan Zoom Camera) add the Cinemachine InputProvider component.

The cinemachine input provider handles mouse input for cinemachine virtual cameras.

Expand the input actions you created.

Drag the Pan controls to the XY Axis and the Zoom control to the Z axis.

Next create an empty GameObject to manage the camera.

Create a new Script for managing the camera. In this example it is called PanAndZoomCameraControl.

Attach this script to the CameraManager.

Open the script in your editor. We will use this to manage and update the position of the camera.

Add in points 1-8 shown below.

1 and 2 input the InputSystem and Cinemachine packages

3-8 setup different variables for the camera namagement.

3 is for the Cinemachine Input Provider

4 is the virtual camera

5 is the virtual camera transform

6 is the percentage of the screen to use for scrolling the camera. For example 0.05 would mean that 5% of the screen at the top, left, bottom, and right would be used to scroll when the mouse moves within that percentage of the edge of the screen.

7 is the speed that the camera will pan (scroll) on the x and y axis.

8 is the speed to zoom in and out (changing the field of view of the camera).

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
// 1. Add the input system package
using UnityEngine.InputSystem;
// 2. Add the cinemachine package
using Cinemachine;

public class PanAndZoomCameraControl : MonoBehaviour
{
// 3. The input provider for handling mouse input to the camera
[SerializeField]
private CinemachineInputProvider _inputProvider;
// 4. The virtual camera for the camera
[SerializeField]
private CinemachineVirtualCamera _virtualCamera;

// 5. Transform of the camera
[SerializeField]
private Transform _cameraTransform;

// 6. When the mouse is in this percent of the edge scroll the screen
[SerializeField]
private float _scrollBorderPercent = 0.05f;

// 7. Speed to pan the camera
[SerializeField]
private float _panSpeed = 50.0f;

// 8. Speed to zoom the camera (change field of view)
[SerializeField]
private float _zoomSpeed= 5.0f;
}

Next we want to link up the components to this script. Save the script.

Head back to Unity.

Drag the virtual camera into each of the three variables:

  • Input Provider
  • Virtual Camera
  • Camera Transform

Now we need to write the code to Pan the camera.

Points 9-15 below show this.

First we create a function to manage panning the screen. This is point 9.

The function is called PanScreenInDirection and takes two arguments x and y which are both floats. This will be the x and y position of the mouse.

Next we create a Vector3 to store the x, y, and z position of the camera. Note x will be the x mouse position and z will be the y mouse position.

We initially set this to the world origin by using Vector3.zero as shown in 10.

Now we need to check if the mouse if near the edge of the screen. This is shown in 11-14.

11 checks if the mouse position y is greater than the Screen height - the scroll border set in 6.

If it is we increment the panDirection y value by +1.

Then we do the same if it is in the bottom scroll border percent as shown in 12. This time we reduce y by 1.

We do similar checks for the x position of the mouse on the screen as shown in 13 and 14.

Finally in 15 we need to move the camera. We use the lerp function. This takes three arguments.

  1. The current position of the camera (or other object)
  2. The position to move the camera (or other object) to.
  3. The linear scale (in this case Time expired) to move the object.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
// 1. Add the input system package
using UnityEngine.InputSystem;
// 2. Add the cinemachine package
using Cinemachine;

public class PanAndZoomCameraControl : MonoBehaviour
{
// 3. The input provider for handling mouse input to the camera
[SerializeField]
private CinemachineInputProvider _inputProvider;
// 4. The virtual camera for the camera
[SerializeField]
private CinemachineVirtualCamera _virtualCamera;

// 5. Transform of the camera
[SerializeField]
private Transform _cameraTransform;

// 6. When the mouse is in this percent of the edge scroll the screen
[SerializeField]
private float _scrollBorderPercent = 0.05f;

// 7. Speed to pan the camera
[SerializeField]
private float _panSpeed = 50.0f;

// 8. Speed to zoom the camera (change field of view)
[SerializeField]
private float _zoomSpeed= 5.0f;

// 9. Pan the screen in the x and y direction
public void PanScreenInDirection(float x, float y) {
// 10. Set the panDirection to 0,0,0
Vector3 panDirection = Vector3.zero;

/* 11.
If the y mouse position is within the percent of the top border pan the screen up
*/
if(y >= Screen.height * (1 - _scrollBorderPercent)) {
panDirection.z += 1;
} else
/* 12.
If the y mouse position is within the percent of the bottom border pan the screen down
*/
if(y <= Screen.height * _scrollBorderPercent) {
panDirection.z -= 1;
}
/* 13.
If the x mouse position is within the percent of the left border pan the screen left
*/
if(x >= Screen.width * (1 - _scrollBorderPercent)) {
panDirection.x += 1;
} else
/* 14.
If the x mouse position is within the percent of the left border pan the screen left
*/
if(x <= Screen.width * _scrollBorderPercent) {
panDirection.x -= 1;
}

/* 15.
Move the camera transform to the new panDirection position.
Lerp takes the current position, the new position and then the time scale to move
*/
_cameraTransform.position = Vector3.Lerp(_cameraTransform.position,
_cameraTransform.position + panDirection * _panSpeed,
Time.deltaTime
);
}

Now we need to add an Update function to the script.

Point 16 will setup three variables for the x and y position of the mouse and also z for the position +/- of the scroll wheel. We will only use x and y here, z will be used for the zoom function we will create later.

17 will check if the x or y position has changed.

If either has we will run the PanScreenInDirection function passing the x and y mouse position to it.

// Update is called once per frame
void Update()
{
// 16. Setup the mouse x and y inputs position and the z (scroll) values
float x = _inputProvider.GetAxisValue(0);
float y = _inputProvider.GetAxisValue(1);
float z = _inputProvider.GetAxisValue(2);

// 17. If the mouse has moved in x and y position move the camera
if(x !=0 || y != 0) {
PanScreenInDirection(x,y);
}
}

Save the code and return to Unity and play the Scene. You should now be able to pan the camera when the mouse moves to the edge of the window.

Finally we need to add the zoom in / change the field of view.

We will add in another function ZoomScreen as shown in point 18. This take the z value of the scroll.

Point 19 creates a float to store the current camera lens field of view of the virtual camera.

Point 20 updates the field of view of the camera using the lerp function taking the current field of view, the current field of view with the z scroll value (+/-) to move the field of view in or out and finally the zoom speed multiplied by the time since the last frame / update.

    /* 18. 
Similar to 15. but for the zoom in and out
We adjust the field of view of the camera rather than moving the z axis
*/
public void ZoomScreen(float z) {
// 19. Get the camera lens field of view
float fieldOfView = _virtualCamera.m_Lens.FieldOfView;
// 20. Update the field of view to the new z position and adjust by the _zoomSpeed
_virtualCamera.m_Lens.FieldOfView = Mathf.Lerp(fieldOfView,
fieldOfView + z,
_zoomSpeed * Time.deltaTime
);
}

Finally we need to modify the update function to call the ZoomScreen function if the scroll wheel is used, i.e. if z not equal to 0. This is shown in point 21.

    // Update is called once per frame
void Update()
{
// 16. Setup the mouse x and y inputs position and the z (scroll) values
float x = _inputProvider.GetAxisValue(0);
float y = _inputProvider.GetAxisValue(1);
float z = _inputProvider.GetAxisValue(2);

// 17. If the mouse has moved in x and y position move the camera
if(x !=0 || y != 0) {
PanScreenInDirection(x,y);
}
// 21. If z (scrollwheel) has been pressed adjust the field of view.
if(z !=0) {
ZoomScreen(z);
}
}

Save and test your pan and zoom camera as it should now be complete.

The code below is the complete script for building the pan and zoom camera.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
// 1. Add the input system package
using UnityEngine.InputSystem;
// 2. Add the cinemachine package
using Cinemachine;

public class PanAndZoomCameraControl : MonoBehaviour
{
// 3. The input provider for handling mouse input to the camera
[SerializeField]
private CinemachineInputProvider _inputProvider;
// 4. The virtual camera for the camera
[SerializeField]
private CinemachineVirtualCamera _virtualCamera;

// 5. Transform of the camera
[SerializeField]
private Transform _cameraTransform;

// 6. When the mouse is in this percent of the edge scroll the screen
[SerializeField]
private float _scrollBorderPercent = 0.05f;

// 7. Speed to pan the camera
[SerializeField]
private float _panSpeed = 50.0f;

// 8. Speed to zoom the camera (change field of view)
[SerializeField]
private float _zoomSpeed= 5.0f;

// Update is called once per frame
void Update()
{
// 16. Setup the mouse x and y inputs position and the z (scroll) values
float x = _inputProvider.GetAxisValue(0);
float y = _inputProvider.GetAxisValue(1);
float z = _inputProvider.GetAxisValue(2);

// 17. If the mouse has moved in x and y position move the camera
if(x !=0 || y != 0) {
PanScreenInDirection(x,y);
}
// 21. If z (scrollwheel) has been pressed adjust the field of view.
if(z !=0) {
ZoomScreen(z);
}
}

// 9. Pan the screen in the x and y direction
public void PanScreenInDirection(float x, float y) {
// 10. Set the panDirection to 0,0,0
Vector3 panDirection = Vector3.zero;

/* 11.
If the y mouse position is within the percent of the top border pan the screen up
*/
if(y >= Screen.height * (1 - _scrollBorderPercent)) {
panDirection.z += 1;
} else
/* 12.
If the y mouse position is within the percent of the bottom border pan the screen down
*/
if(y <= Screen.height * _scrollBorderPercent) {
panDirection.z -= 1;
}
/* 13.
If the x mouse position is within the percent of the left border pan the screen left
*/
if(x >= Screen.width * (1 - _scrollBorderPercent)) {
panDirection.x += 1;
} else
/* 14.
If the x mouse position is within the percent of the left border pan the screen left
*/
if(x <= Screen.width * _scrollBorderPercent) {
panDirection.x -= 1;
}

/* 15.
Move the camera transform to the new panDirection position.
Lerp takes the current position, the new position and then the time scale to move
*/
_cameraTransform.position = Vector3.Lerp(_cameraTransform.position,
_cameraTransform.position + panDirection * _panSpeed,
Time.deltaTime
);
}

/* 18.
Similar to 15. but for the zoom in and out
We adjust the field of view of the camera rather than moving the z axis
*/
public void ZoomScreen(float z) {
// 19. Get the camera lens field of view
float fieldOfView = _virtualCamera.m_Lens.FieldOfView;
// 20. Update the field of view to the new z position and adjust by the _zoomSpeed
_virtualCamera.m_Lens.FieldOfView = Mathf.Lerp(fieldOfView,
fieldOfView + z,
_zoomSpeed * Time.deltaTime
);
}
}

View Quiz

Complete Lesson

Next Lesson

Reset Lesson