컴사양은 좋음






using UnityEngine;
using UnityEngine.InputSystem;

[RequireComponent(typeof(Camera))]
public class CameraMove : MonoBehaviour
{
    [Header("Frame Rate")]
    [SerializeField, Min(1)] private int targetFrameRate = 120;
    [SerializeField] private bool syncFixedTimeStep = true;

    [Header("Movement")]
    [SerializeField] private float moveSpeed = 10f;

    [Header("View Toggle")]
    [SerializeField] private Vector3 frontViewEulerAngles = Vector3.zero;
    [SerializeField] private float rotationSmoothSpeed = 12f;
    [SerializeField] private float doubleClickInterval = 0.3f;
    [SerializeField] private float groundPlaneZ = 0f;
    [SerializeField] private float viewTransitionDuration = 0.35f;

    [Header("Zoom")]
    [SerializeField] private float zoomSpeed = 3f;
    [SerializeField] private float minFieldOfView = 20f;
    [SerializeField] private float maxFieldOfView = 80f;
    [SerializeField] private float minOrthographicSize = 2f;
    [SerializeField] private float maxOrthographicSize = 20f;

    private Camera controlledCamera;
    private float pendingScroll;
    private int inputSystemScrollFrame = -1;
    private Quaternion tiltedRotation;
    private Quaternion targetRotation;
    private float lastLeftClickTime = float.NegativeInfinity;
    private bool isFrontView;
    private bool isViewTransitioning;
    private Vector3 viewPivot;
    private float viewPivotDistance;
    private Quaternion transitionStartRotation;
    private Quaternion transitionEndRotation;
    private float viewTransitionProgress;

    private void Awake()
    {
        controlledCamera = GetComponent<Camera>();
        tiltedRotation = transform.rotation;
        targetRotation = tiltedRotation;
    }

    private void Start()
    {
        Application.targetFrameRate = targetFrameRate;

        if (syncFixedTimeStep)
            Time.fixedDeltaTime = 1f / targetFrameRate;
    }

    private void Update()
    {
        Move();
        Zoom();
        HandleViewToggle();
    }

    private void FixedUpdate()
    {
        if (isViewTransitioning)
        {
            UpdateViewTransition();
            return;
        }

        float rotationStep = 1f - Mathf.Exp(-rotationSmoothSpeed * Time.fixedDeltaTime);
        transform.rotation = Quaternion.Slerp(
            transform.rotation,
            targetRotation,
            rotationStep
        );
    }

    private void Move()
    {
        if (isViewTransitioning)
            return;

        if (Keyboard.current == null)
            return;

        float horizontal = 0f;
        float vertical = 0f;

        if (Keyboard.current.aKey.isPressed)
            horizontal -= 1f;

        if (Keyboard.current.dKey.isPressed)
            horizontal += 1f;

        if (Keyboard.current.sKey.isPressed)
            vertical -= 1f;

        if (Keyboard.current.wKey.isPressed)
            vertical += 1f;

        Vector3 direction =
            (Vector3.right * horizontal + Vector3.up * vertical).normalized;

        transform.position += direction * moveSpeed * Time.deltaTime;
    }

    private void HandleViewToggle()
    {
        if (Mouse.current == null || !Mouse.current.leftButton.wasPressedThisFrame)
            return;

        float clickTime = Time.unscaledTime;

        if (clickTime - lastLeftClickTime <= doubleClickInterval)
        {
            isFrontView = !isFrontView;
            Quaternion nextRotation = isFrontView
                ? Quaternion.Euler(frontViewEulerAngles)
                : tiltedRotation;

            StartViewTransition(nextRotation);

            lastLeftClickTime = float.NegativeInfinity;
            return;
        }

        lastLeftClickTime = clickTime;
    }

    private void StartViewTransition(Quaternion nextRotation)
    {
        Ray centerRay = controlledCamera.ViewportPointToRay(
            new Vector3(0.5f, 0.5f, 0f)
        );

        Plane groundPlane = new Plane(Vector3.forward, new Vector3(0f, 0f, groundPlaneZ));

        if (!groundPlane.Raycast(centerRay, out float hitDistance))
            return;

        viewPivot = centerRay.GetPoint(hitDistance);
        viewPivotDistance = Vector3.Distance(transform.position, viewPivot);
        transitionStartRotation = transform.rotation;
        transitionEndRotation = nextRotation;
        targetRotation = nextRotation;
        viewTransitionProgress = 0f;
        isViewTransitioning = true;
    }

    private void UpdateViewTransition()
    {
        float duration = Mathf.Max(0.01f, viewTransitionDuration);
        viewTransitionProgress += Time.fixedDeltaTime / duration;

        float t = Mathf.SmoothStep(0f, 1f, viewTransitionProgress);
        Quaternion newRotation = Quaternion.Slerp(
            transitionStartRotation,
            transitionEndRotation,
            t
        );

        transform.SetPositionAndRotation(
            viewPivot - newRotation * Vector3.forward * viewPivotDistance,
            newRotation
        );

        if (viewTransitionProgress < 1f)
            return;

        isViewTransitioning = false;
    }

    private void Zoom()
    {
        float scrollY = pendingScroll;
        pendingScroll = 0f;

        if (Mouse.current != null)
        {
            float inputSystemScroll = Mouse.current.scroll.y.ReadValue();

            if (Mathf.Abs(inputSystemScroll) >= 0.01f)
            {
                scrollY = inputSystemScroll;
                inputSystemScrollFrame = Time.frameCount;
            }
        }

        if (Mathf.Abs(scrollY) < 0.01f)
            return;

        float zoomAmount = Mathf.Sign(scrollY) * zoomSpeed;

        ApplyZoom(zoomAmount);
    }

    private void OnGUI()
    {
        if (Event.current.type != EventType.ScrollWheel)
            return;

        // OnGUI is a fallback for devices whose wheel is not reported by Mouse.current.
        if (inputSystemScrollFrame != Time.frameCount)
            pendingScroll = -Event.current.delta.y;
    }

    private void ApplyZoom(float zoomAmount)
    {

        if (controlledCamera.orthographic)
        {
            controlledCamera.orthographicSize = Mathf.Clamp(
                controlledCamera.orthographicSize - zoomAmount,
                minOrthographicSize,
                maxOrthographicSize
            );
            return;
        }

        controlledCamera.fieldOfView = Mathf.Clamp(
            controlledCamera.fieldOfView - zoomAmount,
            minFieldOfView,
            maxFieldOfView
        );
    }
}