diff --git a/Assets/Scripts/FixSystem/Memory/MemoryProcess.cs b/Assets/Scripts/FixSystem/Memory/MemoryProcess.cs index 8df8c0472..d39b93f70 100644 --- a/Assets/Scripts/FixSystem/Memory/MemoryProcess.cs +++ b/Assets/Scripts/FixSystem/Memory/MemoryProcess.cs @@ -1,15 +1,11 @@ using System.Collections; using UnityEngine; -using UnityEngine.UI; using DG.Tweening; using Yarn.Unity; -using AibisDream; -using AibisDream.FixSystem; using AibisDream.Framework; using Cinemachine; using UnityEngine.Rendering; using AibisDream.Kit; -using UnityEngine.EventSystems; using UnityEngine.ResourceManagement.AsyncOperations; namespace AibisDream.FixSystem @@ -17,46 +13,38 @@ namespace AibisDream.FixSystem public class MemoryProcess : MonoBehaviour { private const string MemorySpritePath = "Sprite/Memory/{0}.png"; + private const float FadeMaterialStartValue = -15f; + private const float DefaultFadeDuration = 1f; + private const float SearchStartDelay = 1f; + private const float ProjectorMoveDuration = 2f; - public BubbleSlotGroup bubbleSlotGroup; - public GameObject memoryView; - public GameObject projector; - public SpriteRenderer memoryFinishedspRender; // 用于显示记忆处理结束的表现 - public SpriteRenderer memoryFinishedImageNoColor; // 用于显示记忆处理结束的表现 + [SerializeField] private BubbleSlotGroup bubbleSlotGroup; + public BubbleSlotGroup BubbleSlotGroup => bubbleSlotGroup; + [SerializeField] private GameObject memoryView; + [SerializeField] private GameObject projector; + [SerializeField] private SpriteRenderer memoryFinishedSpriteRenderer; // 用于显示记忆处理结束的表现 + [SerializeField] private SpriteRenderer memoryFinishedImageNoColor; // 用于显示记忆处理结束的表现 - [SerializeField] private Volume MainEffect; - [SerializeField] private Volume MemoryClarityVloume; - [SerializeField] private Volume Memoryfrequencyloume; - [SerializeField] private Volume MemoryProcessVloume; - public Slider frequencySlider; // 频率调节滑动条 + [SerializeField] private Volume mainEffect; + [SerializeField] private Volume memoryClarityVolume; + [SerializeField] private Volume memoryFrequencyVolume; + [SerializeField] private Volume memoryProcessVolume; + [SerializeField] private MemorySlider frequencySlider; // 频率调节滑动条 + [SerializeField] private MemorySlider claritySlider; // 清晰度调节滑动条 - public Slider claritySlider; // 频率调节滑动条 + [SerializeField] private CanvasGroup playBackground; - public CanvasGroup playBackground; - public float targetFrequency = 1.25f; // 目标频率 - - public float targetClarity = 5.7f; // 目标频率 - - private GameObject memoryGroup; - private float lockThreshold = 0.06f; // 锁定阈值 + [SerializeField] private GameObject memoryGroup; + [SerializeField] private CinemachineVirtualCamera memoryCamera; private bool isProcessing = false; // 新增变量来追踪是否正在处理记忆 - private FMOD.Studio.EventInstance faderSoundInstance; // 添加音效实例变量 private bool isPlayingFaderSound = false; // 追踪音效是否正在播放 - private float lastFrequencyValue; // 记录上一帧的频率值 - private float lastClarityValue; // 记录上一帧的清晰度值 - - private bool isColorful = false; // - private bool isFrequencyFound = false; // 追踪是否找到目标频率 - private bool isClarityFound = false; // 追踪是否找到目标清晰度 - - private Material memoryMaterial; // 噪波材质 private PunchTape _activePunchTape; [SerializeField] private MemoryPunchTapeSlot punchTapeSlot; - public Material fadeMaterial; + [SerializeField] private Material fadeMaterial; public void OpenMemoryView() { @@ -68,15 +56,14 @@ namespace AibisDream.FixSystem public IEnumerator DropMemoryProjector() { DG.Tweening.Sequence zoomInSequence = DOTween.Sequence(); - zoomInSequence.Append(projector.transform.DOLocalMoveY(-5, 2f).SetEase(Ease.OutElastic, 0.5f)); + zoomInSequence.Append(projector.transform.DOLocalMoveY(-5, ProjectorMoveDuration).SetEase(Ease.OutElastic, 0.5f)); yield return zoomInSequence.WaitForCompletion(); } public void PullMemoryProjector() { DG.Tweening.Sequence zoomOutSequence = DOTween.Sequence(); - zoomOutSequence.Append(projector.transform.DOLocalMoveY(0, 2f).SetEase(Ease.OutElastic, 0.5f)); - //yield return zoomOutSequence.WaitForCompletion(); + zoomOutSequence.Append(projector.transform.DOLocalMoveY(0, ProjectorMoveDuration).SetEase(Ease.OutElastic, 0.5f)); } public void CloseMemoryView() @@ -88,15 +75,17 @@ namespace AibisDream.FixSystem public void OnEnter() { OpenMemoryView(); - EnsurePunchTapeSlotReference(); - PunchTapeManager.Instance.OpenFavoritesPanel(); - playBackground.gameObject.SetActive(false); - playBackground.alpha = 0; + PrepareMemoryView(); } public void OnShow() { Debug.Log("memoryView showed."); + PrepareMemoryView(); + } + + private void PrepareMemoryView() + { EnsurePunchTapeSlotReference(); PunchTapeManager.Instance.OpenFavoritesPanel(); playBackground.gameObject.SetActive(false); @@ -116,6 +105,9 @@ namespace AibisDream.FixSystem CloseMemoryView(); // 离开视图时的逻辑 Debug.Log("MemoryView exited."); + + if (frequencySlider != null) frequencySlider.LockSlider(); + if (claritySlider != null) claritySlider.LockSlider(); } private void Awake() @@ -138,19 +130,27 @@ namespace AibisDream.FixSystem private void InitSystem() { FixSystemCenter.SystemDic.Register(this); - memoryGroup = transform.Find("Memory Group").gameObject; - memoryGroup.SetActive(false); - var virtualCam = transform.Find("Memory Camera").GetComponent(); - CameraKit.Instance.RegisterCamera(CameraEnum.Memory, virtualCam); + if (memoryGroup != null) + memoryGroup.SetActive(false); + if (memoryCamera != null) + CameraKit.Instance.RegisterCamera(CameraEnum.Memory, memoryCamera); } private void Start() { + frequencySlider.OnFound += CheckAllFound; + claritySlider.OnFound += CheckAllFound; Init(); } - float GetRandomNumber() + private void CheckAllFound() + { + if (frequencySlider.IsFound && claritySlider.IsFound) + OnSearchComplete(); + } + + private float GetRandomNumber() { // 随机决定选择哪个区间 if (UnityEngine.Random.value < 0.5f) // 50% 概率选择第一个区间 @@ -163,46 +163,11 @@ namespace AibisDream.FixSystem } } - void OnFrequencyChanged(float value) - { - float distance = Mathf.Abs(value - targetFrequency); - float maxDistance = Mathf.Max( - Mathf.Abs(frequencySlider.maxValue - targetFrequency), - Mathf.Abs(frequencySlider.minValue - targetFrequency) - ); - float t = Mathf.Clamp01(distance / maxDistance); - UpdateFrequencyShaderParameters(t); - CheckIfFound(t, ref isFrequencyFound); - - // 控制音效 - 使用更平滑的速度计算 - float currentSpeed = Mathf.Abs(value - lastFrequencyValue) / Time.deltaTime; - lastFrequencyValue = value; - // UpdateFaderSound(currentSpeed); - } - - void OnClarityChanged(float value) - { - float distance = Mathf.Abs(value - targetClarity); - float maxDistance = Mathf.Max( - Mathf.Abs(claritySlider.maxValue - targetClarity), - Mathf.Abs(claritySlider.minValue - targetClarity) - ); - float t = Mathf.Clamp01(distance / maxDistance); - - UpdateClarityShaderParameters(t); - CheckIfFound(t, ref isClarityFound); - - // 控制音效 - 使用更平滑的速度计算 - float currentSpeed = Mathf.Abs(value - lastClarityValue) / Time.deltaTime; - lastClarityValue = value; - // UpdateFaderSound(currentSpeed); - } - - void OnSearchComplete() + private void OnSearchComplete() { Debug.Log("Memory search completed."); - frequencySlider.interactable = false; - claritySlider.interactable = false; + frequencySlider.SetInteractable(false); + claritySlider.SetInteractable(false); // 停止拉条音频 if (isPlayingFaderSound) @@ -215,33 +180,6 @@ namespace AibisDream.FixSystem StartCoroutine(PlayMemory()); } - void CheckIfFound(float distance, ref bool isFound) - { - if (distance < lockThreshold) - { - isFound = true; - // 检查是否两个条件都满足 - if (isFrequencyFound && isClarityFound) - { - OnSearchComplete(); // 一旦条件满足,调用完成处理 - } - } - else - { - isFound = false; - } - } - - void UpdateFrequencyShaderParameters(float distance) - { - Memoryfrequencyloume.weight = Mathf.Lerp(0, 1, distance); - } - - void UpdateClarityShaderParameters(float t) - { - MemoryClarityVloume.weight = Mathf.Lerp(0, 1, t); - } - public void TweenWeight(Volume postProcessingVolume, float targetWeight, float duration) { // 动态调整权重 @@ -250,8 +188,6 @@ namespace AibisDream.FixSystem public void Init() { - isFrequencyFound = false; - isClarityFound = false; isProcessing = false; // 停止拉条音频 @@ -262,47 +198,14 @@ namespace AibisDream.FixSystem } if (frequencySlider != null) - { - frequencySlider.onValueChanged.RemoveAllListeners(); - frequencySlider.onValueChanged.AddListener(OnFrequencyChanged); - frequencySlider.gameObject.SetActive(false); - frequencySlider.value = 3; - lastFrequencyValue = frequencySlider.value; - frequencySlider.interactable = true; - // 添加EventTrigger监听(避免 Init 重复调用时叠加) - var trigger = frequencySlider.gameObject.GetComponent() ?? frequencySlider.gameObject.AddComponent(); - trigger.triggers.Clear(); - var entryDown = new EventTrigger.Entry { eventID = EventTriggerType.PointerDown }; - entryDown.callback.AddListener((data) => OnFaderPointerDown()); - trigger.triggers.Add(entryDown); - var entryUp = new EventTrigger.Entry { eventID = EventTriggerType.PointerUp }; - entryUp.callback.AddListener((data) => OnFaderPointerUp()); - trigger.triggers.Add(entryUp); - } - + frequencySlider.Init(memoryFrequencyVolume); if (claritySlider != null) - { - claritySlider.onValueChanged.RemoveAllListeners(); - claritySlider.onValueChanged.AddListener(OnClarityChanged); - claritySlider.gameObject.SetActive(false); - claritySlider.value = 3; - lastClarityValue = claritySlider.value; - claritySlider.interactable = true; - var trigger = claritySlider.gameObject.GetComponent() ?? claritySlider.gameObject.AddComponent(); - trigger.triggers.Clear(); - var entryDown = new EventTrigger.Entry { eventID = EventTriggerType.PointerDown }; - entryDown.callback.AddListener((data) => OnFaderPointerDown()); - trigger.triggers.Add(entryDown); - var entryUp = new EventTrigger.Entry { eventID = EventTriggerType.PointerUp }; - entryUp.callback.AddListener((data) => OnFaderPointerUp()); - trigger.triggers.Add(entryUp); - } + claritySlider.Init(memoryClarityVolume); - fadeMaterial.SetFloat("_DirectionalDistortionFade", -15f); - memoryFinishedspRender.color = Color.black; - MemoryProcessVloume.weight = 0; - MemoryClarityVloume.weight = 0; - Memoryfrequencyloume.weight = 0; + if (fadeMaterial != null) + fadeMaterial.SetFloat("_DirectionalDistortionFade", FadeMaterialStartValue); + memoryFinishedSpriteRenderer.color = Color.black; + memoryProcessVolume.weight = 0; _activePunchTape = null; playBackground.gameObject.SetActive(false); @@ -312,16 +215,12 @@ namespace AibisDream.FixSystem [YarnCommand("MemoryFinished")] public IEnumerator MemoryFinished() { - // AudioManager.PauseMemoryAudio(); - // Fade out alpha from 1 to 0 - yield return playBackground.DOFade(0f, 1f).WaitForCompletion(); // Fade to fully transparent over 1 second + yield return playBackground.DOFade(0f, DefaultFadeDuration).WaitForCompletion(); - // Deactivate playBackground after fade out playBackground.gameObject.SetActive(false); PunchTapeManager.Instance.OpenFavoritesPanel(); - // Initialize and release slot Init(); EnsurePunchTapeSlotReference(); if (punchTapeSlot != null && punchTapeSlot.punchTape != null) @@ -334,8 +233,9 @@ namespace AibisDream.FixSystem [YarnCommand("ColorFadeOut")] public IEnumerator ColorFadeOut() { - fadeMaterial.SetFloat("_DirectionalDistortionFade", -15f); - // AudioManager.PauseMemoryAudio(); + if (fadeMaterial == null) yield break; + + fadeMaterial.SetFloat("_DirectionalDistortionFade", FadeMaterialStartValue); Tween tween = fadeMaterial.DOFloat(0, "_DirectionalDistortionFade", 3); yield return tween.WaitForCompletion(); } @@ -343,51 +243,41 @@ namespace AibisDream.FixSystem [YarnCommand("PlayMemory")] public IEnumerator PlayMemory(bool useYarn = false, string memName = "") { - memoryFinishedspRender.color = Color.white; + memoryFinishedSpriteRenderer.color = Color.white; + PunchTapeManager.Instance.CloseFavoritesPanel(); + if (useYarn) { - fadeMaterial.SetFloat("_DirectionalDistortionFade", -15); - PunchTapeManager.Instance.CloseFavoritesPanel(); + if (fadeMaterial != null) + fadeMaterial.SetFloat("_DirectionalDistortionFade", FadeMaterialStartValue); var memoryKey = string.Format(MemorySpritePath, memName); var memoryHandle = ResourceSystem.LoadAsync(memoryKey); yield return memoryHandle; Sprite memSprite = memoryHandle.Status == AsyncOperationStatus.Succeeded ? memoryHandle.Result : null; - memoryFinishedspRender.sprite = memSprite; + memoryFinishedSpriteRenderer.sprite = memSprite; memoryFinishedImageNoColor.sprite = memSprite; if (memSprite == null) { Debug.LogError("Memory not found: " + memoryKey); } - - // AudioManager.PlayMemoryAudio("mem_" + memName); - playBackground.gameObject.SetActive(true); - //PunchTapeManager.Instance.CloseFavoritesPanel(); - playBackground.alpha = 0; - AudioManager.Instance.PlaySfx("event:/ActionFB/mem_play"); - yield return playBackground.DOFade(1f, 1f).WaitForCompletion(); // Fade to fully opaque over 1 second - MemoryClarityVloume.weight = 0; - Memoryfrequencyloume.weight = 0; } else { - PunchTapeManager.Instance.CloseFavoritesPanel(); - // Activate playBackground and fade in alpha from 0 to 1 - // AudioManager.PlayMemoryAudio("mem_" + punchTapeSlot.punchTape.MemoryIndex); - playBackground.gameObject.SetActive(true); - //PunchTapeManager.Instance.CloseFavoritesPanel(); - playBackground.alpha = 0; - AudioManager.Instance.PlaySfx("event:/ActionFB/mem_play"); - yield return playBackground.DOFade(1f, 1f).WaitForCompletion(); // Fade to fully opaque over 1 second - // Set material properties - MemoryClarityVloume.weight = 0; - Memoryfrequencyloume.weight = 0; EnsurePunchTapeSlotReference(); if (punchTapeSlot == null || punchTapeSlot.punchTape == null) { Debug.LogError("[MemoryProcess] PlayMemory:未配置插槽或打孔带数据缺失。", this); yield break; } + } + playBackground.gameObject.SetActive(true); + playBackground.alpha = 0; + AudioManager.Instance.PlaySfx("event:/ActionFB/mem_play"); + yield return playBackground.DOFade(1f, DefaultFadeDuration).WaitForCompletion(); + + if (!useYarn) + { DialogController.Instance.StartDialogNode( LocalizationKit.GetL10NParamKey(punchTapeSlot.punchTape.ClueName)); } @@ -396,17 +286,16 @@ namespace AibisDream.FixSystem [YarnCommand("search_memory")] public IEnumerator SearchMemory(PunchTape punchTape, bool isSkipDialogue = false) { - // 停止之前的拉条音频 if (isPlayingFaderSound) { AudioManager.Instance.StopSfx("event:/ActionFB/mem_fader"); isPlayingFaderSound = false; } - targetClarity = GetRandomNumber(); - targetFrequency = GetRandomNumber(); + frequencySlider.TargetValue = GetRandomNumber(); + claritySlider.TargetValue = GetRandomNumber(); PunchTapeManager.Instance.CloseFavoritesPanel(); - //PunchTapeManager.Instance.CloseFavoritesPanel(); + if (punchTape == null) { Debug.Log("no punchTape"); @@ -415,7 +304,6 @@ namespace AibisDream.FixSystem _activePunchTape = punchTape; - // AudioManager.PauseMemoryAudio(); if (isProcessing) { Debug.LogWarning("Already processing a memory. Please wait until the process is finished."); @@ -428,29 +316,27 @@ namespace AibisDream.FixSystem Sprite memorySprite = searchMemoryHandle.Status == AsyncOperationStatus.Succeeded ? searchMemoryHandle.Result : null; - memoryFinishedspRender.sprite = memorySprite; + memoryFinishedSpriteRenderer.sprite = memorySprite; memoryFinishedImageNoColor.sprite = memorySprite; if (memorySprite == null) { Debug.LogError("Memory not found: " + searchMemoryKey); } - MemoryProcessVloume.weight = 1; + memoryProcessVolume.weight = 1; AudioManager.Instance.PlaySfx("event:/Scriptal/mem_load"); - yield return new WaitForSeconds(1f); + yield return new WaitForSeconds(SearchStartDelay); AudioManager.Instance.SetSfxParam("event:/Amb/amb_mem_tuning", "is_mem_tuning", 1); - - memoryFinishedspRender.color = Color.white; - OnFrequencyChanged(frequencySlider.value); - OnClarityChanged(claritySlider.value); - MemoryProcessVloume.weight = 0; - isProcessing = true; // 开始处理,设置为true - frequencySlider.gameObject.SetActive(true); - claritySlider.gameObject.SetActive(true); - DG.Tweening.Sequence memorySequence = DOTween.Sequence(); - yield return memorySequence.WaitForCompletion(); + memoryFinishedSpriteRenderer.color = Color.white; + memoryProcessVolume.weight = 0; + isProcessing = true; + bool freqUnlocked = false; + bool clarityUnlocked = false; + frequencySlider.UnlockSlider(() => freqUnlocked = true); + claritySlider.UnlockSlider(() => clarityUnlocked = true); + yield return new WaitUntil(() => freqUnlocked && clarityUnlocked); } @@ -458,13 +344,5 @@ namespace AibisDream.FixSystem { return isProcessing; } - - private void OnFaderPointerDown() - { - } - - private void OnFaderPointerUp() - { - } } } \ No newline at end of file diff --git a/Assets/Scripts/FixSystem/Memory/MemorySlider.cs b/Assets/Scripts/FixSystem/Memory/MemorySlider.cs new file mode 100644 index 000000000..9502a30c8 --- /dev/null +++ b/Assets/Scripts/FixSystem/Memory/MemorySlider.cs @@ -0,0 +1,135 @@ +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; + 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(); + playableDirector = GetComponent(); + + _volume = volume; + IsFound = false; + slider.onValueChanged.RemoveAllListeners(); + slider.onValueChanged.AddListener(HandleValueChanged); + slider.maxValue = sliderMaxValue; + slider.value = defaultValue; + UpdateVolumeWeight(slider.value); + + isLocked = true; + slider.interactable = false; + } + + public void SetInteractable(bool interactable) => slider.interactable = interactable; + + public void UnlockSlider(Action onComplete = null) + { + if (!isLocked) + { + onComplete?.Invoke(); + return; + } + + PlayTimeline(openAsset, () => + { + slider.interactable = true; + isLocked = false; + onComplete?.Invoke(); + }); + } + + public void LockSlider(Action onComplete = null) + { + if (isLocked) + { + onComplete?.Invoke(); + return; + } + + slider.value = 0f; + UpdateVolumeWeight(0f); + + PlayTimeline(closeAsset, () => + { + slider.interactable = false; + 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 callback = null; + callback = pd => + { + playableDirector.stopped -= callback; + onComplete?.Invoke(); + }; + playableDirector.stopped += callback; + playableDirector.Play(); + } + } +} diff --git a/Assets/Scripts/FixSystem/Memory/MemorySlider.cs.meta b/Assets/Scripts/FixSystem/Memory/MemorySlider.cs.meta new file mode 100644 index 000000000..20664b70e --- /dev/null +++ b/Assets/Scripts/FixSystem/Memory/MemorySlider.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 3538641e32b03b44f9824058d4275a73 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: