263 lines
8.1 KiB
C#
263 lines
8.1 KiB
C#
using System.IO;
|
|
using UnityEngine;
|
|
using static UnityEngine.Debug;
|
|
|
|
public class FlyCamera : MonoBehaviour
|
|
{
|
|
[Header("Movement Settings")]
|
|
public float moveSpeed = 10f;
|
|
public float fastMoveSpeed = 50f;
|
|
public float mouseSensitivity = 2f;
|
|
|
|
[Header("FOV Settings")]
|
|
public float minFOV = 10f;
|
|
public float maxFOV = 120f;
|
|
public float fovScrollSpeed = 10f;
|
|
|
|
[Header("Screenshot Settings")]
|
|
public KeyCode screenshotKey = KeyCode.F2;
|
|
|
|
[Header("Rough Mode")]
|
|
[Tooltip("러프(느슨) 이동 모드. 켜면 이동/회전 입력을 즉각 반영하지 않고 둔하게 지연시켜 느슨하게 따라갑니다.")]
|
|
public bool roughMode = false;
|
|
[Tooltip("러프 모드 위치 추적 속도. 낮을수록 더 둔하고 묵직하게 따라갑니다.")]
|
|
public float roughMoveSmoothing = 3f;
|
|
[Tooltip("러프 모드 회전 추적 속도. 낮을수록 더 둔하고 묵직하게 따라갑니다.")]
|
|
public float roughLookSmoothing = 5f;
|
|
|
|
private Camera _cam;
|
|
private float _rotationX;
|
|
private float _rotationY;
|
|
private Vector3 _targetPosition;
|
|
private bool _isFlyCamEnabled = true;
|
|
private float _screenshotFlashTime;
|
|
private bool _showScreenshotFlash;
|
|
|
|
private void Start()
|
|
{
|
|
_cam = GetComponent<Camera>();
|
|
if (_cam == null)
|
|
{
|
|
_cam = Camera.main;
|
|
}
|
|
|
|
SyncRoughState();
|
|
|
|
Cursor.lockState = CursorLockMode.Locked;
|
|
Cursor.visible = false;
|
|
|
|
CreateScreenshotDirectory();
|
|
}
|
|
|
|
private void Update()
|
|
{
|
|
if (!_isFlyCamEnabled) return;
|
|
|
|
HandleMouseLook();
|
|
HandleMovement();
|
|
HandleFOVControl();
|
|
HandleScreenshot();
|
|
HandleScreenshotFlash();
|
|
|
|
if (Input.GetKeyDown(KeyCode.Escape))
|
|
{
|
|
ToggleCursor();
|
|
}
|
|
|
|
if (Cursor.lockState != CursorLockMode.Locked && Input.GetMouseButtonDown(0))
|
|
{
|
|
Cursor.lockState = CursorLockMode.Locked;
|
|
Cursor.visible = false;
|
|
}
|
|
}
|
|
|
|
private void HandleMouseLook()
|
|
{
|
|
if (Cursor.lockState != CursorLockMode.Locked) return;
|
|
|
|
float mouseX = Input.GetAxis("Mouse X") * mouseSensitivity;
|
|
float mouseY = Input.GetAxis("Mouse Y") * mouseSensitivity;
|
|
|
|
_rotationX -= mouseY;
|
|
_rotationX = Mathf.Clamp(_rotationX, -90f, 90f);
|
|
_rotationY += mouseX;
|
|
|
|
Quaternion targetRotation = Quaternion.Euler(_rotationX, _rotationY, 0f);
|
|
|
|
// 러프 모드면 회전을 둔하게 지연시켜 따라가고, 아니면 즉각 반영
|
|
transform.rotation = roughMode
|
|
? Quaternion.Slerp(transform.rotation, targetRotation, roughLookSmoothing * Time.deltaTime)
|
|
: targetRotation;
|
|
}
|
|
|
|
private void HandleMovement()
|
|
{
|
|
Vector3 movement = Vector3.zero;
|
|
float currentSpeed = Input.GetKey(KeyCode.LeftShift) ? fastMoveSpeed : moveSpeed;
|
|
|
|
if (Input.GetKey(KeyCode.W))
|
|
movement += transform.forward;
|
|
if (Input.GetKey(KeyCode.S))
|
|
movement -= transform.forward;
|
|
if (Input.GetKey(KeyCode.A))
|
|
movement -= transform.right;
|
|
if (Input.GetKey(KeyCode.D))
|
|
movement += transform.right;
|
|
if (Input.GetKey(KeyCode.Q))
|
|
movement -= transform.up;
|
|
if (Input.GetKey(KeyCode.E))
|
|
movement += transform.up;
|
|
|
|
Vector3 delta = movement * (currentSpeed * Time.deltaTime);
|
|
|
|
if (roughMode)
|
|
{
|
|
// 입력은 목표 위치에 누적하고, 실제 위치는 둔하게 지연시켜 따라감
|
|
_targetPosition += delta;
|
|
transform.position = Vector3.Lerp(transform.position, _targetPosition, roughMoveSmoothing * Time.deltaTime);
|
|
}
|
|
else
|
|
{
|
|
transform.position += delta;
|
|
_targetPosition = transform.position;
|
|
}
|
|
}
|
|
|
|
private void HandleFOVControl()
|
|
{
|
|
float scroll = Input.GetAxis("Mouse ScrollWheel");
|
|
if (scroll != 0f)
|
|
{
|
|
_cam.fieldOfView = Mathf.Clamp(_cam.fieldOfView - scroll * fovScrollSpeed, minFOV, maxFOV);
|
|
}
|
|
}
|
|
|
|
private void HandleScreenshot()
|
|
{
|
|
if (Input.GetKeyDown(screenshotKey))
|
|
{
|
|
TakeScreenshot();
|
|
}
|
|
}
|
|
|
|
private void CreateScreenshotDirectory()
|
|
{
|
|
string screenshotDir = Path.Combine(Application.dataPath, "..", "Screenshots");
|
|
if (!Directory.Exists(screenshotDir))
|
|
{
|
|
Directory.CreateDirectory(screenshotDir);
|
|
}
|
|
}
|
|
|
|
private void TakeScreenshot()
|
|
{
|
|
StartCoroutine(CaptureScreenshotDelayed());
|
|
}
|
|
|
|
// ReSharper disable Unity.PerformanceAnalysis
|
|
private System.Collections.IEnumerator CaptureScreenshotDelayed()
|
|
{
|
|
yield return new WaitForEndOfFrame();
|
|
|
|
string timestamp = System.DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss");
|
|
string filename = $"Screenshot_{timestamp}.png";
|
|
string screenshotDir = Path.Combine(Application.dataPath, "..", "Screenshots");
|
|
string fullPath = Path.Combine(screenshotDir, filename);
|
|
|
|
ScreenCapture.CaptureScreenshot(fullPath);
|
|
Log($"Screenshot saved: {fullPath}");
|
|
|
|
_showScreenshotFlash = true;
|
|
_screenshotFlashTime = 0.2f;
|
|
}
|
|
|
|
private void HandleScreenshotFlash()
|
|
{
|
|
if (_showScreenshotFlash)
|
|
{
|
|
_screenshotFlashTime -= Time.deltaTime;
|
|
if (_screenshotFlashTime <= 0f)
|
|
{
|
|
_showScreenshotFlash = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
private void OnGUI()
|
|
{
|
|
if (_showScreenshotFlash)
|
|
{
|
|
GUI.color = new Color(1f, 1f, 1f, 0.8f);
|
|
GUI.DrawTexture(new Rect(0, 0, Screen.width, Screen.height), Texture2D.whiteTexture);
|
|
|
|
GUIStyle style = new GUIStyle();
|
|
style.fontSize = 36;
|
|
style.normal.textColor = Color.black;
|
|
style.alignment = TextAnchor.MiddleCenter;
|
|
|
|
GUI.Label(new Rect(0, Screen.height / 2 - 50, Screen.width, 100), "Screenshot Captured!", style);
|
|
}
|
|
}
|
|
|
|
private void ToggleCursor()
|
|
{
|
|
if (Cursor.lockState == CursorLockMode.Locked)
|
|
{
|
|
Cursor.lockState = CursorLockMode.None;
|
|
Cursor.visible = true;
|
|
}
|
|
else
|
|
{
|
|
Cursor.lockState = CursorLockMode.Locked;
|
|
Cursor.visible = false;
|
|
}
|
|
}
|
|
|
|
public void EnableFlyCamera(bool enable)
|
|
{
|
|
_isFlyCamEnabled = enable;
|
|
if (enable)
|
|
{
|
|
// 비활성화 동안 외부에서 카메라가 이동했을 수 있으므로 러프 모드 점프 방지를 위해 동기화
|
|
SyncRoughState();
|
|
Cursor.lockState = CursorLockMode.Locked;
|
|
Cursor.visible = false;
|
|
}
|
|
else
|
|
{
|
|
Cursor.lockState = CursorLockMode.None;
|
|
Cursor.visible = true;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 러프 모드의 목표 위치/회전 누적값을 현재 트랜스폼에 맞춰 동기화합니다. (점프 방지)
|
|
/// </summary>
|
|
private void SyncRoughState()
|
|
{
|
|
// eulerAngles.x는 0~360 범위라 위를 볼 때(예: 350°) 클램프에 걸려 튀므로 -180~180으로 정규화
|
|
float pitch = transform.eulerAngles.x;
|
|
if (pitch > 180f) pitch -= 360f;
|
|
_rotationX = Mathf.Clamp(pitch, -90f, 90f);
|
|
_rotationY = transform.eulerAngles.y;
|
|
_targetPosition = transform.position;
|
|
}
|
|
|
|
/// <summary>러프(느슨) 이동 모드 활성 여부.</summary>
|
|
public bool IsRoughMode => roughMode;
|
|
|
|
/// <summary>러프 이동 모드를 토글합니다.</summary>
|
|
public void ToggleRoughMode()
|
|
{
|
|
SetRoughMode(!roughMode);
|
|
}
|
|
|
|
/// <summary>러프 이동 모드를 명시적으로 켜거나 끕니다.</summary>
|
|
public void SetRoughMode(bool enable)
|
|
{
|
|
if (roughMode == enable) return;
|
|
// 토글 시점에 목표값을 현재 위치로 맞춰 전환 직후 튐 방지
|
|
SyncRoughState();
|
|
roughMode = enable;
|
|
}
|
|
} |