RandomGFGoStop/Assets/Scripts/My/TouchToCloseOrDragCamera.cs

159 lines
5.0 KiB
C#

using UnityEngine;
using UnityEngine.Events;
public class TouchToCloseOrDragCamera : MonoBehaviour
{
[Header("Targets")]
public Camera cam;
public Transform orbitTarget; // 기준점(없으면 월드원점)
[Header("Tap Settings")]
public float tapMaxTime = 0.25f; // 이 시간 이내면 탭
public float tapMaxMovePixels = 10f; // 이 픽셀 이내면 탭
[Header("Orbit Settings")]
public float orbitSpeedX = 0.2f; // 좌우 회전 감도
public float orbitSpeedY = 0.2f; // 상하 회전 감도
public float minPitch = -80f;
public float maxPitch = 80f;
public float distance = 6f; // 타깃까지 거리
[Header("On Tap Action")]
public UnityEvent OnSingleTap; // 탭 시 실행. 기본으로 현재 화면 비활성화하도록 세팅
// 내부 상태
int activePointerId = -1;
Vector2 downPos;
float downTime;
bool dragging;
float yaw, pitch;
void Reset()
{
cam = Camera.main;
}
void Awake()
{
if (cam == null) cam = Camera.main;
if (orbitTarget == null) { var t = new GameObject("OrbitTarget").transform; t.position = Vector3.zero; orbitTarget = t; }
// 기본 탭 동작: 이 스크립트가 달린 루트 오브젝트 비활성화(화면 종료 역할)
if (OnSingleTap == null) OnSingleTap = new UnityEvent();
if (OnSingleTap.GetPersistentEventCount() == 0)
OnSingleTap.AddListener(() => gameObject.SetActive(false));
// 초기 카메라 각도 계산
var toCam = (cam.transform.position - orbitTarget.position).normalized;
yaw = Mathf.Atan2(toCam.x, toCam.z) * Mathf.Rad2Deg;
pitch = Mathf.Asin(toCam.y) * Mathf.Rad2Deg;
}
void Update()
{
#if UNITY_EDITOR || UNITY_STANDALONE
HandleMouse();
#else
HandleTouch();
#endif
// 오빗 카메라 적용
ApplyOrbit();
}
void HandleMouse()
{
if (Input.GetMouseButtonDown(0))
{
activePointerId = 0;
downPos = Input.mousePosition;
downTime = Time.unscaledTime;
dragging = false;
}
else if (Input.GetMouseButton(0) && activePointerId == 0)
{
Vector2 cur = Input.mousePosition;
if (!dragging && Vector2.Distance(cur, downPos) > tapMaxMovePixels) dragging = true;
if (dragging)
{
Vector2 delta = (Vector2)Input.mousePosition - downPos;
OrbitByDelta(delta);
downPos = cur; // 연속 드래그
}
}
else if (Input.GetMouseButtonUp(0) && activePointerId == 0)
{
Vector2 upPos = Input.mousePosition;
float dt = Time.unscaledTime - downTime;
float dist = Vector2.Distance(upPos, downPos);
if (!dragging && dt <= tapMaxTime && dist <= tapMaxMovePixels)
{
// 단일 탭
OnSingleTap?.Invoke();
}
activePointerId = -1;
dragging = false;
}
}
void HandleTouch()
{
if (Input.touchCount == 0) return;
// 이미 추적 중인 손가락이 있으면 그걸 우선
int idx = -1;
if (activePointerId != -1)
{
for (int i = 0; i < Input.touchCount; i++)
if (Input.touches[i].fingerId == activePointerId) { idx = i; break; }
}
// 없으면 첫 터치로 시작
if (idx == -1) { var t0 = Input.touches[0]; idx = 0; activePointerId = t0.fingerId; }
var t = Input.touches[idx];
switch (t.phase)
{
case TouchPhase.Began:
downPos = t.position;
downTime = Time.unscaledTime;
dragging = false;
break;
case TouchPhase.Moved:
case TouchPhase.Stationary:
if (!dragging && Vector2.Distance(t.position, downPos) > tapMaxMovePixels) dragging = true;
if (dragging && t.phase == TouchPhase.Moved)
{
OrbitByDelta(t.deltaPosition);
}
break;
case TouchPhase.Ended:
case TouchPhase.Canceled:
float dt = Time.unscaledTime - downTime;
float dist = Vector2.Distance(t.position, downPos);
if (!dragging && dt <= tapMaxTime && dist <= tapMaxMovePixels)
{
OnSingleTap?.Invoke();
}
activePointerId = -1;
dragging = false;
break;
}
}
void OrbitByDelta(Vector2 delta)
{
yaw += delta.x * orbitSpeedX;
pitch -= delta.y * orbitSpeedY;
pitch = Mathf.Clamp(pitch, minPitch, maxPitch);
}
void ApplyOrbit()
{
Quaternion rot = Quaternion.Euler(pitch, yaw, 0f);
Vector3 pos = orbitTarget.position + rot * (Vector3.back * distance);
cam.transform.SetPositionAndRotation(pos, rot);
}
}