Files
aibis-dream/Assets/Scripts/FixSystem/Memory/MemorySlider.cs
T

149 lines
4.5 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Rendering;
using UnityEngine.Playables;
using UnityEngine.Timeline;
namespace AibisDream.FixSystem
{
[RequireComponent(typeof(Slider))]
public class MemorySlider : MonoBehaviour
{
[SerializeField] private float defaultValue = 0f;
[SerializeField] private float sliderMaxValue = 6f;
[SerializeField] private float lockThreshold = 0.06f;
[SerializeField] private TimelineAsset openAsset;
[SerializeField] private TimelineAsset closeAsset;
private Slider slider;
private PlayableDirector playableDirector;
private Volume _volume;
/// <summary>视觉 Lock/UnlockTimeline);true = 收起未开,false = 已打开。</summary>
private bool isLocked;
public float TargetValue { get; set; }
public bool IsFound { get; private set; }
public event Action OnFound;
public event Action OnLost;
public void Init(Volume volume)
{
slider = GetComponent<Slider>();
playableDirector = GetComponent<PlayableDirector>();
_volume = volume;
IsFound = false;
slider.onValueChanged.RemoveAllListeners();
slider.onValueChanged.AddListener(HandleValueChanged);
slider.maxValue = sliderMaxValue;
slider.value = defaultValue;
UpdateVolumeWeight(slider.value);
isLocked = true;
SetInteractable(false);
}
/// <summary>仅控制是否可拖动,与 <see cref="UnlockSlider"/> / <see cref="LockSlider"/> 无关。</summary>
public void SetInteractable(bool interactable) => slider.interactable = interactable;
/// <summary>
/// 新一轮搜索前复位滑条与 IsFound(不播 Timeline、不改变视觉锁状态)。
/// 需先设置好 <see cref="TargetValue"/>。
/// </summary>
public void PrepareForSearch()
{
slider.value = defaultValue;
HandleValueChanged(slider.value);
}
/// <summary>仅播放打开 Timeline(若有),不碰 interactable。</summary>
public void UnlockSlider(Action onComplete = null)
{
if (!isLocked)
{
onComplete?.Invoke();
return;
}
PlayTimeline(openAsset, () =>
{
isLocked = false;
onComplete?.Invoke();
});
}
/// <summary>仅播放收起 Timeline(若有),不碰 interactable。</summary>
public void LockSlider(Action onComplete = null)
{
if (isLocked)
{
onComplete?.Invoke();
return;
}
slider.value = 0f;
UpdateVolumeWeight(0f);
PlayTimeline(closeAsset, () =>
{
isLocked = true;
onComplete?.Invoke();
});
}
private void HandleValueChanged(float value)
{
float t = UpdateVolumeWeight(value);
bool wasFound = IsFound;
IsFound = t < lockThreshold;
if (IsFound && !wasFound)
OnFound?.Invoke();
else if (!IsFound && wasFound)
OnLost?.Invoke();
}
private float UpdateVolumeWeight(float value)
{
float distance = Mathf.Abs(value - TargetValue);
float maxDistance = Mathf.Max(
Mathf.Abs(slider.maxValue - TargetValue),
Mathf.Abs(slider.minValue - TargetValue)
);
float t = Mathf.Clamp01(distance / maxDistance);
if (_volume != null)
_volume.weight = Mathf.Lerp(0, 1, t);
return t;
}
private void PlayTimeline(TimelineAsset asset, Action onComplete)
{
if (asset == null || playableDirector == null)
{
onComplete?.Invoke();
return;
}
playableDirector.Stop();
playableDirector.playableAsset = asset;
playableDirector.time = 0;
playableDirector.extrapolationMode = DirectorWrapMode.None;
Action<PlayableDirector> callback = null;
callback = pd =>
{
playableDirector.stopped -= callback;
onComplete?.Invoke();
};
playableDirector.stopped += callback;
playableDirector.Play();
}
}
}