How to build an elegant Isometric Camera control scheme

Hello everyone !

I have just started fiddling with Unity - and saw a lot of questions and methods around Isometric camera views, but none that addresses the fine points of what I’m trying to do.

I’m developping my game with an orthogonal isometric view using the basic approach: my camera object (“IsoCamera”) is rotated according to the required isometric values, and orthogonal. It is inside an empty object (“IsoCameraView”), and i’m manipulating that object to achieve the camera fonctionnality I desire.

Here are their respective setups in the inspector:
57863-isocamera-settings.png
57864-isocameraview-settings.png

As you can see, I’m using a single script called “CameraController”, attached to the IsoCameraView object.

Currently, the script works for panning up/down and left/right (screen-wise, not isometric view-wise) using the arrow keys, zooming in and out using the mouse wheel, and rotating the camera view along an axis that is roughly in the center of the screen.

My objective is to have the camera rotations “smoothed” when the corresponding keys (A and Q) are pressed, pretty much like it appears in this video:

But I can't figure out how to do it.

Here is my script (my last attempt is commented out under “//Smoothing”):

using UnityEngine;
using System.Collections;

// Camera Controller Isometric
// Allows the camera to move left, right along a fixed axis, zoom in and out, rotate, tilt
// Attach to a camera GameObject (e.g IsoCamera) for functionality.

public class CameraController : MonoBehaviour {
	
	//Include the CameraObject
	public Camera IsoCamera;
	// How fast the camera moves (panning)
	public int cameraVelocity = 10;
	// How fast the camera zooms (zooming)
	public float cameraZoomStep = 2f;
	// how much the camera rotates (rotating)
	float cameraRotationStep = 90f;
	
	// Use this for initialization
	void Start () {

	}
	
	// Update is called once per frame
	void Update () {
		KeyboardControl();
		MouseControl ();
	}
	// Smooth Rotating
	
	/*void RotateObject(Vector3 vector3Point, Vector3 vector3Axis, float rotateAmount, float rotateTime) {
		float step = 0.0f; //non-smoothed
		float rate = 1.0f/rotateTime; //amount to increase non-smooth step by
		float smoothStep = 0.0f; //smooth step this time
		float lastStep = 0.0f; //smooth step last time
		while(step < 1.0f) { // until we're done
			Debug.Log ("I've stepped!");
			step += Time.deltaTime * rate; //increase the step
			smoothStep = Mathf.SmoothStep(0.0f, 1.0f, step); //get the smooth step
			transform.RotateAround(vector3Point, vector3Axis, rotateAmount * (smoothStep - lastStep));
			lastStep = smoothStep; //store the smooth step
		}
		//finish any left-over
		if(step > 1.0f) {
			transform.RotateAround(vector3Point, vector3Axis, rotateAmount * (1.0 - lastStep));
			}
	}*/

	// Keyboard input controls
	void KeyboardControl(){
		// Left (screen-wise)
		if((Input.GetKey(KeyCode.LeftArrow)))
		{
			transform.Translate((Vector3.left * cameraVelocity) * Time.deltaTime);
			transform.Translate((Vector3.up * 0.4f * cameraVelocity) * Time.deltaTime);
		}
		// Right (screen-wise)
		if((Input.GetKey(KeyCode.RightArrow)))
		{
			transform.Translate((Vector3.right * cameraVelocity) * Time.deltaTime);
			transform.Translate((Vector3.down * 0.4f * cameraVelocity) * Time.deltaTime);
		}
		// Up
		if((Input.GetKey(KeyCode.UpArrow)))
		{
			transform.Translate((Vector3.up * cameraVelocity) * Time.deltaTime);
		}
		// Down
		if(Input.GetKey(KeyCode.DownArrow))
		{
			transform.Translate((Vector3.down * cameraVelocity) * Time.deltaTime);
		}
		// rotate left one step when key pressed
		if (Input.GetKeyDown(KeyCode.A)){
			transform.RotateAround(this.transform.position, Vector3.up, cameraRotationStep);
		}
		//rotate right one step when key pressed
		if (Input.GetKeyDown(KeyCode.E)){
			transform.RotateAround(this.transform.position, Vector3.down, cameraRotationStep);
		}
	}
	
	//Mouse input controls
	void MouseControl(){
		//Zooming
		//First, adjust Zoom Step depending on current Zoom level
		if (IsoCamera.orthographicSize < 8.5f){
			cameraZoomStep = 1f;
		} else if (IsoCamera.orthographicSize < 20f){
			cameraZoomStep = 5f;
		} else if (IsoCamera.orthographicSize < 50f){
			cameraZoomStep = 10f;
		} else {
			cameraZoomStep = 20f;
		}
		//Zoom when mouse wheel used
		if (Input.GetAxis ("Mouse ScrollWheel") < 0) {
			IsoCamera.orthographicSize = Mathf.Clamp(IsoCamera.orthographicSize+cameraZoomStep, 1f, 50f);
		}
		if (Input.GetAxis ("Mouse ScrollWheel") > 0) {
			IsoCamera.orthographicSize = Mathf.Clamp(IsoCamera.orthographicSize-cameraZoomStep, 1f, 50f);
		}
	}
}

Thanks a ton to anyone who can help me doing this!

So, I’ve come up with a solution to my issue, wich seemed to be input related.

I’m simply now always rotating in update(), but changing the rotation amount via input, meaning if you don’t input, the rotation is 0.

It’s probably not the most elegant or efficient way of doing this, so I’m really opened to hearing the approaches of more seasoned coders than I am (which must be a lot of people ;p).

In particular, the use of the mouse X and Y axis to translate when the left mouse button is pressed and maintained is behaving erratically, because I just tampered with multiplicators. I’m sure there is a far better way to achieve the perfect control I’m looking for, independantly of screen resolution etc.

For those who are looking for my solution, it’s below (full code):

using UnityEngine;
using System.Collections;

// Camera Controller Isometric
// Allows the camera to move left, right along a fixed axis, zoom in and out.
// Attach to a camera GameObject (e.g IsoCamera) for functionality.

public class CameraController : MonoBehaviour {
	
	//Include the CameraObject
	public Camera IsoCamera;
	// How fast the camera moves (panning)
	public int cameraVelocity = 10;
	// How fast the camera zooms (zooming)
	public float cameraZoomStep = 2f;
	// how much the camera rotates per rotation (rotating) - if this gets changed to another angle, the rest of the controls will not work properly
	float cameraRotationStep = 90f;
	// how much rotation do we need to do each frame
	float cameraRotationQueued = 0f;
	// declaring the rotation we'll be using
	Quaternion newRotation;
	public string cameraPointing = "N";
	public float mouseAxisCorrection = 10.5f;
	
	// Use this for initialization
	void Start () {
	}
	
	// Update is called once per frame
	void Update ()
	{
		KeyboardControl(); // call Keyboard controls
		MouseControl(); // call Mouse controls
		GamePadControl(); // call GamePad controls
		//Check every frame if there is rotation to be done
		transform.rotation = Quaternion.Lerp(transform.rotation, newRotation, 0.2f);
	}

	void RotateRight()
	{
		//Keeping track of camera orientation to translate correctly
		if (cameraPointing == "N"){
			cameraPointing = "E";
		} else if (cameraPointing == "E"){
			cameraPointing = "S";
		} else if (cameraPointing == "S"){
			cameraPointing = "W";
		} else if (cameraPointing == "W"){
			cameraPointing = "N";
		}
		cameraRotationQueued -= cameraRotationStep; //adding a left step to rotation needed to be done
		newRotation = Quaternion.AngleAxis(cameraRotationQueued, Vector3.up);
	}
	
	void RotateLeft()
	{
		//Keeping track of camera orientation to translate correctly
		if (cameraPointing == "N"){
			cameraPointing = "W";
		} else if (cameraPointing == "W"){
			cameraPointing = "S";
		} else if (cameraPointing == "S"){
			cameraPointing = "E";
		} else if (cameraPointing == "E"){
			cameraPointing = "N";
		}
		cameraRotationQueued += cameraRotationStep; //adding a right step to ritation needed to be done
		newRotation = Quaternion.AngleAxis(cameraRotationQueued, Vector3.up);
	}
	
	void TranslateLeft(){
		if(cameraPointing == "N" | cameraPointing == "W"){
			transform.Translate((Vector3.left * cameraVelocity) * Time.deltaTime, Space.World);
			transform.Translate((Vector3.up * 0.4f * cameraVelocity) * Time.deltaTime, Space.World);
		} else {
			transform.Translate((Vector3.left * cameraVelocity) * Time.deltaTime, Space.World);
			transform.Translate((Vector3.down * 0.4f * cameraVelocity) * Time.deltaTime, Space.World);
		}
	}
	
	void TranslateRight(){
		if(cameraPointing == "N" | cameraPointing == "W"){
			transform.Translate((Vector3.right * cameraVelocity) * Time.deltaTime, Space.World);
			transform.Translate((Vector3.down * 0.4f * cameraVelocity) * Time.deltaTime, Space.World);
		} else {
			transform.Translate((Vector3.right * cameraVelocity) * Time.deltaTime, Space.World);
			transform.Translate((Vector3.up * 0.4f * cameraVelocity) * Time.deltaTime, Space.World);
		}
	}
	
	void TranslateUp(){
		transform.Translate((Vector3.up * cameraVelocity) * Time.deltaTime, Space.World);
	}
	void TranslateDown(){
		transform.Translate((Vector3.down * cameraVelocity) * Time.deltaTime, Space.World);
	}
	
	// Keyboard input controls
	void KeyboardControl(){
		// Left (screen-wise)
		if((Input.GetKey(KeyCode.LeftArrow)))
		{
			TranslateLeft();
		}
		// Right (screen-wise)
		if((Input.GetKey(KeyCode.RightArrow)))
		{
			TranslateRight();
		}
		// Up
		if((Input.GetKey(KeyCode.UpArrow)))
		{
			TranslateUp();
		}
		// Down
		if(Input.GetKey(KeyCode.DownArrow))
		{
			TranslateDown();
		}
		// rotate left one step when key pressed
		if (Input.GetKeyDown(KeyCode.A))
		{
			RotateLeft ();
		}
		//rotate right one step when key pressed
		if (Input.GetKeyDown(KeyCode.E))
		{
			RotateRight ();
		}
	}
	
	//GamePad input controls
	void GamePadControl() {
		
	}
	
	//Mouse input controls
	void MouseControl(){
		//Zooming
		//First, adjust Zoom Step depending on current Zoom level
		if (IsoCamera.orthographicSize < 8.5f){
			cameraZoomStep = 1f;
		} else if (IsoCamera.orthographicSize < 20f){
			cameraZoomStep = 5f;
		} else if (IsoCamera.orthographicSize < 50f){
			cameraZoomStep = 10f;
		} else {
			cameraZoomStep = 20f;
		}
		//Zoom when mouse wheel used
		if (!Input.GetMouseButton(0)){ // Make sure the player isn't trying to rotate rather than zoom
			if (Input.GetAxis ("Mouse ScrollWheel") < 0) {
				IsoCamera.orthographicSize = Mathf.Clamp(IsoCamera.orthographicSize+cameraZoomStep, 1f, 50f);
			}
			if (Input.GetAxis ("Mouse ScrollWheel") > 0) {
				IsoCamera.orthographicSize = Mathf.Clamp(IsoCamera.orthographicSize-cameraZoomStep, 1f, 50f);
			}
		}
		
		//Panning and rotating with mouse via left button
		if (Input.GetMouseButton(0)){
			if (Input.GetAxis ("Mouse X")<0) {
				if(cameraPointing == "N"){
					transform.Translate((Vector3.right * cameraVelocity * Mathf.Abs (Input.GetAxis ("Mouse X"))*mouseAxisCorrection) * Time.deltaTime, Space.World);
					transform.Translate((Vector3.down * 0.4f * cameraVelocity * Mathf.Abs (Input.GetAxis ("Mouse X"))*mouseAxisCorrection) * Time.deltaTime, Space.World);
				} else if (cameraPointing == "E"){
					transform.Translate((Vector3.right * cameraVelocity * Mathf.Abs (Input.GetAxis ("Mouse X"))*mouseAxisCorrection) * Time.deltaTime, Space.World);
					transform.Translate((Vector3.up * 0.4f * cameraVelocity * Mathf.Abs (Input.GetAxis ("Mouse X"))*mouseAxisCorrection) * Time.deltaTime, Space.World);
				} else if (cameraPointing == "S") {
					transform.Translate((Vector3.left * cameraVelocity * Mathf.Abs (Input.GetAxis ("Mouse X"))*mouseAxisCorrection) * Time.deltaTime, Space.World);
					transform.Translate((Vector3.down * 0.4f * cameraVelocity * Mathf.Abs (Input.GetAxis ("Mouse X"))*mouseAxisCorrection) * Time.deltaTime, Space.World);
				} else {
					transform.Translate((Vector3.left * cameraVelocity * Mathf.Abs (Input.GetAxis ("Mouse X"))*mouseAxisCorrection) * Time.deltaTime, Space.World);
					transform.Translate((Vector3.up * 0.4f * cameraVelocity * Mathf.Abs (Input.GetAxis ("Mouse X"))*mouseAxisCorrection) * Time.deltaTime, Space.World);
				}
			}
			if (Input.GetAxis ("Mouse X")>0) {
				if(cameraPointing == "N"){
					transform.Translate((Vector3.left * cameraVelocity * Mathf.Abs (Input.GetAxis ("Mouse X"))*mouseAxisCorrection) * Time.deltaTime, Space.World);
					transform.Translate((Vector3.up * 0.4f * cameraVelocity * Mathf.Abs (Input.GetAxis ("Mouse X"))*mouseAxisCorrection) * Time.deltaTime, Space.World);
				} else if (cameraPointing == "E"){
					transform.Translate((Vector3.left * cameraVelocity * Mathf.Abs (Input.GetAxis ("Mouse X"))*mouseAxisCorrection) * Time.deltaTime, Space.World);
					transform.Translate((Vector3.down * 0.4f * cameraVelocity * Mathf.Abs (Input.GetAxis ("Mouse X"))*mouseAxisCorrection) * Time.deltaTime, Space.World);
				} else if (cameraPointing == "S"){
					transform.Translate((Vector3.right * cameraVelocity * Mathf.Abs (Input.GetAxis ("Mouse X"))*mouseAxisCorrection) * Time.deltaTime, Space.World);
					transform.Translate((Vector3.up * 0.4f * cameraVelocity * Mathf.Abs (Input.GetAxis ("Mouse X"))*mouseAxisCorrection) * Time.deltaTime, Space.World);
				} else if (cameraPointing == "W"){
					transform.Translate((Vector3.right * cameraVelocity * Mathf.Abs (Input.GetAxis ("Mouse X"))*mouseAxisCorrection) * Time.deltaTime, Space.World);
					transform.Translate((Vector3.down * 0.4f * cameraVelocity * Mathf.Abs (Input.GetAxis ("Mouse X"))*mouseAxisCorrection) * Time.deltaTime, Space.World);
				}
			}
			if (Input.GetAxis ("Mouse Y")<0){
				transform.Translate((Vector3.up * cameraVelocity * Mathf.Abs (Input.GetAxis ("Mouse Y"))*mouseAxisCorrection) * Time.deltaTime, Space.Self);
			}
			if (Input.GetAxis ("Mouse Y")>0){
				transform.Translate((Vector3.down * cameraVelocity * Mathf.Abs (Input.GetAxis ("Mouse Y"))*mouseAxisCorrection) * Time.deltaTime, Space.World);
			}
			if (Input.GetAxis ("Mouse ScrollWheel") < 0) {
				RotateLeft ();
			}
			if (Input.GetAxis ("Mouse ScrollWheel") > 0) {
				RotateRight ();
			}
		}
	}
}