Files
XRLib/Assets/Scripts/Factory/Playback/UI/UIPlaybackProgressBar.cs
2026-01-21 20:43:54 +09:00

219 lines
7.5 KiB
C#

using System;
using TMPro;
using UnityEngine;
using UVC.Extension;
using UVC.UI;
namespace UVC.Factory.Playback.UI
{
/// <summary>
/// 재생 위치를 표시하고 조작할 수 있는 UI 프로그레스바 컴포넌트입니다.
/// - 현재 재생 시간, 전체 시간, 슬라이더(Seek Bar)로 구성됩니다.
/// - 슬라이더를 드래그하거나 클릭하여 재생 위치를 변경할 수 있습니다.
///
/// <para>샘플 사용법:</para>
/// <code>
/// // UIPlaybackProgressBar를 가진 오브젝트를 찾고 초기화
/// var progressBar = FindObjectOfType<UIPlaybackProgressBar>();
/// progressBar.Init(1); // 1시간짜리 재생바로 초기화
///
/// // 값이 변경될 때마다 호출되는 콜백 등록
/// progressBar.OnChangeValue = (value) => {
/// Debug.Log($"재생 위치가 {value}초로 변경됨");
/// };
///
/// // 재생 위치를 코드로 변경
/// progressBar.Value = 120; // 2분(120초) 위치로 이동
/// </code>
/// </summary>
public class UIPlaybackProgressBar : MonoBehaviour
{
[Header("UI Playback Progress Bar")]
[Tooltip("UI 활성/비활성 및 상호작용 제어용")]
[SerializeField]
private CanvasGroup canvasGroup;// UI 활성/비활성 및 상호작용 제어용
[Tooltip("현재 재생 시간 텍스트")]
[SerializeField]
private TextMeshProUGUI playTimeTxt;// 현재 재생 시간 텍스트
[Tooltip("전체 재생 시간 텍스트")]
[SerializeField]
private TextMeshProUGUI totalTimeTxt;// 전체 재생 시간 텍스트
[Tooltip("재생 위치를 조작하는 슬라이더")]
[SerializeField]
private SliderWithEvent progressBar;// 재생 위치를 조작하는 슬라이더
private float progressBarPrevValue = 0;// 이전 슬라이더 값(변경 감지용)
// 전체 시간(시간 단위, 예: 1이면 1시간)
private int time;
/// <summary>
/// 슬라이더 값이 변경될 때 호출되는 이벤트입니다.
/// <para>예시: progressBar.OnChangeValue = (value) => { ... };</para>
/// </summary>
public Action<int> OnChangeValue { get; set; }
/// <summary>
/// 현재 슬라이더(재생 위치) 값(초 단위)입니다.
/// 값을 설정하면 UI와 내부 상태가 함께 갱신됩니다.
/// </summary>
public int Value
{
get
{
return (int)progressBar.value;
}
set
{
if (progressBar.value != value)
{
progressBar.value = value;
UpdateTimeText();
progressBarPrevValue = value;
}
}
}
/// <summary>
/// 슬라이더의 최대값(전체 재생 시간, 초 단위)입니다.
/// </summary>
public int MaxValue
{
get => (int)progressBar.maxValue;
}
/// <summary>
/// 프로그레스바의 상호작용 가능 여부를 설정/조회합니다.
/// </summary>
public bool Interactable
{
get => canvasGroup.interactable;
set => canvasGroup.interactable = value;
}
/// <summary>
/// 오브젝트가 파괴될 때 이벤트 리스너를 해제합니다.
/// </summary>
private void OnDestroy()
{
progressBar.onValueChanged.RemoveListener(OnChangeSlider);
progressBar.OnClickAction = null;
progressBar.OnDragAction = null;
progressBar.OnEndDragAction = null;
}
/// <summary>
/// 프로그레스바를 초기화합니다.
/// </summary>
/// <param name="time">전체 시간(시간 단위, 예: 1이면 1시간)</param>
/// <example>
/// progressBar.Init(1); // 1시간짜리 재생바로 초기화
/// </example>
public void Init(int time)
{
this.time = time;
// 슬라이더 이벤트 등록
progressBar.onValueChanged.AddListener(OnChangeSlider);
progressBar.OnClickAction += OnClickProgressBar;
progressBar.OnDragAction += OnDragProgressBar;
progressBar.OnEndDragAction += OnEndDragProgressBar;
// 초기 텍스트 및 값 설정
playTimeTxt.text = $"{time.ToString("00")}:00:00";
totalTimeTxt.text = $"{(time + 1).ToString("00")}:00:00";
progressBar.value = 0;
Interactable = true;
}
/// <summary>
/// 슬라이더 값이 변경될 때 호출됩니다.
/// 상호작용 불가 상태면 값을 되돌립니다.
/// </summary>
/// <param name="newValue">변경된 값</param>
private void OnChangeSlider(float newValue)
{
if (!Interactable)
{
progressBar.value = progressBarPrevValue;
UpdateTimeText();
return;
}
}
/// <summary>
/// 슬라이더를 클릭했을 때 호출됩니다.
/// 값이 변경되면 OnChangeValue 이벤트가 발생합니다.
/// </summary>
private void OnClickProgressBar()
{
float snapedValue = SnapProgressBarValue();
progressBar.value = snapedValue;
UpdateTimeText();
if (progressBarPrevValue != snapedValue)
{
progressBarPrevValue = snapedValue;
if (OnChangeValue != null) OnChangeValue.Invoke((int)snapedValue);
}
}
/// <summary>
/// 슬라이더를 드래그하는 동안 계속 호출됩니다.
/// </summary>
private void OnDragProgressBar()
{
float snapedValue = SnapProgressBarValue();
progressBar.value = snapedValue;
UpdateTimeText();
}
/// <summary>
/// 슬라이더 드래그가 끝났을 때 호출됩니다.
/// 값이 변경되면 OnChangeValue 이벤트가 발생합니다.
/// </summary>
private void OnEndDragProgressBar()
{
float snapedValue = SnapProgressBarValue();
progressBar.value = snapedValue;
UpdateTimeText();
if (progressBarPrevValue != snapedValue)
{
progressBarPrevValue = snapedValue;
if (OnChangeValue != null) OnChangeValue.Invoke((int)snapedValue);
}
}
/// <summary>
/// 슬라이더 값을 60초(1분) 단위로 스냅합니다.
/// </summary>
/// <returns>스냅된 값</returns>
private float SnapProgressBarValue()
{
float value = progressBar.value;
float interval = 60f;
value = Mathf.Round(value / interval) * interval;
return value;
}
/// <summary>
/// 현재 재생 시간 텍스트를 갱신합니다.
/// </summary>
private void UpdateTimeText()
{
int minute = (int)progressBar.value / 60;
int second = (int)progressBar.value % 60;
string timeStr = time.ToString("00");
if (progressBar.value == 3600)
{
timeStr = (time + 1).ToString("00");
minute = 0;
}
playTimeTxt.text = $"{timeStr}:{minute.ToString("00")}:{second.ToString("00")}";
}
}
}