DEV Community

Matthew Odle
Matthew Odle

Posted on

Simple 3D Camera Movement in Unity

I'm making a 3D village management sim, and when things get crazy, I like to be able to zoom in on the action in a specific region.

This camera control script looks for horizontal and vertical axis inputs (WASD and arrow keys) to allow the player to pan around. It also watches the mouse scrollwheel to allow the player to raise and lower the camera.

Just drop it on your Main Camera gameobject in your scene, and you're good to go.

The basic pattern is:

  • if input
    • create a new Vector3(x, y, z) using the target inputs
      • x and z for pan, y for zoom, the rest 0
    • multiply the Vector3 by some predefined speed
    • add the new Vector3 to the position of the camera transform

I've inverted the mouse scrollwheel input so that scrolling up zooms in and scrolling down zooms out.

using UnityEngine;

public class CameraController : MonoBehaviour {

    private float moveSpeed = 0.5f;
    private float scrollSpeed = 10f;

    void Update () {
        if (Input.GetAxisRaw("Horizontal") != 0 || Input.GetAxisRaw("Vertical") != 0) {
            transform.position += moveSpeed * new Vector3(Input.GetAxisRaw("Horizontal"), 0, Input.GetAxisRaw("Vertical"));
        }

        if (Input.GetAxis("Mouse ScrollWheel") != 0) {
            transform.position += scrollSpeed * new Vector3(0, -Input.GetAxis("Mouse ScrollWheel"), 0);
        }
    }

}

Enter fullscreen mode Exit fullscreen mode

Top comments (6)

Collapse
 
k1pash profile image
Christopher Toman • Edited

Hey Matthew,

by adding movement in inconsistent loop, Update in this case, clients with higher FPS are more swift and less smooth. To counter the FPS differences, you could do two things:

  • Use Time.deltaTime

    deltaTime is duration of lastFrame, if you multiplay any value what is based on FPS loops (Update), you'll have consistent results on different frame rates. Check the documentation for it. Usually your moveSpeed or scrollSpeed have to be 10 times bigger.


using UnityEngine;

public class CameraController : MonoBehaviour {

    private float moveSpeed = 0.5f;
    private float scrollSpeed = 10f;

    void Update () {
        if (Input.GetAxisRaw("Horizontal") != 0 || Input.GetAxisRaw("Vertical") != 0) {
            transform.position += moveSpeed * new Vector3(Input.GetAxisRaw("Horizontal"), 0, Input.GetAxisRaw("Vertical")) * Time.deltaTime;
        }

        if (Input.GetAxis("Mouse ScrollWheel") != 0) {
            transform.position += scrollSpeed * new Vector3(0, -Input.GetAxis("Mouse ScrollWheel"), 0) * Time.deltaTime;
        }
    }

}

  • Apply the movement in consistent loop

    FixedUpdate is a loop for physics engine and it runs constant 60 fps. The FPS of the game don't affect the frame rate of FixedUpdate. But the inputs as keystrokes and mouse wheel is detected in Update loop, so to not miss key strokes, you have to do it like so:

using UnityEngine;

public class CameraController : MonoBehaviour {

    private float moveSpeed = 0.5f;
    private float scrollSpeed = 10f;

    float horizontalInput;
    float verticalInput;
    float wheelInput;

    void Update () {
        horizontalInput = Input.GetAxisRaw("Horizontal")
        verticalInput = Input.GetAxisRaw("Vertical")
        wheelInput = Input.GetAxis("Mouse ScrollWheel")
    }

    void FixedUpdate()
    {
        if (Input.GetAxisRaw("Horizontal") != 0 || verticalInput != 0) {
            transform.position += moveSpeed * new Vector3(horizontalInput, 0, verticalInput);
        }

        if (Input.GetAxis("Mouse ScrollWheel") != 0) {
            transform.position += scrollSpeed * new Vector3(0, -Input.GetAxis("Mouse ScrollWheel"), 0);
        }
    }
}

I always wonder which is more robust solution.

Happy coding!

Collapse
 
shigsy profile image
shigsy • Edited

Probably best not to instantiate anything in your Update loop.

private float moveSpeed = 150.0f;
private Vector3 moveVector;

void Start()
{
    moveVector = new Vector3(0, 0, 0);
}

void Update()
{
    moveVector.x = Input.GetAxisRaw("Horizontal");
    moveVector.z = Input.GetAxisRaw("Vertical");

    if (Input.GetAxisRaw("Horizontal") != 0 || Input.GetAxisRaw("Vertical") != 0) {
        transform.position += moveSpeed * moveVector * Time.deltaTime;
    }

} 
Collapse
 
k1pash profile image
Christopher Toman

Good point!

Collapse
 
guteres007 profile image
Martin Andráši

Hello, please change

int horizontalInput;
int verticalInput;
int wheelInput;

all int to float;

Collapse
 
k1pash profile image
Christopher Toman

Ah, thank you :)

Collapse
 
matthewodle profile image
Matthew Odle

Thanks for the tip on FixedUpdate! I hadn't used that before.