Skip to content

Instantly share code, notes, and snippets.

@danieldownes
Created January 10, 2023 09:49
Show Gist options
  • Select an option

  • Save danieldownes/3e3c0fe221834e919a79621d14f48b7c to your computer and use it in GitHub Desktop.

Select an option

Save danieldownes/3e3c0fe221834e919a79621d14f48b7c to your computer and use it in GitHub Desktop.

Revisions

  1. danieldownes created this gist Jan 10, 2023.
    178 changes: 178 additions & 0 deletions SceneLikeCamera
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,178 @@
    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;
    }
    }