Files
EnglewoodLAB/Assets/Scripts/Factory/Playback/UI/UIPlaybackController.cs

284 lines
9.4 KiB
C#

using Cysharp.Threading.Tasks;
using System;
using UVC.UI.Loading;
namespace UVC.Factory.Playback.UI
{
/// <summary>
/// UIPlaybackController는 UIPlayback(View)에서 발생하는 이벤트를 받아
/// 실제 재생, 일시정지, 데이터 준비 등 비즈니스 로직을 처리하는 컨트롤러입니다.
///
/// <b>예시: UIPlayback과의 연결</b>
/// <code>
/// // UIPlayback에서 컨트롤러를 생성할 때 View를 넘겨줍니다.
/// var controller = new UIPlaybackController(this);
/// </code>
/// </summary>
public class UIPlaybackController : IDisposable
{
/// <summary>
/// View 역할을 하는 UIPlayback 참조입니다.
/// </summary>
private readonly UIPlayback view;
// 재생 중 여부
private bool isPlaying = false;
// 데이터 준비 중 여부
private bool preparingData = false;
// 재생에 필요한 정보
private string date;
private string time;
private string fileName;
// 타이머 동작 여부
private bool isTick = false;
/// <summary>
/// 타이머 동작 여부를 외부에서 제어할 수 있습니다.
/// true로 설정하면 내부적으로 OnTimer()가 실행됩니다.
/// </summary>
/// <example>
/// <code>
/// // 타이머를 시작하려면
/// controller.IsTick = true;
/// // 타이머를 멈추려면
/// controller.IsTick = false;
/// </code>
/// </example>
public bool IsTick
{
get => isTick;
set
{
if (isTick != value)
{
isTick = value;
if (isTick) OnTimer().Forget();
}
}
}
/// <summary>
/// 생성자에서 View와 이벤트를 연결합니다.
/// </summary>
/// <param name="view">UIPlayback 인스턴스</param>
public UIPlaybackController(UIPlayback view)
{
this.view = view;
SubscribeToViewEvents();
}
/// <summary>
/// View에서 발생하는 이벤트를 컨트롤러의 메서드와 연결합니다.
/// </summary>
private void SubscribeToViewEvents()
{
view.OnClickExitButton += OnClickExit;
view.OnClickPlayButton += OnClickPlay;
view.OnChangeProgressValue += OnChangeProgress;
view.OnChangeSpeedValue += OnChangeSpeed;
view.OnChangeOpacityValue += OnValueChangedOpacity;
}
/// <summary>
/// 컨트롤러가 더 이상 필요 없을 때 이벤트 연결을 해제합니다.
/// </summary>
public void Dispose()
{
if (isPlaying) IsTick = false;
view.OnClickExitButton -= OnClickExit;
view.OnClickPlayButton -= OnClickPlay;
view.OnChangeProgressValue -= OnChangeProgress;
view.OnChangeSpeedValue -= OnChangeSpeed;
view.OnChangeOpacityValue -= OnValueChangedOpacity;
}
/// <summary>
/// 재생에 필요한 데이터를 설정하고 UI를 초기화합니다.
/// </summary>
/// <param name="date">날짜(예: "2024-07-29")</param>
/// <param name="time">시간(초 단위 문자열, 예: "3600")</param>
/// <param name="fileName">파일명</param>
/// <returns>비동기 작업(UniTask)</returns>
/// <example>
/// <code>
/// // 재생 데이터를 설정하는 예시
/// await controller.SetData("2024-07-29", "3600", "sample.sqlite");
/// </code>
/// </example>
public async UniTask SetData(string date, string time, string fileName)
{
this.date = date;
this.time = time;
this.fileName = fileName;
int timeInt = int.Parse(time);
view.UpdateDateTime(date.Substring(2).Replace("-", "."));
view.InitProgressBar(timeInt);
view.InitSpeedSlider();
view.SetOpacity(1);
UpdateTimeScale(1);
preparingData = true;
view.SetUIInteractable(!preparingData);
isPlaying = false;
UpdatePlayState();
// 실제 데이터 로딩 (비동기)
await PlaybackService.Instance.DispatchBaseInfoData(date, time, fileName);
preparingData = false;
view.SetUIInteractable(!preparingData);
}
/// <summary>
/// 종료 버튼 클릭 시 호출됩니다.
/// </summary>
/// <remarks>
/// UI를 숨기고, 재생 상태를 초기화하며, PlaybackService에 종료를 알립니다.
/// </remarks>
private void OnClickExit()
{
UILoading.Show();
isPlaying = false;
UpdatePlayState();
view.Hide();
PlaybackService.Instance.Exit();
}
/// <summary>
/// 재생/일시정지 버튼 클릭 시 호출됩니다.
/// </summary>
/// <remarks>
/// 데이터 준비 중에는 동작하지 않습니다.
/// </remarks>
private void OnClickPlay()
{
if (preparingData) return;
isPlaying = !isPlaying;
UpdatePlayState();
}
// <summary>
/// 재생 위치(프로그레스바) 변경 시 호출됩니다.
/// </summary>
/// <param name="newValue">변경된 위치(초)</param>
private void OnChangeProgress(int newValue)
{
ChangePlayTime().Forget();
}
/// <summary>
/// 재생 속도 변경 시 호출됩니다.
/// </summary>
/// <param name="newValue">변경된 속도 값</param>
private void OnChangeSpeed(int newValue)
{
UpdateTimeScale(isPlaying ? view.GetSpeedValue() : 1);
}
/// <summary>
/// 투명도 슬라이더 변경 시 호출됩니다.
/// </summary>
/// <param name="newValue">0~1 사이의 투명도 값</param>
private void OnValueChangedOpacity(float newValue)
{
view.SetOpacity(newValue);
}
/// <summary>
/// 재생/일시정지 상태에 따라 UI와 타이머를 갱신합니다.
/// </summary>
private void UpdatePlayState()
{
view.UpdatePlayButtonState(isPlaying);
IsTick = isPlaying;
OnChangeSpeed(view.GetSpeedValue());
}
/// <summary>
/// 재생 위치를 변경할 때 호출됩니다.
/// 데이터 준비, UI 비활성화, 로딩 표시 등 처리 후 재생 위치를 이동합니다.
/// </summary>
private async UniTaskVoid ChangePlayTime()
{
bool tempIsPlaying = isPlaying;
isPlaying = false;
UpdatePlayState();
int newSecond = view.GetProgressValue();
if (newSecond == view.GetProgressMaxValue())
{
newSecond -= 60;
view.SetProgressValue(newSecond);
}
preparingData = true;
view.SetUIInteractable(!preparingData);
IsTick = false;
UILoading.Show();
int minute = newSecond / 60;
int seconds = newSecond % 60;
// 새로운 위치로 데이터 요청
await PlaybackService.Instance.DispatchBaseInfoData(date, time, fileName, minute.ToString("00"), seconds.ToString("00"));
preparingData = false;
view.SetUIInteractable(!preparingData);
if (tempIsPlaying)
{
isPlaying = true;
UpdatePlayState();
}
UILoading.Hide();
}
/// <summary>
/// 재생 중일 때 일정 간격으로 호출되어 진행 바를 업데이트합니다.
/// </summary>
/// <remarks>
/// IsTick이 true일 때만 동작합니다.
/// </remarks>
/// <example>
/// <code>
/// // 타이머를 시작하려면
/// controller.IsTick = true;
/// </code>
/// </example>
private async UniTaskVoid OnTimer()
{
if (view.GetProgressValue() >= view.GetProgressMaxValue())
{
if (isPlaying) OnClickPlay();
return;
}
view.SetProgressValue(view.GetProgressValue() + 1);
// 실시간 데이터 요청
PlaybackService.Instance.DispatchRealTimeData(view.GetProgressValue(), view.GetSpeedValue()).Forget();
if (IsTick)
{
// 재생 속도에 따라 대기 시간 조절
await UniTask.Delay(TimeSpan.FromMilliseconds(1000 / view.GetSpeedValue()));
if (IsTick) OnTimer().Forget();
}
}
/// <summary>
/// 재생 속도를 PlaybackService에 반영합니다.
/// </summary>
/// <param name="timeScale">적용할 재생 속도</param>
/// <example>
/// <code>
/// // 재생 속도를 2배로 변경
/// controller.UpdateTimeScale(2f);
/// </code>
/// </example>
internal void UpdateTimeScale(float timeScale)
{
PlaybackService.Instance.TimeScale = timeScale;
}
}
}