Skip to content

Instantly share code, notes, and snippets.

@danieldownes
Created January 10, 2023 09:49
Show Gist options
  • Save danieldownes/3e3c0fe221834e919a79621d14f48b7c to your computer and use it in GitHub Desktop.
Save danieldownes/3e3c0fe221834e919a79621d14f48b7c to your computer and use it in GitHub Desktop.
Camera controls like Unity Editor
using UnityEngine;
/// <summary>
/// Camera controls like Unity Editor.
/// </summary>
public class SceneLikeCamera : MonoBehaviour
{
[Header("Focus Object")]
[SerializeField, Tooltip("Enable double-click to focus on objects?")]
private bool doFocus = false;
[SerializeField] private float focusLimit = 100f;
[SerializeField] private float minFocusDistance = 5.0f;
private float doubleClickTime = .15f;
private float cooldown = 0;
[Header("Undo - Only undoes the Focus Object - The keys must be pressed in order.")]
[SerializeField] private KeyCode firstUndoKey = KeyCode.LeftControl;
[SerializeField] private KeyCode secondUndoKey = KeyCode.Z;
[Header("Movement")]
[SerializeField] private float moveSpeed = 1.0f;
[SerializeField] private float panSpeed = 3.0f;
[SerializeField] private float rotationSpeed = 10.0f;
[SerializeField] private float zoomSpeed = 10.0f;
//Cache last pos and rot be able to undo last focus object action.
Quaternion prevRot = new Quaternion();
Vector3 prevPos = new Vector3();
[Header("Axes Names")]
[SerializeField, Tooltip("Otherwise known as the vertical axis")] private string mouseY = "Mouse Y";
[SerializeField, Tooltip("AKA horizontal axis")] private string mouseX = "Mouse X";
[SerializeField, Tooltip("The axis you want to use for zoom.")] private string zoomAxis = "Mouse ScrollWheel";
[Header("Move Keys")]
[SerializeField] private KeyCode forwardKey = KeyCode.W;
[SerializeField] private KeyCode backKey = KeyCode.S;
[SerializeField] private KeyCode leftKey = KeyCode.A;
[SerializeField] private KeyCode rightKey = KeyCode.D;
[SerializeField] private KeyCode upKey = KeyCode.E;
[SerializeField] private KeyCode downKey = KeyCode.Q;
[Header("Flat Move"), Tooltip("Instead of going where the camera is pointed, the camera moves only on the horizontal plane (Assuming you are working in 3D with default preferences).")]
[SerializeField] private KeyCode flatMoveKey = KeyCode.LeftShift;
[Header("Anchored Movement"), Tooltip("By default in scene-view, this is done by right-clicking for rotation or middle mouse clicking for up and down")]
[SerializeField] private KeyCode anchoredMoveKey = KeyCode.Mouse2;
[SerializeField] private KeyCode anchoredRotateKey = KeyCode.Mouse1;
private void Start()
{
SavePosAndRot();
}
void Update()
{
if (!doFocus)
return;
//Double click for focus
if (cooldown > 0 && Input.GetKeyDown(KeyCode.Mouse0))
FocusObject();
if (Input.GetKeyDown(KeyCode.Mouse0))
cooldown = doubleClickTime;
//--------UNDO FOCUS---------
if (Input.GetKey(firstUndoKey))
{
if (Input.GetKeyDown(secondUndoKey))
GoBackToLastPosition();
}
cooldown -= Time.deltaTime;
}
private void LateUpdate()
{
Vector3 move = Vector3.zero;
//Move and rotate the camera
if (Input.GetKey(anchoredRotateKey))
{
if (Input.GetKey(forwardKey))
move += Vector3.forward * moveSpeed;
if (Input.GetKey(backKey))
move += Vector3.back * moveSpeed;
if (Input.GetKey(leftKey))
move += Vector3.left * moveSpeed;
if (Input.GetKey(rightKey))
move += Vector3.right * moveSpeed;
if (Input.GetKey(upKey))
move += Vector3.up * moveSpeed;
if (Input.GetKey(downKey))
move += Vector3.down * moveSpeed;
}
//By far the simplest solution I could come up with for moving only on the Horizontal plane - no rotation, just cache y
if (Input.GetKey(flatMoveKey))
{
float origY = transform.position.y;
transform.Translate(move);
transform.position = new Vector3(transform.position.x, origY, transform.position.z);
return;
}
float mouseMoveY = Input.GetAxis(mouseY);
float mouseMoveX = Input.GetAxis(mouseX);
//Move the camera when anchored
if (Input.GetKey(anchoredMoveKey))
{
move += Vector3.up * mouseMoveY * -panSpeed;
move += Vector3.right * mouseMoveX * -panSpeed;
}
//Rotate the camera when anchored
if (Input.GetKey(anchoredRotateKey))
{
transform.RotateAround(transform.position, transform.right, mouseMoveY * -rotationSpeed);
transform.RotateAround(transform.position, Vector3.up, mouseMoveX * rotationSpeed);
}
transform.Translate(move);
//Scroll to zoom
float mouseScroll = Input.GetAxis(zoomAxis);
transform.Translate(Vector3.forward * mouseScroll * zoomSpeed);
}
public void FocusOnObject(Collider target)
{
// Save for undo
SavePosAndRot();
// Debug.Log("collider" + target.gameObject.name);
GameObject go = target.gameObject;
Vector3 targetPos = go.transform.position;
Vector3 targetSize = target.bounds.size;
//transform.position = targetPos + GetOffset(targetPos, targetSize);
transform.LookAt(go.transform);
}
private void FocusObject()
{
//If we double-clicked an object in the scene, go to its position
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
if (Physics.Raycast(ray, out hit, focusLimit))
FocusOnObject(hit.collider);
}
private void SavePosAndRot()
{
prevRot = transform.rotation;
prevPos = transform.position;
}
private void GoBackToLastPosition()
{
transform.position = prevPos;
transform.rotation = prevRot;
}
private Vector3 GetOffset(Vector3 targetPos, Vector3 targetSize)
{
Vector3 dirToTarget = targetPos - transform.position;
float focusDistance = Mathf.Max(targetSize.x, targetSize.z);
focusDistance = Mathf.Clamp(focusDistance, minFocusDistance, focusDistance);
return -dirToTarget.normalized * focusDistance;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment