diff --git a/Assets/Scripts/FixSystem/Memory/MemoryProcess.cs b/Assets/Scripts/FixSystem/Memory/MemoryProcess.cs index fbb1c87f0..8df8c0472 100644 --- a/Assets/Scripts/FixSystem/Memory/MemoryProcess.cs +++ b/Assets/Scripts/FixSystem/Memory/MemoryProcess.cs @@ -1,5 +1,4 @@ using System.Collections; -using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; using DG.Tweening; @@ -13,448 +12,459 @@ using AibisDream.Kit; using UnityEngine.EventSystems; using UnityEngine.ResourceManagement.AsyncOperations; -public class MemoryProcess : MonoBehaviour +namespace AibisDream.FixSystem { - private const string MemorySpritePath = "Sprite/Memory/{0}.png"; - - public BubbleSlotGroup bubbleSlotGroup; - public GameObject memoryView; - public GameObject projector; - public SpriteRenderer memoryFinishedspRender; // 用于显示记忆处理结束的表现 - public SpriteRenderer memoryFinishedImageNoColor; // 用于显示记忆处理结束的表现 - - [SerializeField] private Volume MainEffect; - [SerializeField] private Volume MemoryClarityVloume; - [SerializeField] private Volume Memoryfrequencyloume; - [SerializeField] private Volume MemoryProcessVloume; - public Slider frequencySlider; // 频率调节滑动条 - - public Slider claritySlider; // 频率调节滑动条 - - public CanvasGroup playBackground; - public float targetFrequency = 1.25f; // 目标频率 - - public float targetClarity = 5.7f; // 目标频率 - - private GameObject memoryGroup; - private float lockThreshold = 0.06f; // 锁定阈值 - - 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 PunchTape; - - private MemoryClueSlot clueSlot; - - public Material fadeMaterial; - - private Dictionary processedPunchTapes = new Dictionary(); - - public void OpenMemoryView() + public class MemoryProcess : MonoBehaviour { - memoryGroup.SetActive(true); - memoryView.gameObject.SetActive(true); - - } + private const string MemorySpritePath = "Sprite/Memory/{0}.png"; - public IEnumerator DropMemoryProjector() - { - DG.Tweening.Sequence zoomInSequence = DOTween.Sequence(); - zoomInSequence.Append(projector.transform.DOLocalMoveY(-5, 2f).SetEase(Ease.OutElastic, 0.5f)); - yield return zoomInSequence.WaitForCompletion(); - } + public BubbleSlotGroup bubbleSlotGroup; + public GameObject memoryView; + public GameObject projector; + public SpriteRenderer memoryFinishedspRender; // 用于显示记忆处理结束的表现 + public SpriteRenderer memoryFinishedImageNoColor; // 用于显示记忆处理结束的表现 - 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(); - } + [SerializeField] private Volume MainEffect; + [SerializeField] private Volume MemoryClarityVloume; + [SerializeField] private Volume Memoryfrequencyloume; + [SerializeField] private Volume MemoryProcessVloume; + public Slider frequencySlider; // 频率调节滑动条 - public void CloseMemoryView() - { - memoryView.gameObject.SetActive(false); - memoryGroup.SetActive(false); - } + public Slider claritySlider; // 频率调节滑动条 - public void OnEnter() - { - OpenMemoryView(); - clueSlot = FindObjectOfType(); - FavoritesManager.Instance.OpenFavorites(); - playBackground.gameObject.SetActive(false); - playBackground.alpha = 0; - } + public CanvasGroup playBackground; + public float targetFrequency = 1.25f; // 目标频率 - public void OnShow() - { - Debug.Log("memoryView showed."); - clueSlot = FindObjectOfType(); - FavoritesManager.Instance.OpenFavorites(); - playBackground.gameObject.SetActive(false); - playBackground.alpha = 0; - } + public float targetClarity = 5.7f; // 目标频率 - public void OnExit() - { - // 停止拉条音频 - if (isPlayingFaderSound) + private GameObject memoryGroup; + private float lockThreshold = 0.06f; // 锁定阈值 + + 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; + + public void OpenMemoryView() { - AudioManager.Instance.StopSfx("event:/ActionFB/mem_fader"); - isPlayingFaderSound = false; + memoryGroup.SetActive(true); + memoryView.gameObject.SetActive(true); + } - - FavoritesManager.Instance.CloseFavorites(); - CloseMemoryView(); - // 离开视图时的逻辑 - Debug.Log("MemoryView exited."); - } - private void Awake() - { - InitSystem(); - } - - 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); - } - - - private void Start() - { - Init(); - } - - float GetRandomNumber() - { - // 随机决定选择哪个区间 - if (UnityEngine.Random.value < 0.5f) // 50% 概率选择第一个区间 + public IEnumerator DropMemoryProjector() { - return UnityEngine.Random.Range(0.5f, 2f); + DG.Tweening.Sequence zoomInSequence = DOTween.Sequence(); + zoomInSequence.Append(projector.transform.DOLocalMoveY(-5, 2f).SetEase(Ease.OutElastic, 0.5f)); + yield return zoomInSequence.WaitForCompletion(); } - else // 50% 概率选择第二个区间 + + public void PullMemoryProjector() { - return UnityEngine.Random.Range(4f, 5.5f); + DG.Tweening.Sequence zoomOutSequence = DOTween.Sequence(); + zoomOutSequence.Append(projector.transform.DOLocalMoveY(0, 2f).SetEase(Ease.OutElastic, 0.5f)); + //yield return zoomOutSequence.WaitForCompletion(); } - } - 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() - { - Debug.Log("Memory search completed."); - frequencySlider.interactable = false; - claritySlider.interactable = false; - - // 停止拉条音频 - if (isPlayingFaderSound) + public void CloseMemoryView() { - AudioManager.Instance.StopSfx("event:/ActionFB/mem_fader"); - isPlayingFaderSound = false; + memoryView.gameObject.SetActive(false); + memoryGroup.SetActive(false); } - - AudioManager.Instance.SetSfxParam("event:/Amb/amb_mem_tuning", "is_mem_tuning", 0); - StartCoroutine(PlayMemory()); - } - void CheckIfFound(float distance, ref bool isFound) - { - if (distance < lockThreshold) + public void OnEnter() { - isFound = true; - // 检查是否两个条件都满足 - if (isFrequencyFound && isClarityFound) + OpenMemoryView(); + EnsurePunchTapeSlotReference(); + PunchTapeManager.Instance.OpenFavoritesPanel(); + playBackground.gameObject.SetActive(false); + playBackground.alpha = 0; + } + + public void OnShow() + { + Debug.Log("memoryView showed."); + EnsurePunchTapeSlotReference(); + PunchTapeManager.Instance.OpenFavoritesPanel(); + playBackground.gameObject.SetActive(false); + playBackground.alpha = 0; + } + + public void OnExit() + { + // 停止拉条音频 + if (isPlayingFaderSound) { - 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) - { - // 动态调整权重 - DOTween.To(() => postProcessingVolume.weight, x => postProcessingVolume.weight = x, targetWeight, duration); - } - - public void Init() - { - isFrequencyFound = false; - isClarityFound = false; - isProcessing = false; - - // 停止拉条音频 - if (isPlayingFaderSound) - { - AudioManager.Instance.StopSfx("event:/ActionFB/mem_fader"); - isPlayingFaderSound = false; - } - - if (frequencySlider != null) - { - frequencySlider.onValueChanged.AddListener(OnFrequencyChanged); - frequencySlider.gameObject.SetActive(false); - frequencySlider.value = 3; - lastFrequencyValue = frequencySlider.value; - frequencySlider.interactable = true; - // 添加EventTrigger监听 - var trigger = frequencySlider.gameObject.GetComponent() ?? frequencySlider.gameObject.AddComponent(); - 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); - } - - if (claritySlider != null) - { - claritySlider.onValueChanged.AddListener(OnClarityChanged); - claritySlider.gameObject.SetActive(false); - claritySlider.value = 3; - lastClarityValue = claritySlider.value; - claritySlider.interactable = true; - // 添加EventTrigger监听 - var trigger = claritySlider.gameObject.GetComponent() ?? claritySlider.gameObject.AddComponent(); - 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); - } - - fadeMaterial.SetFloat("_DirectionalDistortionFade", -15f); - memoryFinishedspRender.color = Color.black; - MemoryProcessVloume.weight = 0; - MemoryClarityVloume.weight = 0; - Memoryfrequencyloume.weight = 0; - PunchTape = null; - - playBackground.gameObject.SetActive(false); - playBackground.alpha = 0; - } - - [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 - - // Deactivate playBackground after fade out - playBackground.gameObject.SetActive(false); - - FavoritesManager.Instance.OpenFavorites(); - - // Initialize and release slot - Init(); - clueSlot.punchTape.used = true; - clueSlot.ReleaseSlot(); - } - - [YarnCommand("ColorFadeOut")] - public IEnumerator ColorFadeOut() - { - fadeMaterial.SetFloat("_DirectionalDistortionFade", -15f); - // AudioManager.PauseMemoryAudio(); - Tween tween = fadeMaterial.DOFloat(0, "_DirectionalDistortionFade", 3); - yield return tween.WaitForCompletion(); - } - - [YarnCommand("PlayMemory")] - public IEnumerator PlayMemory(bool useYarn = false, string memName = "") - { - memoryFinishedspRender.color = Color.white; - if (useYarn) - { - fadeMaterial.SetFloat("_DirectionalDistortionFade", -15); - FavoritesManager.Instance.CloseFavorites(); - 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; - memoryFinishedImageNoColor.sprite = memSprite; - if (memSprite == null) - { - Debug.LogError("Memory not found: " + memoryKey); + AudioManager.Instance.StopSfx("event:/ActionFB/mem_fader"); + isPlayingFaderSound = false; } - // AudioManager.PlayMemoryAudio("mem_" + memName); - playBackground.gameObject.SetActive(true); - //FavoritesManager.Instance.CloseFavorites(); - playBackground.alpha = 0; - AudioManager.Instance.PlaySfx("event:/ActionFB/mem_play"); - yield return playBackground.DOFade(1f, 1f).WaitForCompletion(); // Fade to fully opaque over 1 second + PunchTapeManager.Instance.CloseFavoritesPanel(); + CloseMemoryView(); + // 离开视图时的逻辑 + Debug.Log("MemoryView exited."); + } + + private void Awake() + { + InitSystem(); + } + + private void EnsurePunchTapeSlotReference() + { + if (punchTapeSlot != null) return; + punchTapeSlot = MemoryPunchTapeSlot.ActiveInstance; + if (punchTapeSlot == null) + { + Debug.LogWarning( + "[MemoryProcess] 未配置 MemoryPunchTapeSlot:请在 Inspector 将 punchTapeSlot 赋值为场景中的插槽,或确保存在 MemoryPunchTapeSlot。", + this); + } + } + + 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); + } + + + private void Start() + { + Init(); + } + + float GetRandomNumber() + { + // 随机决定选择哪个区间 + if (UnityEngine.Random.value < 0.5f) // 50% 概率选择第一个区间 + { + return UnityEngine.Random.Range(0.5f, 2f); + } + else // 50% 概率选择第二个区间 + { + return UnityEngine.Random.Range(4f, 5.5f); + } + } + + 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() + { + Debug.Log("Memory search completed."); + frequencySlider.interactable = false; + claritySlider.interactable = false; + + // 停止拉条音频 + if (isPlayingFaderSound) + { + AudioManager.Instance.StopSfx("event:/ActionFB/mem_fader"); + isPlayingFaderSound = false; + } + + AudioManager.Instance.SetSfxParam("event:/Amb/amb_mem_tuning", "is_mem_tuning", 0); + 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) + { + // 动态调整权重 + DOTween.To(() => postProcessingVolume.weight, x => postProcessingVolume.weight = x, targetWeight, duration); + } + + public void Init() + { + isFrequencyFound = false; + isClarityFound = false; + isProcessing = false; + + // 停止拉条音频 + if (isPlayingFaderSound) + { + AudioManager.Instance.StopSfx("event:/ActionFB/mem_fader"); + isPlayingFaderSound = false; + } + + 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); + } + + 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); + } + + fadeMaterial.SetFloat("_DirectionalDistortionFade", -15f); + memoryFinishedspRender.color = Color.black; + MemoryProcessVloume.weight = 0; MemoryClarityVloume.weight = 0; Memoryfrequencyloume.weight = 0; - } - else - { - FavoritesManager.Instance.CloseFavorites(); - // Activate playBackground and fade in alpha from 0 to 1 - // AudioManager.PlayMemoryAudio("mem_" + clueSlot.punchTape.MemoryIndex); - playBackground.gameObject.SetActive(true); - //FavoritesManager.Instance.CloseFavorites(); + _activePunchTape = null; + + playBackground.gameObject.SetActive(false); 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; - DialogController.Instance.StartDialogNode(clueSlot.punchTape.YarnNodeName); } - } - [YarnCommand("search_memory")] - public IEnumerator SearchMemory(PunchTape punchTape, bool isSkipDialogue = false) - { - // 停止之前的拉条音频 - if (isPlayingFaderSound) + [YarnCommand("MemoryFinished")] + public IEnumerator MemoryFinished() { - AudioManager.Instance.StopSfx("event:/ActionFB/mem_fader"); - isPlayingFaderSound = false; + // AudioManager.PauseMemoryAudio(); + // Fade out alpha from 1 to 0 + yield return playBackground.DOFade(0f, 1f).WaitForCompletion(); // Fade to fully transparent over 1 second + + // Deactivate playBackground after fade out + playBackground.gameObject.SetActive(false); + + PunchTapeManager.Instance.OpenFavoritesPanel(); + + // Initialize and release slot + Init(); + EnsurePunchTapeSlotReference(); + if (punchTapeSlot != null && punchTapeSlot.punchTape != null) + { + punchTapeSlot.punchTape.used = true; + punchTapeSlot.ReleaseSlot(); + } } - - targetClarity = GetRandomNumber(); - targetFrequency = GetRandomNumber(); - FavoritesManager.Instance.CloseFavorites(); - //FavoritesManager.Instance.CloseFavorites(); - if (punchTape == null) + + [YarnCommand("ColorFadeOut")] + public IEnumerator ColorFadeOut() { - Debug.Log("no punchTape"); - yield break; + fadeMaterial.SetFloat("_DirectionalDistortionFade", -15f); + // AudioManager.PauseMemoryAudio(); + Tween tween = fadeMaterial.DOFloat(0, "_DirectionalDistortionFade", 3); + yield return tween.WaitForCompletion(); } - PunchTape = punchTape; - - // AudioManager.PauseMemoryAudio(); - if (isProcessing) + [YarnCommand("PlayMemory")] + public IEnumerator PlayMemory(bool useYarn = false, string memName = "") { - Debug.LogWarning("Already processing a memory. Please wait until the process is finished."); - yield break; + memoryFinishedspRender.color = Color.white; + if (useYarn) + { + fadeMaterial.SetFloat("_DirectionalDistortionFade", -15); + PunchTapeManager.Instance.CloseFavoritesPanel(); + 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; + 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; + } + + DialogController.Instance.StartDialogNode( + LocalizationKit.GetL10NParamKey(punchTapeSlot.punchTape.ClueName)); + } } - var searchMemoryKey = string.Format(MemorySpritePath, PunchTape.MemoryIndex); - var searchMemoryHandle = ResourceSystem.LoadAsync(searchMemoryKey); - yield return searchMemoryHandle; - Sprite memorySprite = searchMemoryHandle.Status == AsyncOperationStatus.Succeeded - ? searchMemoryHandle.Result - : null; - memoryFinishedspRender.sprite = memorySprite; - memoryFinishedImageNoColor.sprite = memorySprite; - if (memorySprite == null) + [YarnCommand("search_memory")] + public IEnumerator SearchMemory(PunchTape punchTape, bool isSkipDialogue = false) { - Debug.LogError("Memory not found: " + searchMemoryKey); + // 停止之前的拉条音频 + if (isPlayingFaderSound) + { + AudioManager.Instance.StopSfx("event:/ActionFB/mem_fader"); + isPlayingFaderSound = false; + } + + targetClarity = GetRandomNumber(); + targetFrequency = GetRandomNumber(); + PunchTapeManager.Instance.CloseFavoritesPanel(); + //PunchTapeManager.Instance.CloseFavoritesPanel(); + if (punchTape == null) + { + Debug.Log("no punchTape"); + yield break; + } + + _activePunchTape = punchTape; + + // AudioManager.PauseMemoryAudio(); + if (isProcessing) + { + Debug.LogWarning("Already processing a memory. Please wait until the process is finished."); + yield break; + } + + var searchMemoryKey = string.Format(MemorySpritePath, _activePunchTape.MemoryIndex); + var searchMemoryHandle = ResourceSystem.LoadAsync(searchMemoryKey); + yield return searchMemoryHandle; + Sprite memorySprite = searchMemoryHandle.Status == AsyncOperationStatus.Succeeded + ? searchMemoryHandle.Result + : null; + memoryFinishedspRender.sprite = memorySprite; + memoryFinishedImageNoColor.sprite = memorySprite; + if (memorySprite == null) + { + Debug.LogError("Memory not found: " + searchMemoryKey); + } + + MemoryProcessVloume.weight = 1; + AudioManager.Instance.PlaySfx("event:/Scriptal/mem_load"); + + yield return new WaitForSeconds(1f); + 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(); } - MemoryProcessVloume.weight = 1; - AudioManager.Instance.PlaySfx("event:/Scriptal/mem_load"); - yield return new WaitForSeconds(1f); - AudioManager.Instance.SetSfxParam("event:/Amb/amb_mem_tuning", "is_mem_tuning", 1); + public bool IsProcessing() + { + return isProcessing; + } + private void OnFaderPointerDown() + { + } - 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(); + private void OnFaderPointerUp() + { + } } - - - public bool IsProcessing() - { - return isProcessing; - } - - private void OnFaderPointerDown() - { - // if (!isPlayingFaderSound) - // { - // AudioManager.Instance.PlaySfx("event:/ActionFB/mem_fader"); - // isPlayingFaderSound = true; - // } - } - - private void OnFaderPointerUp() - { - // if (isPlayingFaderSound) - // { - // AudioManager.Instance.StopSfx("event:/ActionFB/mem_fader"); - // isPlayingFaderSound = false; - // } - } - - // private void UpdateFaderSound(float speed) - // { - // // 速度归一化,最大速度可根据实际情况调整 - // float normalizedSpeed = Mathf.Clamp01(speed / 10f); - // AudioManager.Instance.SetSfxParam("event:/ActionFB/mem_fader", "mem_fader_speed", normalizedSpeed); - // } } \ No newline at end of file diff --git a/Assets/Scripts/FixSystem/PunchType/MemoryPunchTapeSlot.cs b/Assets/Scripts/FixSystem/PunchType/MemoryPunchTapeSlot.cs new file mode 100644 index 000000000..caa480884 --- /dev/null +++ b/Assets/Scripts/FixSystem/PunchType/MemoryPunchTapeSlot.cs @@ -0,0 +1,190 @@ +using AibisDream; +using AibisDream.FixSystem; +using AibisDream.Kit; +using DG.Tweening; +using UnityEngine; +using UnityEngine.UI; + +/// +/// 记忆流程中的打孔带投放槽:胶片动画、投放逻辑;插入后触发 。 +/// +namespace AibisDream.FixSystem +{ + public class MemoryPunchTapeSlot : MonoBehaviour + { + /// 当前场景中的记忆插槽(用于 MemoryProcess 等避免 FindObjectOfType)。 + public static MemoryPunchTapeSlot ActiveInstance { get; private set; } + + public Image filmImage; + public Image RoateImage; + + private Vector3 filmStartLocalPosition; + private PunchTapeInteraction interaction; + + [HideInInspector] + public PunchTapeItem currentPunchTapeItem; + public Image punchTapeImage; + public Image coverClose; + public Image coverOpen; + + [HideInInspector] + public bool isOccupied = false; + + [HideInInspector] + public bool isLocked = false; + + private MemoryProcess memoryManager; + [HideInInspector] public PunchTape punchTape; + + private void Awake() + { + if (ActiveInstance != null && ActiveInstance != this) + { + Debug.LogWarning("[MemoryPunchTapeSlot] 场景中存在多个 MemoryPunchTapeSlot,ActiveInstance 将保留最先 Awake 的实例。", this); + } + else + { + ActiveInstance = this; + } + } + + private void OnDestroy() + { + if (ActiveInstance == this) + ActiveInstance = null; + } + + private void OnEnable() + { + PunchTapeFavoritesDragEvents.DragStarted += OnFavoriteDragStarted; + PunchTapeFavoritesDragEvents.DragEnded += OnFavoriteDragEnded; + } + + private void OnDisable() + { + PunchTapeFavoritesDragEvents.DragStarted -= OnFavoriteDragStarted; + PunchTapeFavoritesDragEvents.DragEnded -= OnFavoriteDragEnded; + } + + private void OnFavoriteDragStarted() + { + if (isOccupied) return; + coverOpen.enabled = true; + coverClose.enabled = false; + } + + private void OnFavoriteDragEnded() + { + if (isOccupied) return; + coverOpen.enabled = false; + coverClose.enabled = true; + } + + private void Start() + { + currentPunchTapeItem = null; + coverClose.enabled = true; + coverOpen.enabled = false; + punchTapeImage.enabled = false; + filmStartLocalPosition = filmImage.rectTransform.localPosition; + + memoryManager = FixSystemCenter.SystemDic.Get(); + if (memoryManager == null) + Debug.LogWarning("[MemoryPunchTapeSlot] 未在 FixSystemCenter.SystemDic 中注册 MemoryProcess。", this); + } + + public void SetSlotCorrect() + { + SetSlotLockState(true); + } + + public bool TryAcceptDrop(PunchTapeItem item) + { + if (isOccupied || item == null) return false; + + PunchTape droppedTape = item.GetPunchTapeData(); + if (droppedTape == null) return false; + + interaction = item.GetComponent(); + currentPunchTapeItem = item; + isOccupied = true; + punchTapeImage.enabled = true; + AudioManager.Instance.PlaySfx("event:/ActionFB/mem_insert"); + StartFilmAnimation(); + PunchTapeManager.Instance.CloseFavoritesPanel(); + punchTapeImage.rectTransform.DOAnchorPosY(-473f, 4f) + .OnComplete(() => + { + punchTapeImage.enabled = false; + punchTapeImage.rectTransform.DOAnchorPosY(-3f, 0.1f); + OnPunchTapeInsertedAfterAnimation(droppedTape); + StopFilmAnimation(); + }); + return true; + } + + public void ReleaseSlot() + { + Debug.Log("释放记忆插槽"); + coverClose.enabled = false; + coverOpen.enabled = true; + isOccupied = false; + currentPunchTapeItem = null; + interaction = null; + filmImage.rectTransform.localPosition = filmStartLocalPosition; + } + + private void OnPunchTapeInsertedAfterAnimation(PunchTape tape) + { + punchTape = tape; + coverClose.enabled = true; + coverOpen.enabled = false; + PlayMemory(punchTape); + PunchTapeManager.Instance.RemovePunchTape(tape); + } + + private void PlayMemory(PunchTape tape) + { + if (tape != null) + { + Debug.Log("播放记忆:" + tape.MemoryIndex); + if (memoryManager != null) + memoryManager.StartCoroutine(memoryManager.SearchMemory(tape)); + } + } + + public void SetSlotLockState(bool lockState) + { + isLocked = lockState; + } + + private void StartFilmAnimation() + { + filmImage.rectTransform.DOLocalMoveX(-494f, 1f) + .SetLoops(-1, LoopType.Restart) + .SetEase(Ease.Linear); + RoateImage.rectTransform.DORotate(new Vector3(0, 0, 360), 1f, RotateMode.FastBeyond360) + .SetLoops(-1, LoopType.Restart) + .SetEase(Ease.Linear); + } + + private void StopFilmAnimation() + { + filmImage.rectTransform.DOKill(); + RoateImage.rectTransform.DOKill(); + } + } + + /// + /// 收藏夹内打孔带拖拽生命周期事件,供插槽等监听以切换封面表现,避免 UI Item 直接引用插槽。 + /// + public static class PunchTapeFavoritesDragEvents + { + public static event System.Action DragStarted; + public static event System.Action DragEnded; + + public static void NotifyDragStarted() => DragStarted?.Invoke(); + + public static void NotifyDragEnded() => DragEnded?.Invoke(); + } +} diff --git a/Assets/Scripts/FixSystem/PunchType/MemoryPunchTapeSlot.cs.meta b/Assets/Scripts/FixSystem/PunchType/MemoryPunchTapeSlot.cs.meta new file mode 100644 index 000000000..f11bbbded --- /dev/null +++ b/Assets/Scripts/FixSystem/PunchType/MemoryPunchTapeSlot.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 08ace7d1cbb872b49a73a914f1f93b10 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/FixSystem/PunchType/PunchTape.cs b/Assets/Scripts/FixSystem/PunchType/PunchTape.cs new file mode 100644 index 000000000..6e1a0ef17 --- /dev/null +++ b/Assets/Scripts/FixSystem/PunchType/PunchTape.cs @@ -0,0 +1,64 @@ +using System; + +/// +/// 打孔带数据:用于记忆系统的资源键与对话节点,不依赖已废弃的 Clue 体系。 +/// 仅保留可序列化字段;本地化与 Yarn 键由 UI / 流程层调用 LocalizationKit 处理。 +/// +namespace AibisDream.FixSystem +{ + [Serializable] + public class PunchTape : IEquatable + { + public string Category; + public string ClueName; + public string MemoryIndex; + public bool used; + + public PunchTape() + { + } + + public PunchTape(string category, string name, string memoryIndex) + { + Category = category; + ClueName = name; + MemoryIndex = memoryIndex; + } + + /// 用于收藏列表去重与 UI 行映射(与 Equals 一致)。 + public string GetRowKey() + { + return $"{Category ?? string.Empty}\u001f{ClueName ?? string.Empty}\u001f{MemoryIndex ?? string.Empty}"; + } + + public bool Equals(PunchTape other) + { + if (other is null) return false; + if (ReferenceEquals(this, other)) return true; + return string.Equals(Category, other.Category, StringComparison.Ordinal) && + string.Equals(ClueName, other.ClueName, StringComparison.Ordinal) && + string.Equals(MemoryIndex, other.MemoryIndex, StringComparison.Ordinal); + } + + public override bool Equals(object obj) => Equals(obj as PunchTape); + + public override int GetHashCode() + { + unchecked + { + var hc = (Category != null ? StringComparer.Ordinal.GetHashCode(Category) : 0); + hc = (hc * 397) ^ (ClueName != null ? StringComparer.Ordinal.GetHashCode(ClueName) : 0); + hc = (hc * 397) ^ (MemoryIndex != null ? StringComparer.Ordinal.GetHashCode(MemoryIndex) : 0); + return hc; + } + } + + public static bool operator ==(PunchTape left, PunchTape right) + { + if (left is null) return right is null; + return left.Equals(right); + } + + public static bool operator !=(PunchTape left, PunchTape right) => !(left == right); + } +} diff --git a/Assets/Scripts/FixSystem/PunchType/PunchTape.cs.meta b/Assets/Scripts/FixSystem/PunchType/PunchTape.cs.meta new file mode 100644 index 000000000..8a981bbbd --- /dev/null +++ b/Assets/Scripts/FixSystem/PunchType/PunchTape.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f4e3d2c1b0a9f8e7d6c5b4a3210fed98 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/FixSystem/PunchType/PunchTapeFavoritesPanel.cs b/Assets/Scripts/FixSystem/PunchType/PunchTapeFavoritesPanel.cs new file mode 100644 index 000000000..1f08fd407 --- /dev/null +++ b/Assets/Scripts/FixSystem/PunchType/PunchTapeFavoritesPanel.cs @@ -0,0 +1,191 @@ +using System.Collections.Generic; +using AibisDream.Kit; +using DG.Tweening; +using UnityEngine; + +/// +/// 打孔带收藏夹视图。侧栏( 上的 RectTransform)做滑入滑出; +/// 可选的 仅显隐,与侧栏同开同关。数据来自 。 +/// +namespace AibisDream.FixSystem +{ + public class PunchTapeFavoritesPanel : MonoBehaviour + { + private const float PanelSlideDuration = 0.5f; + + [Header("收藏夹 UI")] + [Tooltip("列表项父节点(Vertical Layout 等)")] + [SerializeField] private Transform punchTapeContent; + + [SerializeField] private GameObject punchTapeItemPrefab; + + [Tooltip("配套列表面板")] + [SerializeField] private RectTransform punchTapePanel; + + [Header("定位信息")] + [SerializeField] private Vector2 panelHiddenAnchoredPos; + [SerializeField] private Vector2 panelVisibleAnchoredPos; + + private SimpleObjectPool _rowPool; + private readonly Dictionary _rowByTapeKey = new(); + + private PunchTapeManager PunchTapeManager => PunchTapeManager.Instance; + + private void Start() + { + if (punchTapeContent == null) + { + Debug.LogError("[PunchTapeFavoritesPanel] 未指定 punchTapeContent。", this); + enabled = false; + return; + } + if (punchTapeItemPrefab == null) + { + Debug.LogError("[PunchTapeFavoritesPanel] 未指定 punchTapeItemPrefab。", this); + enabled = false; + return; + } + + PunchTapeManager.OnCollectionChanged += HandleCollectionChanged; + ApplyClosedLayout(); + SyncListToData(); + } + + private void OnDestroy() + { + if (PunchTapeManager != null) + PunchTapeManager.OnCollectionChanged -= HandleCollectionChanged; + + punchTapePanel?.DOKill(); + + DisposeRowPool(); + _rowByTapeKey.Clear(); + } + + private void HandleCollectionChanged() => SyncListToData(); + + private void DisposeRowPool() + { + if (_rowPool == null) + return; + + _rowPool.Clear(Destroy); + _rowPool = null; + } + + private void EnsureRowPool() + { + if (_rowPool != null) + return; + + _rowPool = new SimpleObjectPool( + () => + { + var go = Instantiate(punchTapeItemPrefab, punchTapeContent); + go.SetActive(false); + return go; + }, + go => + { + go.SetActive(false); + go.transform.SetParent(punchTapeContent, false); + }); + } + + private void SyncListToData() + { + EnsureRowPool(); + + var favorites = PunchTapeManager.GetFavorites(); + RemoveRowsNotInFavorites(favorites); + EnsureRowsForFavorites(favorites); + } + + private void RemoveRowsNotInFavorites(IReadOnlyList favorites) + { + var keepKeys = new HashSet(); + for (var i = 0; i < favorites.Count; i++) + keepKeys.Add(favorites[i].GetRowKey()); + + var staleKeys = new List(); + foreach (var key in _rowByTapeKey.Keys) + { + if (!keepKeys.Contains(key)) + staleKeys.Add(key); + } + + foreach (var key in staleKeys) + { + if (_rowByTapeKey.TryGetValue(key, out var go) && go != null) + _rowPool.Recycle(go); + _rowByTapeKey.Remove(key); + } + } + + private void EnsureRowsForFavorites(IReadOnlyList favorites) + { + for (var i = 0; i < favorites.Count; i++) + { + var tape = favorites[i]; + var key = tape.GetRowKey(); + + if (!_rowByTapeKey.TryGetValue(key, out var rowGo) || rowGo == null) + { + rowGo = _rowPool.Allocate(); + rowGo.transform.SetParent(punchTapeContent, false); + _rowByTapeKey[key] = rowGo; + } + + BindRow(rowGo, tape); + rowGo.SetActive(true); + rowGo.transform.SetSiblingIndex(i); + } + } + + private static void BindRow(GameObject rowGo, PunchTape tape) + { + var interaction = rowGo.GetComponent(); + if (interaction != null) + { + interaction.SetIsLock(false); + interaction.SetIsFavorited(true); + } + + var item = rowGo.GetComponent(); + if (item != null) + item.Setup(tape); + } + + private void ApplyClosedLayout() + { + punchTapePanel.anchoredPosition = panelHiddenAnchoredPos; + + punchTapePanel.gameObject.SetActive(false); + } + + /// 侧栏滑入并显示; 若有则一并显示。 + public void OpenPanel() + { + SyncListToData(); + + punchTapePanel.gameObject.SetActive(true); + + punchTapePanel.DOKill(); + punchTapePanel.DOAnchorPos(panelVisibleAnchoredPos, PanelSlideDuration); + } + + /// 侧栏滑出,结束后侧栏与 一并隐藏。 + public void ClosePanel() + { + punchTapePanel.DOKill(); + punchTapePanel + .DOAnchorPos(panelHiddenAnchoredPos, PanelSlideDuration) + .OnComplete(OnSidebarClosed); + } + + private void OnSidebarClosed() + { + punchTapePanel.gameObject.SetActive(false); + } + } +} diff --git a/Assets/Scripts/FixSystem/PunchType/PunchTapeFavoritesPanel.cs.meta b/Assets/Scripts/FixSystem/PunchType/PunchTapeFavoritesPanel.cs.meta new file mode 100644 index 000000000..ef3d1df6d --- /dev/null +++ b/Assets/Scripts/FixSystem/PunchType/PunchTapeFavoritesPanel.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a84298a2467e2934fa5e8c60405a2834 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/FixSystem/PunchType/PunchTapeInteraction.cs b/Assets/Scripts/FixSystem/PunchType/PunchTapeInteraction.cs new file mode 100644 index 000000000..7c3d03a5e --- /dev/null +++ b/Assets/Scripts/FixSystem/PunchType/PunchTapeInteraction.cs @@ -0,0 +1,150 @@ +using System; +using UnityEngine; +using UnityEngine.EventSystems; + +/// +/// 打孔带在 UI 中的拖拽与点击交互。 +/// +namespace AibisDream.FixSystem +{ + public class PunchTapeInteraction : MonoBehaviour, IPointerEnterHandler, IPointerExitHandler, IPointerClickHandler, + IBeginDragHandler, IDragHandler, IEndDragHandler + { + private Transform originalParent; + public Transform OriginalParent => originalParent; + private Canvas canvas; + [HideInInspector] + public bool isFavorited = false; + + private RectTransform rectTransform; + private Vector2 originalAnchorMin; + private Vector2 originalAnchorMax; + private Vector2 originalOffsetMin; + private Vector2 originalOffsetMax; + private Vector2 originalPivot; + + private CursorMode cursorMode = CursorMode.Auto; + + public event Action OnInteraction; + + private bool isLocked = false; + private bool _favoriteDragActive; + + private void Awake() + { + rectTransform = GetComponent(); + } + + private void OnEnable() + { + canvas = GetComponentInParent(); + StoreOriginalTransform(); + } + + private void OnDisable() + { + if (_favoriteDragActive) + { + _favoriteDragActive = false; + PunchTapeFavoritesDragEvents.NotifyDragEnded(); + } + } + + private void StoreOriginalTransform() + { + originalParent = transform.parent; + originalAnchorMin = rectTransform.anchorMin; + originalAnchorMax = rectTransform.anchorMax; + originalOffsetMin = rectTransform.offsetMin; + originalOffsetMax = rectTransform.offsetMax; + originalPivot = rectTransform.pivot; + } + + public void OnPointerEnter(PointerEventData eventData) + { + } + + public void OnPointerExit(PointerEventData eventData) + { + Cursor.SetCursor(null, Vector2.zero, cursorMode); + } + + public void OnPointerClick(PointerEventData eventData) + { + } + + public void OnBeginDrag(PointerEventData eventData) + { + if (!isFavorited || isLocked) return; + + transform.SetParent(canvas.transform, true); + transform.SetAsLastSibling(); + var cg = GetComponent(); + if (cg != null) cg.blocksRaycasts = false; + _favoriteDragActive = true; + PunchTapeFavoritesDragEvents.NotifyDragStarted(); + } + + public void OnDrag(PointerEventData eventData) + { + if (!isFavorited || isLocked) return; + + if (RectTransformUtility.ScreenPointToLocalPointInRectangle( + canvas.transform as RectTransform, + eventData.position, + eventData.pressEventCamera, + out Vector2 localPoint)) + { + rectTransform.localPosition = new Vector3(localPoint.x, localPoint.y + 200f, 0f); + } + transform.rotation = Quaternion.Euler(0, 0, -90f); + } + + public void OnEndDrag(PointerEventData eventData) + { + if (!isFavorited || isLocked) return; + + var cg = GetComponent(); + if (cg != null) cg.blocksRaycasts = true; + + var slot = eventData.pointerEnter? + .GetComponentInParent(); + var item = GetComponent(); + + bool accepted = slot != null && slot.TryAcceptDrop(item); + + if (_favoriteDragActive) + { + _favoriteDragActive = false; + PunchTapeFavoritesDragEvents.NotifyDragEnded(); + } + + if (accepted) + Destroy(gameObject); + else + ReturnToOriginalPosition(); + } + + public void ReturnToOriginalPosition() + { + isLocked = false; + transform.rotation = Quaternion.Euler(0, 0, 0); + transform.SetParent(originalParent, false); + rectTransform.anchorMin = originalAnchorMin; + rectTransform.anchorMax = originalAnchorMax; + rectTransform.offsetMin = originalOffsetMin; + rectTransform.offsetMax = originalOffsetMax; + rectTransform.pivot = originalPivot; + } + + public void SetIsFavorited(bool favorited) + { + isFavorited = favorited; + } + + public void SetIsLock(bool locked) + { + isLocked = locked; + } + } +} diff --git a/Assets/Scripts/FixSystem/PunchType/PunchTapeInteraction.cs.meta b/Assets/Scripts/FixSystem/PunchType/PunchTapeInteraction.cs.meta new file mode 100644 index 000000000..55bf8c7ef --- /dev/null +++ b/Assets/Scripts/FixSystem/PunchType/PunchTapeInteraction.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d792e0af9bafc174d8a2566cf81b2c6a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/FixSystem/PunchType/PunchTapeItem.cs b/Assets/Scripts/FixSystem/PunchType/PunchTapeItem.cs new file mode 100644 index 000000000..9b1174ad9 --- /dev/null +++ b/Assets/Scripts/FixSystem/PunchType/PunchTapeItem.cs @@ -0,0 +1,61 @@ +using UnityEngine; +using TMPro; +using UnityEngine.UI; +using UnityEngine.Localization.Components; +using AibisDream.Framework; + +/// +/// 收藏夹 / 生成窗口中的打孔带 UI 项。 +/// +namespace AibisDream.FixSystem +{ + public class PunchTapeItem : MonoBehaviour + { + public Image clueImageUI; + public TMP_Text categoryText; + public LocalizeStringEvent nameText; + public GameObject stateText; + + [HideInInspector] + public bool used = false; + + private PunchTape punchTapeData; + + public GameObject finished; + + public void Setup(PunchTape punchTape) + { + punchTapeData = punchTape; + used = punchTape != null && punchTape.used; + ApplyDisplay(punchTapeData); + } + + private void ApplyDisplay(PunchTape pt) + { + if (pt == null) + { + Debug.LogWarning("PunchTape is null. Display aborted."); + return; + } + + if (clueImageUI != null && pt.used) + clueImageUI.color = Color.grey; + + if (stateText != null) + stateText.SetActive(pt.used); + + if (categoryText != null) + categoryText.text = "分类:" + pt.Category; + if (nameText != null) + { + nameText.StringReference.Arguments = new object[] { LocalizationKit.LocalizeParam(pt.ClueName) }; + nameText.RefreshString(); + } + } + + public PunchTape GetPunchTapeData() + { + return punchTapeData; + } + } +} \ No newline at end of file diff --git a/Assets/Scripts/FixSystem/PunchType/PunchTapeItem.cs.meta b/Assets/Scripts/FixSystem/PunchType/PunchTapeItem.cs.meta new file mode 100644 index 000000000..6bcc67d7a --- /dev/null +++ b/Assets/Scripts/FixSystem/PunchType/PunchTapeItem.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 5d5f46e915536ee40a8c605a443e9584 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/FixSystem/PunchType/PunchTapeManager.cs b/Assets/Scripts/FixSystem/PunchType/PunchTapeManager.cs new file mode 100644 index 000000000..8479ea3e8 --- /dev/null +++ b/Assets/Scripts/FixSystem/PunchType/PunchTapeManager.cs @@ -0,0 +1,147 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using AibisDream.Kit; +using UnityEngine; +using AibisDream.Framework; + +namespace AibisDream.FixSystem +{ + /// + /// 打孔带系统入口:收藏数据、存档注册、Yarn 命令、弹窗与收藏夹面板代理。 + /// + public class PunchTapeManager : Singleton + { + public GameObject punchTapecluePrefab; + + [SerializeField] private PunchTapeFavoritesPanel favoritesPanel; + [SerializeField] private PunchTapePrinter punchTapePrinter; + public PunchTapePrinter PunchTapePrinter => punchTapePrinter; + + private readonly PunchTapeCollectionData _collectionData = new(); + + private GameObject currentPunchTapeGO; + + /// 收藏列表变更时通知 等订阅方。 + public event Action OnCollectionChanged; + + public override void OnSingletonInit() + { + StorageSystem.Instance.RegisterData(_collectionData); + _collectionData.LoadEvent += OnCollectionDataLoaded; + } + + public override void OnSingletonDestroy() + { + _collectionData.LoadEvent -= OnCollectionDataLoaded; + } + + private void OnCollectionDataLoaded(PunchTapeCollectionData data) + { + OnCollectionChanged?.Invoke(); + } + + public IReadOnlyList GetFavorites() + { + return _collectionData.punchTapeFavorites; + } + + public void AddPunchTape(PunchTape punchTape) + { + if (punchTape == null) return; + if (_collectionData.punchTapeFavorites.Contains(punchTape)) + return; + + _collectionData.punchTapeFavorites.Add(punchTape); + OnCollectionChanged?.Invoke(); + } + + public void RemovePunchTape(PunchTape punchTape) + { + if (punchTape == null) return; + if (!_collectionData.punchTapeFavorites.Remove(punchTape)) + return; + + OnCollectionChanged?.Invoke(); + } + + public void OpenFavoritesPanel() + { + if (favoritesPanel == null) + { + Debug.LogWarning("[PunchTapeManager] 未指定 _favoritesPanel,无法打开收藏夹面板。", this); + return; + } + + favoritesPanel.OpenPanel(); + } + + public void CloseFavoritesPanel() + { + if (favoritesPanel == null) + { + Debug.LogWarning("[PunchTapeManager] 未指定 _favoritesPanel,无法关闭收藏夹面板。", this); + return; + } + + favoritesPanel.ClosePanel(); + } + + public void GeneratePunchTapeWindow(string clueName, string category, string memoryIndex, + bool directlyAddToFavorites = false) + { + PunchTape punchTape = new PunchTape(category, clueName, memoryIndex); + + if (directlyAddToFavorites) + { + AddPunchTape(punchTape); + return; + } + + GameObject go = Instantiate(punchTapecluePrefab, favoritesPanel.transform); + + PunchTapeItem item = go.GetComponent(); + currentPunchTapeGO = go; + item.Setup(punchTape); + } + + /// + /// 将当前弹窗中的打孔带收入收藏。兼容旧 Yarn:<<CollectionClue ClueManager>> 会传入参数,仍应执行收藏。 + /// + public void CollectPunchTapeFromWindow() + { + if (currentPunchTapeGO == null) + { + Debug.LogWarning("PunchTapeManager: 没有当前打孔带窗口,无法收藏。"); + return; + } + + PunchTapeItem item = currentPunchTapeGO.GetComponent(); + if (item == null) + { + Debug.LogWarning("PunchTapeManager: 当前物体上缺少 PunchTapeItem。"); + return; + } + + AddPunchTape(item.GetPunchTapeData()); + Destroy(currentPunchTapeGO.gameObject); + currentPunchTapeGO = null; + } + } + + /// + /// 打孔带收藏列表的存档载体;由 注册到 。 + /// + public class PunchTapeCollectionData : IData + { + public List punchTapeFavorites = new(); + + public event Action LoadEvent; + + public IEnumerator Load() + { + LoadEvent?.Invoke(this); + yield break; + } + } +} \ No newline at end of file diff --git a/Assets/Scripts/FixSystem/PunchType/PunchTapeManager.cs.meta b/Assets/Scripts/FixSystem/PunchType/PunchTapeManager.cs.meta new file mode 100644 index 000000000..a503987f8 --- /dev/null +++ b/Assets/Scripts/FixSystem/PunchType/PunchTapeManager.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 5dd927405fcdda54fbc837069abc360d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/FixSystem/PunchType/PunchTapePrinter.cs b/Assets/Scripts/FixSystem/PunchType/PunchTapePrinter.cs new file mode 100644 index 000000000..cea184f90 --- /dev/null +++ b/Assets/Scripts/FixSystem/PunchType/PunchTapePrinter.cs @@ -0,0 +1,59 @@ +using System.Collections; +using DG.Tweening; +using UnityEngine; +using UnityEngine.UI; +using AibisDream.Kit; + +namespace AibisDream.FixSystem +{ + /// + /// 躯体维修界面中的「打孔纸带打印机」纯 UI 动画(与叙事侧 无关)。 + /// 注册到 ,由 Yarn <<PrintPunchTapes>> 经 驱动。 + /// + public class PunchTapePrinter : MonoBehaviour + { + public RectTransform punchTapeMachine; + public RectTransform punchTape; + public GameObject blackPanel; + + /// 打印机位移动画序列,可重复调用次数由 决定。 + public IEnumerator RunPrintAnimation(int tapeCount) + { + blackPanel.SetActive(true); + Vector3 tapeStartPos = punchTape.anchoredPosition; + Color tapeStartColor = punchTape.GetComponent().color; + + while (tapeCount > 0) + { + // 0-0.7秒:打印机移动到指定位置 + AudioManager.Instance.PlaySfx("event:/Scriptal/typing"); + yield return punchTapeMachine.DOAnchorPos(new Vector2(-1252f, punchTapeMachine.anchoredPosition.y), 0.7f) + .WaitForCompletion(); + + // 0.7-1秒:停顿 + yield return new WaitForSeconds(0.3f); + + // 1-1.6秒:纸带匀速弹出 + Vector2 targetPosition = new Vector2(796f, punchTape.anchoredPosition.y); + yield return punchTape.DOAnchorPos(targetPosition, 0.6f).WaitForCompletion(); + + // 1.6-2.3秒:停顿 + yield return new WaitForSeconds(0.7f); + + // 2.3-2.7秒:纸带淡出 + yield return punchTape.GetComponent().DOFade(0f, 0.4f).WaitForCompletion(); + + // 重置纸带位置和透明度 + punchTape.anchoredPosition = tapeStartPos; + punchTape.GetComponent().color = tapeStartColor; + + tapeCount--; + } + + // 打印机返回原位 + yield return punchTapeMachine.DOAnchorPos(new Vector2(-1428.186f, punchTapeMachine.anchoredPosition.y), 1f) + .WaitForCompletion(); + blackPanel.SetActive(false); + } + } +} diff --git a/Assets/Scripts/FixSystemNew/BodyModule/PunchTapeSystem.cs.meta b/Assets/Scripts/FixSystem/PunchType/PunchTapePrinter.cs.meta similarity index 100% rename from Assets/Scripts/FixSystemNew/BodyModule/PunchTapeSystem.cs.meta rename to Assets/Scripts/FixSystem/PunchType/PunchTapePrinter.cs.meta diff --git a/Assets/Scripts/FixSystem/PunchType/PunchTypeYarnCommand.cs b/Assets/Scripts/FixSystem/PunchType/PunchTypeYarnCommand.cs new file mode 100644 index 000000000..005cfc950 --- /dev/null +++ b/Assets/Scripts/FixSystem/PunchType/PunchTypeYarnCommand.cs @@ -0,0 +1,31 @@ +using System.Collections; +using AibisDream.FixSystem; +using Yarn.Unity; + +namespace AibisDream +{ + public static class PunchTypeYarnCommand + { + private static PunchTapeManager PunchTapeManager => PunchTapeManager.Instance; + private static PunchTapePrinter PunchTapePrinter => PunchTapeManager.Instance.PunchTapePrinter; + + [YarnCommand("generate_PunchTape")] + public static void GeneratePunchTapeWindow(string clueName, string category, string memoryIndex, + bool directlyAddToFavorites = false) + { + PunchTapeManager.GeneratePunchTapeWindow(clueName, category, memoryIndex, directlyAddToFavorites); + } + + [YarnCommand("CollectionClue")] + public static void CollectPunchTapeFromWindow() + { + PunchTapeManager.CollectPunchTapeFromWindow(); + } + + [YarnCommand("PrintPunchTapes")] + public static IEnumerator PrintPunchTapes(int count) + { + yield return PunchTapePrinter.RunPrintAnimation(count); + } + } +} diff --git a/Assets/Scripts/FixSystem/PunchType/PunchTypeYarnCommand.cs.meta b/Assets/Scripts/FixSystem/PunchType/PunchTypeYarnCommand.cs.meta new file mode 100644 index 000000000..7c8fb632e --- /dev/null +++ b/Assets/Scripts/FixSystem/PunchType/PunchTypeYarnCommand.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b67dac9cdf73ba3458611e5d424ac1df +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/FixSystemNew/BodyModule/BodyModuleYarnCommand.cs b/Assets/Scripts/FixSystemNew/BodyModule/BodyModuleYarnCommand.cs index 1962d1e15..ad546e3ef 100644 --- a/Assets/Scripts/FixSystemNew/BodyModule/BodyModuleYarnCommand.cs +++ b/Assets/Scripts/FixSystemNew/BodyModule/BodyModuleYarnCommand.cs @@ -1,4 +1,3 @@ -using System.Collections; using AibisDream.FixSystem; using Yarn.Unity; using UnityEngine; @@ -8,7 +7,6 @@ namespace AibisDream public static class BodyModuleYarnCommand { private static BodyModuleSystem BodyModuleSystem => FixSystemCenter.SystemDic.Get(); - private static PunchTapeSystem PunchTapeSystem => FixSystemCenter.SystemDic.Get(); private static CablePanel CablePanel => FixPanelSystem.GetRepairPanel(); [YarnCommand("enable_cut")] @@ -133,12 +131,6 @@ namespace AibisDream targetModule.Alarm(enable); } - [YarnCommand("PrintPunchTapes")] - public static IEnumerator PrintPunchTapes(int count) - { - yield return PunchTapeSystem.StartCoroutine(PunchTapeSystem.PrintPunchTapes(count)); - } - [YarnCommand("DropCable")] public static void DropCable() { diff --git a/Assets/Scripts/FixSystemNew/BodyModule/PunchTapeSystem.cs b/Assets/Scripts/FixSystemNew/BodyModule/PunchTapeSystem.cs deleted file mode 100644 index 76c39e2ec..000000000 --- a/Assets/Scripts/FixSystemNew/BodyModule/PunchTapeSystem.cs +++ /dev/null @@ -1,64 +0,0 @@ -using System.Collections; -using DG.Tweening; -using UnityEngine; -using AibisDream.FixSystem; -using UnityEngine.UI; -using AibisDream.Kit; - -public class PunchTapeSystem : MonoBehaviour -{ - public RectTransform punchTapeMachine; - public RectTransform punchTape; - - public GameObject blackPanel; - - private void Awake() - { - FixSystemCenter.SystemDic.Register(this); - } - - private void OnDestroy() - { - FixSystemCenter.SystemDic.Unregister(); - } - - public IEnumerator PrintPunchTapes(int tapeCount) - { - blackPanel.SetActive(true); - Vector3 tapeStartPos = punchTape.anchoredPosition; - Color tapeStartColor = punchTape.GetComponent().color; - - while (tapeCount > 0) - { - // 0-0.7秒:打印机移动到指定位置 - AudioManager.Instance.PlaySfx("event:/Scriptal/typing"); - yield return punchTapeMachine.DOAnchorPos(new Vector2(-1252f, punchTapeMachine.anchoredPosition.y), 0.7f) - .WaitForCompletion(); - - - // 0.7-1秒:停顿 - yield return new WaitForSeconds(0.3f); - - // 1-1.6秒:纸带匀速弹出 - Vector2 targetPosition = new Vector2(796f, punchTape.anchoredPosition.y); - yield return punchTape.DOAnchorPos(targetPosition, 0.6f).WaitForCompletion(); - - // 1.6-2.3秒:停顿 - yield return new WaitForSeconds(0.7f); - - // 2.3-2.7秒:纸带淡出 - yield return punchTape.GetComponent().DOFade(0f, 0.4f).WaitForCompletion(); - - // 重置纸带位置和透明度 - punchTape.anchoredPosition = tapeStartPos; - punchTape.GetComponent().color = tapeStartColor; - - tapeCount--; - } - - // 打印机返回原位 - yield return punchTapeMachine.DOAnchorPos(new Vector2(-1428.186f, punchTapeMachine.anchoredPosition.y), 1f) - .WaitForCompletion(); - blackPanel.SetActive(false); - } -} \ No newline at end of file