playback 기능 추가

This commit is contained in:
logonkhi
2025-07-22 19:58:14 +09:00
parent cf97c6b61b
commit 4d29143d47
124 changed files with 15059 additions and 628 deletions

View File

@@ -0,0 +1,175 @@
using System;
using UnityEngine;
using UnityEngine.EventSystems;
namespace UVC.UI
{
/// <summary>
/// UI 요소를 마우스로 드래그하여 이동할 수 있게 만드는 컴포넌트입니다.
/// IBeginDragHandler, IDragHandler, IEndDragHandler 인터페이스를 구현하여 드래그 이벤트를 처리합니다.
/// 이 컴포넌트는 드래그될 UI 요소의 자식 오브젝트(예: 창의 헤더 영역)에 부착되어야 합니다.
/// </summary>
/// <example>
/// <b>사용 예제:</b>
/// 1. 드래그하고 싶은 창(Panel) UI를 만듭니다.
/// 2. 해당 창의 자식으로 드래그 핸들 역할을 할 UI(예: Image)를 만들고, 이 오브젝트에 `UIDragger` 컴포넌트를 추가합니다.
/// 3. 아래와 같은 컨트롤러 스크립트를 만들어 창에 추가하고, Inspector 창에서 `draggerHandle`과 `windowPanel`을 연결해줍니다.
/// <code>
/// using UnityEngine;
/// using UVC.UI;
///
/// public class DraggableWindowController : MonoBehaviour
/// {
/// // UIDragger 컴포넌트가 부착된 드래그 핸들 오브젝트
/// public UIDragger draggerHandle;
///
/// void Start()
/// {
/// // 드래그 시작 이벤트 구독
/// draggerHandle.onBeginDragHandler += () =>
/// {
/// Debug.Log("창 드래그가 시작되었습니다!");
/// };
///
/// // 드래그 종료 이벤트 구독
/// draggerHandle.OnEndDragHandler += pos =>
/// {
/// Debug.Log($"창 드래그가 종료되었습니다! 최종 위치: {pos}");
/// };
/// }
/// }
/// </code>
/// </example>
public class UIDragger : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler
{
[Header("드래그 설정")]
[SerializeField]
[Tooltip("드래그 가능한 영역을 지정합니다. 이 영역 내에서 드래그가 가능합니다.")]
private RectTransform dragArea; // 드래그가 가능한 영역
[SerializeField]
[Tooltip("드래그할 UI 요소를 지정합니다. 이 요소가 실제로 드래그됩니다.")]
private RectTransform dragObject; // 실제로 드래그될 UI 요소 (예: 창 전체)
[SerializeField]
[Tooltip("드래그 시작 시 해당 UI 요소를 맨 앞으로 가져올지 여부를 설정합니다.")]
private bool topOnDrag = true; // 드래그 시작 시 맨 앞으로 가져올지 여부
[SerializeField]
[Tooltip("드래그 영역 제한 시 사용할 최소 높이값")]
private float yMinHeight = 30;
private Vector2 originalLocalPointerPosition; // 드래그 시작 시 마우스 포인터의 로컬 위치
private Vector3 originalPanelLocalPosition; // 드래그 시작 시 패널의 로컬 위치
/// <summary>
/// 드래그가 끝났을 때 발생하는 이벤트입니다. 최종 위치 정보를 전달합니다.
/// </summary>
public Action<Vector3> OnEndDragHandler { get; set; }
/// <summary>
/// 드래그가 시작될 때 발생하는 액션입니다.
/// </summary>
public Action onBeginDragHandler;
// 원래의 형제 순서(UI 렌더링 순서)
private int baseSibling;
private void Awake()
{
// dragObject가 설정되지 않았다면, 부모를 드래그 대상으로 설정
if (dragObject == null)
{
dragObject = transform.parent as RectTransform;
if (dragObject == null)
{
Debug.LogError("<b>[UIDragger]</b> 드래그할 객체(dragObject)를 찾을 수 없습니다. 부모가 RectTransform이 아닙니다.", this);
enabled = false;
return;
}
}
// dragArea가 설정되지 않았다면, 최상위 Canvas를 드래그 영역으로 설정
if (dragArea == null)
{
dragArea = GetComponentInParent<Canvas>()?.transform as RectTransform;
if (dragArea == null)
{
Debug.LogError("<b>[UIDragger]</b> 드래그 영역(dragArea)으로 사용할 Canvas를 찾을 수 없습니다.", this);
enabled = false;
return;
}
}
}
/// <summary>
/// 드래그가 시작될 때 호출됩니다. (IBeginDragHandler)
/// </summary>
public void OnBeginDrag(PointerEventData data)
{
if (data.button != PointerEventData.InputButton.Left) return;
originalPanelLocalPosition = dragObject.localPosition;
baseSibling = dragObject.GetSiblingIndex();
RectTransformUtility.ScreenPointToLocalPointInRectangle(dragArea, data.position, data.pressEventCamera, out originalLocalPointerPosition);
if (topOnDrag)
{
dragObject.SetAsLastSibling();
}
onBeginDragHandler?.Invoke();
}
/// <summary>
/// 드래그 중일 때 매 프레임 호출됩니다. (IDragHandler)
/// </summary>
public void OnDrag(PointerEventData data)
{
if (data.button != PointerEventData.InputButton.Left) return;
if (RectTransformUtility.ScreenPointToLocalPointInRectangle(dragArea, data.position, data.pressEventCamera, out Vector2 localPointerPosition))
{
Vector3 offsetToOriginal = localPointerPosition - originalLocalPointerPosition;
dragObject.localPosition = originalPanelLocalPosition + offsetToOriginal;
}
ClampToArea();
}
/// <summary>
/// 드래그가 끝났을 때 호출됩니다. (IEndDragHandler)
/// </summary>
public void OnEndDrag(PointerEventData eventData)
{
if (eventData.button != PointerEventData.InputButton.Left) return;
if (topOnDrag)
{
dragObject.SetSiblingIndex(baseSibling);
}
OnEndDragHandler?.Invoke(dragObject.anchoredPosition);
}
/// <summary>
/// UI 요소가 드래그 영역 내에 있도록 위치를 제한합니다.
/// </summary>
private void ClampToArea()
{
Vector3 pos = dragObject.localPosition;
Rect dragObjectRect = dragObject.rect;
Rect dragAreaRect = dragArea.rect;
// Pivot을 기준으로 최소/최대 위치를 계산합니다.
float minX = dragAreaRect.xMin - dragObjectRect.xMin;
float maxX = dragAreaRect.xMax - dragObjectRect.xMax;
float minY = dragAreaRect.yMin - dragObjectRect.yMin;
float maxY = dragAreaRect.yMax - dragObjectRect.yMax;
pos.x = Mathf.Clamp(pos.x, minX, maxX);
pos.y = Mathf.Clamp(pos.y, minY, maxY - yMinHeight);
dragObject.localPosition = pos;
}
}
}