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:
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:
https://www.youtube.com/watch?v=6Yc6IT34EjM
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!
From what I can gather, it has to deal with the input, actually.
When I add in the update() fonction the following code:
Quaternion newRotation = Quaternion.AngleAxis(cameraRotationStep, Vector3.up);
transform.rotation = Quaternion.Slerp(transform.rotation, newRotation, 0.05f);
It operates normally. When I use this same code inside an if{} statement listening to Input.$$anonymous$$eyDown, it moves a little then stops; I assume that's because the if conditionnal just stays true for a very brief moment.
Answer by RaHaN_GS · Nov 09, 2015 at 09:06 PM
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 ();
}
}
}
}