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

626 lines
24 KiB
C#

using System.Collections;
using UnityEngine;
using DG.Tweening;
using Yarn.Unity;
using AibisDream.Framework;
using AibisDream.Kit;
using UnityEngine.Rendering;
using UnityEngine.UI;
using UnityEngine.ResourceManagement.AsyncOperations;
using UnityEngine.Serialization;
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;
private const float MaxClarityDistortion = 0.58f;
private const float MaxFrequencyBlur = 0.6f;
private const float MinPixelDensity = 18f;
private const float MaxPixelDensity = 96f;
private const string DirectionalDistortionFadeProperty = "_DirectionalDistortionFade";
private static readonly int EnablePixelateId = Shader.PropertyToID("_EnablePixelate");
private static readonly int FrequencyTuningId = Shader.PropertyToID("_FrequencyTuning");
private static readonly int ClarityTuningId = Shader.PropertyToID("_ClarityTuning");
private static readonly int DirectionalDistortionFadeId = Shader.PropertyToID(DirectionalDistortionFadeProperty);
private static readonly int PixelateFadeId = Shader.PropertyToID("_PixelateFade");
private static readonly int PixelatePixelDensityId = Shader.PropertyToID("_PixelatePixelDensity");
private static readonly int EnableGaussianBlurId = Shader.PropertyToID("_EnableGaussianBlur");
private static readonly int GaussianBlurFadeId = Shader.PropertyToID("_GaussianBlurFade");
private static readonly int GaussianBlurOffsetId = Shader.PropertyToID("_GaussianBlurOffset");
private static readonly int EnableUvDistortId = Shader.PropertyToID("_EnableUVDistort");
private static readonly int UvDistortFadeId = Shader.PropertyToID("_UVDistortFade");
private static readonly int MemoryGrayscaleId = Shader.PropertyToID("_MemoryGrayscale");
private static readonly int MemoryContrastId = Shader.PropertyToID("_MemoryContrast");
[SerializeField] private GameObject memoryView;
[SerializeField] private GameObject projector;
[SerializeField] private Image memoryFinishedImage; // UI彩色记忆层
[SerializeField] private Image memoryFinishedNoColorImage; // UI去色记忆层
[FormerlySerializedAs("memoryFinishedspRender")]
[FormerlySerializedAs("memoryFinishedSpriteRenderer")]
[HideInInspector]
[SerializeField] private SpriteRenderer legacyMemoryFinishedSpriteRenderer;
[FormerlySerializedAs("memoryFinishedImageNoColor")]
[HideInInspector]
[SerializeField] private SpriteRenderer legacyMemoryFinishedImageNoColor;
[SerializeField] private Volume mainEffect;
[SerializeField] private Volume memoryClarityVolume;
[SerializeField] private Volume memoryFrequencyVolume;
[SerializeField] private Volume memoryProcessVolume;
[SerializeField] private MemorySlider frequencySlider; // 频率调节滑动条
[SerializeField] private MemorySlider claritySlider; // 清晰度调节滑动条
[SerializeField] private bool useSpriteShaderTuning = true;
[SerializeField] private CanvasGroup playBackground;
[SerializeField] private GameObject memoryGroup;
private bool isProcessing = false; // 新增变量来追踪是否正在处理记忆
private bool isPlayingFaderSound = false; // 追踪音效是否正在播放
private PunchTape _activePunchTape;
[SerializeField] private MemoryPunchTapeSlot punchTapeSlot;
[SerializeField] private Material fadeMaterial;
private Material runtimeMemoryMaterial;
private Material runtimeNoColorMemoryMaterial;
public void OpenMemoryView()
{
memoryGroup.SetActive(true);
memoryView.gameObject.SetActive(true);
}
public IEnumerator DropMemoryProjector()
{
DG.Tweening.Sequence zoomInSequence = DOTween.Sequence();
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, ProjectorMoveDuration).SetEase(Ease.OutElastic, 0.5f));
}
public void CloseMemoryView()
{
memoryView.gameObject.SetActive(false);
memoryGroup.SetActive(false);
}
public void OnEnter()
{
OpenMemoryView();
PrepareMemoryView();
}
public void OnShow()
{
Debug.Log("memoryView showed.");
PrepareMemoryView();
}
private void PrepareMemoryView()
{
EnsurePunchTapeSlotReference();
PunchTapeManager.Instance.OpenFavoritesPanel();
playBackground.gameObject.SetActive(false);
playBackground.alpha = 0;
}
public void OnExit()
{
// 停止拉条音频
if (isPlayingFaderSound)
{
AudioManager.Instance.StopSfx("event:/ActionFB/mem_fader");
isPlayingFaderSound = false;
}
PunchTapeManager.Instance.CloseFavoritesPanel();
CloseMemoryView();
// 离开视图时的逻辑
Debug.Log("MemoryView exited.");
if (frequencySlider != null)
{
frequencySlider.SetInteractable(false);
frequencySlider.LockSlider();
}
if (claritySlider != null)
{
claritySlider.SetInteractable(false);
claritySlider.LockSlider();
}
}
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);
DisableLegacyRenderTextureCameras();
if (memoryGroup != null)
memoryGroup.SetActive(false);
}
private void Start()
{
PrepareRuntimeMemoryMaterial();
frequencySlider.OnFound += CheckAllFound;
claritySlider.OnFound += CheckAllFound;
Init();
}
private void OnDestroy()
{
if (runtimeMemoryMaterial != null)
Destroy(runtimeMemoryMaterial);
if (runtimeNoColorMemoryMaterial != null)
Destroy(runtimeNoColorMemoryMaterial);
}
private void DisableLegacyRenderTextureCameras()
{
if (memoryGroup == null)
return;
var cameras = memoryGroup.GetComponentsInChildren<Camera>(true);
foreach (var renderCamera in cameras)
{
if (renderCamera != null && renderCamera.targetTexture != null)
renderCamera.enabled = false;
}
}
private void CheckAllFound()
{
if (frequencySlider.IsFound && claritySlider.IsFound)
OnSearchComplete();
}
private 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);
}
}
private void OnSearchComplete()
{
Debug.Log("Memory search completed.");
frequencySlider.SetInteractable(false);
claritySlider.SetInteractable(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());
}
public void TweenWeight(Volume postProcessingVolume, float targetWeight, float duration)
{
if (postProcessingVolume == null)
return;
// 动态调整权重
DOTween.To(() => postProcessingVolume.weight, x => postProcessingVolume.weight = x, targetWeight, duration);
}
public void Init()
{
isProcessing = false;
// 停止拉条音频
if (isPlayingFaderSound)
{
AudioManager.Instance.StopSfx("event:/ActionFB/mem_fader");
isPlayingFaderSound = false;
}
if (frequencySlider != null)
{
frequencySlider.OnTuningChanged -= ApplyFrequencyTuning;
frequencySlider.Init(memoryFrequencyVolume);
frequencySlider.SetVolumeOutputEnabled(!useSpriteShaderTuning);
frequencySlider.OnTuningChanged += ApplyFrequencyTuning;
}
if (claritySlider != null)
{
claritySlider.OnTuningChanged -= ApplyClarityTuning;
claritySlider.Init(memoryClarityVolume);
claritySlider.SetVolumeOutputEnabled(!useSpriteShaderTuning);
claritySlider.OnTuningChanged += ApplyClarityTuning;
}
ResetPresentationState();
}
/// <summary>
/// 单条记忆播放结束后的复位:不调用 <see cref="MemorySlider.Init"/>(避免再次锁条并触发打开 Timeline),
/// 仅禁用拖动;完整 <see cref="MemorySlider.LockSlider"/> 仅在退出 <see cref="MemoryCue"/> 时 <see cref="OnExit"/> 执行。
/// </summary>
private void ResetAfterMemoryFinished()
{
isProcessing = false;
if (isPlayingFaderSound)
{
AudioManager.Instance.StopSfx("event:/ActionFB/mem_fader");
isPlayingFaderSound = false;
}
if (frequencySlider != null)
frequencySlider.SetInteractable(false);
if (claritySlider != null)
claritySlider.SetInteractable(false);
ResetPresentationState();
}
private void ResetPresentationState()
{
if (fadeMaterial != null)
SetFloatIfExists(DirectionalDistortionFadeId, FadeMaterialStartValue);
ResetSpriteTuning();
SetMemoryColor(Color.black);
SetVolumeWeight(memoryProcessVolume, 0f);
_activePunchTape = null;
playBackground.gameObject.SetActive(false);
playBackground.alpha = 0;
}
[YarnCommand("MemoryFinished")]
public IEnumerator MemoryFinished()
{
yield return playBackground.DOFade(0f, DefaultFadeDuration).WaitForCompletion();
playBackground.gameObject.SetActive(false);
PunchTapeManager.Instance.OpenFavoritesPanel();
ResetAfterMemoryFinished();
EnsurePunchTapeSlotReference();
if (punchTapeSlot != null && punchTapeSlot.punchTape != null)
{
punchTapeSlot.punchTape.used = true;
punchTapeSlot.ReleaseSlot();
}
}
[YarnCommand("ColorFadeOut")]
public IEnumerator ColorFadeOut()
{
if (fadeMaterial == null) yield break;
SetFloatIfExists(DirectionalDistortionFadeId, FadeMaterialStartValue);
DG.Tweening.Sequence sequence = DOTween.Sequence();
AppendMaterialFloatTween(sequence, fadeMaterial, 0f, DirectionalDistortionFadeProperty, 3f);
if (runtimeNoColorMemoryMaterial != null && runtimeNoColorMemoryMaterial != fadeMaterial)
AppendMaterialFloatTween(sequence, runtimeNoColorMemoryMaterial, 0f, DirectionalDistortionFadeProperty, 3f);
Tween tween = sequence;
yield return tween.WaitForCompletion();
}
[YarnCommand("PlayMemory")]
public IEnumerator PlayMemory(bool useYarn = false, string memName = "")
{
SetMemoryColor(Color.white);
PunchTapeManager.Instance.CloseFavoritesPanel();
if (useYarn)
{
if (fadeMaterial != null)
SetFloatIfExists(DirectionalDistortionFadeId, FadeMaterialStartValue);
var memoryKey = string.Format(MemorySpritePath, memName);
var memoryHandle = ResourceSystem.LoadAsync<Sprite>(memoryKey);
yield return memoryHandle;
Sprite memSprite = memoryHandle.Status == AsyncOperationStatus.Succeeded ? memoryHandle.Result : null;
SetMemorySprite(memSprite);
if (memSprite == null)
{
Debug.LogError("Memory not found: " + memoryKey);
}
}
else
{
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));
}
}
public IEnumerator SearchMemory(PunchTape punchTape, bool isSkipDialogue = false)
{
if (isPlayingFaderSound)
{
AudioManager.Instance.StopSfx("event:/ActionFB/mem_fader");
isPlayingFaderSound = false;
}
frequencySlider.TargetValue = GetRandomNumber();
claritySlider.TargetValue = GetRandomNumber();
PunchTapeManager.Instance.CloseFavoritesPanel();
if (punchTape == null)
{
Debug.Log("no punchTape");
yield break;
}
_activePunchTape = punchTape;
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<Sprite>(searchMemoryKey);
yield return searchMemoryHandle;
Sprite memorySprite = searchMemoryHandle.Status == AsyncOperationStatus.Succeeded
? searchMemoryHandle.Result
: null;
SetMemorySprite(memorySprite);
if (memorySprite == null)
{
Debug.LogError("Memory not found: " + searchMemoryKey);
}
SetVolumeWeight(memoryProcessVolume, 1f);
AudioManager.Instance.PlaySfx("event:/Scriptal/mem_load");
yield return new WaitForSeconds(SearchStartDelay);
AudioManager.Instance.SetSfxParam("event:/Amb/amb_mem_tuning", "is_mem_tuning", 1);
SetMemoryColor(Color.white);
SetVolumeWeight(memoryProcessVolume, 0f);
isProcessing = true;
frequencySlider.PrepareForSearch();
claritySlider.PrepareForSearch();
bool freqUnlocked = false;
bool clarityUnlocked = false;
frequencySlider.UnlockSlider(() => freqUnlocked = true);
claritySlider.UnlockSlider(() => clarityUnlocked = true);
yield return new WaitUntil(() => freqUnlocked && clarityUnlocked);
frequencySlider.SetInteractable(true);
claritySlider.SetInteractable(true);
}
private void ApplyFrequencyTuning(float strength)
{
if (!useSpriteShaderTuning || fadeMaterial == null)
return;
bool active = strength > 0.001f;
SetFloatIfExists(FrequencyTuningId, strength);
SetFloatIfExists(EnablePixelateId, active ? 1f : 0f);
SetFloatIfExists(PixelateFadeId, strength);
SetFloatIfExists(PixelatePixelDensityId, Mathf.Lerp(MaxPixelDensity, MinPixelDensity, strength));
SetFloatIfExists(EnableGaussianBlurId, active ? 1f : 0f);
SetFloatIfExists(GaussianBlurFadeId, strength * MaxFrequencyBlur);
SetFloatIfExists(GaussianBlurOffsetId, Mathf.Lerp(0.05f, 0.35f, strength));
}
private void ApplyClarityTuning(float strength)
{
if (!useSpriteShaderTuning || fadeMaterial == null)
return;
bool active = strength > 0.001f;
SetFloatIfExists(ClarityTuningId, strength);
SetFloatIfExists(EnableUvDistortId, active ? 1f : 0f);
SetFloatIfExists(UvDistortFadeId, strength * MaxClarityDistortion);
}
private void ResetSpriteTuning()
{
if (!useSpriteShaderTuning || fadeMaterial == null)
return;
ApplyFrequencyTuning(0f);
ApplyClarityTuning(0f);
}
private void SetFloatIfExists(int propertyId, float value)
{
SetFloatIfExists(fadeMaterial, propertyId, value);
if (runtimeNoColorMemoryMaterial != null && runtimeNoColorMemoryMaterial != fadeMaterial)
SetFloatIfExists(runtimeNoColorMemoryMaterial, propertyId, value);
}
private static void SetVolumeWeight(Volume volume, float weight)
{
if (volume != null)
volume.weight = weight;
}
private static void SetFloatIfExists(Material material, int propertyId, float value)
{
if (material != null && material.HasProperty(propertyId))
material.SetFloat(propertyId, value);
}
private static void AppendMaterialFloatTween(
DG.Tweening.Sequence sequence,
Material material,
float target,
string property,
float duration)
{
if (sequence != null && material != null && material.HasProperty(property))
sequence.Join(material.DOFloat(target, property, duration));
}
private void PrepareRuntimeMemoryMaterial()
{
if (!useSpriteShaderTuning || !HasMemoryGraphic())
return;
var baseMaterial = GetColorMemoryMaterial();
if (baseMaterial == null)
baseMaterial = fadeMaterial;
var shader = baseMaterial != null ? baseMaterial.shader : null;
if (shader == null || shader.name != "AibisDream/MemoryTuningSprite")
shader = Shader.Find("AibisDream/MemoryTuningSprite");
if (shader == null)
{
Debug.LogWarning("[MemoryProcess] 未找到 AibisDream/MemoryTuningSprite,调频将回退到当前材质表现。", this);
return;
}
runtimeMemoryMaterial = CreateRuntimeMemoryMaterial(baseMaterial, shader, "Color", 0f, 1f);
SetColorMemoryMaterial(runtimeMemoryMaterial);
fadeMaterial = runtimeMemoryMaterial;
var noColorBaseMaterial = GetNoColorMemoryMaterial();
if (noColorBaseMaterial != null)
{
runtimeNoColorMemoryMaterial = CreateRuntimeMemoryMaterial(
noColorBaseMaterial,
shader,
"NoColor",
1f,
1.8f);
SetNoColorMemoryMaterial(runtimeNoColorMemoryMaterial);
}
ResetSpriteTuning();
}
private static Material CreateRuntimeMemoryMaterial(
Material source,
Shader shader,
string suffix,
float grayscale,
float contrast)
{
var material = source != null ? new Material(source) : new Material(shader);
material.shader = shader;
material.name = $"{(source != null ? source.name : shader.name)}_RuntimeMemoryTuning_{suffix}";
SetFloatIfExists(material, MemoryGrayscaleId, grayscale);
SetFloatIfExists(material, MemoryContrastId, contrast);
return material;
}
private bool HasMemoryGraphic()
{
return memoryFinishedImage != null || legacyMemoryFinishedSpriteRenderer != null;
}
private void SetMemorySprite(Sprite sprite)
{
if (memoryFinishedImage != null)
memoryFinishedImage.sprite = sprite;
if (memoryFinishedNoColorImage != null)
memoryFinishedNoColorImage.sprite = sprite;
if (legacyMemoryFinishedSpriteRenderer != null)
legacyMemoryFinishedSpriteRenderer.sprite = sprite;
if (legacyMemoryFinishedImageNoColor != null)
legacyMemoryFinishedImageNoColor.sprite = sprite;
}
private void SetMemoryColor(Color color)
{
if (memoryFinishedImage != null)
memoryFinishedImage.color = color;
if (legacyMemoryFinishedSpriteRenderer != null)
legacyMemoryFinishedSpriteRenderer.color = color;
}
private Material GetColorMemoryMaterial()
{
if (memoryFinishedImage != null)
return memoryFinishedImage.material;
return legacyMemoryFinishedSpriteRenderer != null
? legacyMemoryFinishedSpriteRenderer.sharedMaterial
: null;
}
private Material GetNoColorMemoryMaterial()
{
if (memoryFinishedNoColorImage != null)
return memoryFinishedNoColorImage.material;
return legacyMemoryFinishedImageNoColor != null
? legacyMemoryFinishedImageNoColor.sharedMaterial
: null;
}
private void SetColorMemoryMaterial(Material material)
{
if (memoryFinishedImage != null)
memoryFinishedImage.material = material;
if (legacyMemoryFinishedSpriteRenderer != null)
legacyMemoryFinishedSpriteRenderer.sharedMaterial = material;
}
private void SetNoColorMemoryMaterial(Material material)
{
if (memoryFinishedNoColorImage != null)
memoryFinishedNoColorImage.material = material;
if (legacyMemoryFinishedImageNoColor != null)
legacyMemoryFinishedImageNoColor.sharedMaterial = material;
}
public bool IsProcessing()
{
return isProcessing;
}
}
}