746 lines
28 KiB
C#
746 lines
28 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;
|
|
// ColorFadeOut 专用:从溶解刚要开始的位置起 tween,去掉 -15→-8 的空转,让整段时长都是可见的褪色。
|
|
private const float ColorFadeStartValue = -8.5f;
|
|
private const float ColorFadeDuration = 4f;
|
|
private const float DefaultFadeDuration = 1f;
|
|
private const float MemoryFinishSnowDuration = 0.1f;
|
|
private const float SearchStartDelay = 1f;
|
|
private const float ProjectorMoveDuration = 2f;
|
|
private const float MaxClarityDistortion = 1f;
|
|
private const float MaxFrequencyBlur = 0.6f;
|
|
private const float MobileMaxFrequencyBlur = 0.45f;
|
|
private const float MinPixelDensity = 18f;
|
|
private const float MobileMinPixelDensity = 24f;
|
|
private const float MaxPixelDensity = 96f;
|
|
private const string DirectionalDistortionFadeProperty = "_DirectionalDistortionFade";
|
|
|
|
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 GaussianBlurFadeId = Shader.PropertyToID("_GaussianBlurFade");
|
|
private static readonly int GaussianBlurOffsetId = Shader.PropertyToID("_GaussianBlurOffset");
|
|
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 Graphic standbyOverlay;
|
|
[SerializeField, Range(0f, 1f)] private float standbyOverlayMinAlpha = 0.04f;
|
|
[SerializeField, Range(0f, 1f)] private float standbyOverlayMaxAlpha = 0.12f;
|
|
[SerializeField, Range(0f, 1f)] private float searchOverlayAlpha = 0.85f;
|
|
[SerializeField] private float standbyOverlayPulseDuration = 0.8f;
|
|
[SerializeField] private float searchTargetMinDistanceFromStart = 2.5f;
|
|
|
|
[SerializeField] private GameObject memoryGroup;
|
|
|
|
private bool isProcessing = false; // 新增变量来追踪是否正在处理记忆
|
|
private bool isPlayingFaderSound = false; // 追踪音效是否正在播放
|
|
|
|
private PunchTape _activePunchTape;
|
|
|
|
[SerializeField] private MemoryPunchTapeSlot punchTapeSlot;
|
|
|
|
[SerializeField] private Material fadeMaterial;
|
|
[SerializeField] private Shader memoryTuningShader;
|
|
private Material runtimeMemoryMaterial;
|
|
private Material runtimeNoColorMemoryMaterial;
|
|
private bool spriteShaderTuningAvailable;
|
|
private Tween overlayTween;
|
|
|
|
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()
|
|
{
|
|
StopStandbyOverlay();
|
|
SetOverlayAlpha(0f);
|
|
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();
|
|
SetPlayBackgroundVisible(false, 0f);
|
|
StartStandbyOverlay();
|
|
}
|
|
|
|
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 SetPlayBackgroundVisible(bool active, float alpha)
|
|
{
|
|
if (playBackground == null)
|
|
{
|
|
Debug.LogWarning("[MemoryProcess] playBackground 未配置。", this);
|
|
return;
|
|
}
|
|
|
|
playBackground.gameObject.SetActive(active);
|
|
playBackground.alpha = alpha;
|
|
}
|
|
|
|
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()
|
|
{
|
|
overlayTween?.Kill();
|
|
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 GetRandomSearchTarget(MemorySlider slider)
|
|
{
|
|
float start = slider != null ? slider.StartValue : 0f;
|
|
float max = slider != null ? slider.MaxValue : 6f;
|
|
float minDistance = Mathf.Max(0f, searchTargetMinDistanceFromStart);
|
|
float nearBandMin = start + minDistance;
|
|
float nearBandMax = start + (max - start) * 0.55f;
|
|
float farBandMin = max - 1.5f;
|
|
float farBandMax = max - 0.5f;
|
|
|
|
if (nearBandMin >= nearBandMax)
|
|
return UnityEngine.Random.Range(farBandMin, farBandMax);
|
|
|
|
if (UnityEngine.Random.value < 0.5f)
|
|
return UnityEngine.Random.Range(nearBandMin, nearBandMax);
|
|
|
|
return UnityEngine.Random.Range(farBandMin, farBandMax);
|
|
}
|
|
|
|
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(!spriteShaderTuningAvailable);
|
|
frequencySlider.OnTuningChanged += ApplyFrequencyTuning;
|
|
}
|
|
if (claritySlider != null)
|
|
{
|
|
claritySlider.OnTuningChanged -= ApplyClarityTuning;
|
|
claritySlider.Init(memoryClarityVolume);
|
|
claritySlider.SetVolumeOutputEnabled(!spriteShaderTuningAvailable);
|
|
claritySlider.OnTuningChanged += ApplyClarityTuning;
|
|
}
|
|
|
|
ResetPresentationState();
|
|
}
|
|
|
|
/// <summary>
|
|
/// 复位拉杆与调谐输出:不调用 <see cref="MemorySlider.Init"/> / <see cref="MemorySlider.LockSlider"/>
|
|
/// (避免收起 Timeline),仅禁用拖动并把拉杆视觉回到默认位置。
|
|
/// 应在黑幕淡出前调用,避免玩家看到拉杆跳回初始态。
|
|
/// </summary>
|
|
private void ResetSlidersAndTuning()
|
|
{
|
|
if (isPlayingFaderSound)
|
|
{
|
|
AudioManager.Instance.StopSfx("event:/ActionFB/mem_fader");
|
|
isPlayingFaderSound = false;
|
|
}
|
|
|
|
AudioManager.Instance.SetSfxParam("event:/Amb/amb_mem_tuning", "is_mem_tuning", 0);
|
|
|
|
if (frequencySlider != null)
|
|
{
|
|
frequencySlider.SetInteractable(false);
|
|
frequencySlider.ResetVisualToDefault();
|
|
}
|
|
if (claritySlider != null)
|
|
{
|
|
claritySlider.SetInteractable(false);
|
|
claritySlider.ResetVisualToDefault();
|
|
}
|
|
|
|
ResetSpriteTuning();
|
|
}
|
|
|
|
/// <summary>
|
|
/// 单条记忆播放结束后的完整复位;完整 <see cref="MemorySlider.LockSlider"/> 仅在退出记忆模块时 <see cref="OnExit"/> 执行。
|
|
/// </summary>
|
|
private void ResetAfterMemoryFinished()
|
|
{
|
|
isProcessing = false;
|
|
ResetSlidersAndTuning();
|
|
ResetPresentationState();
|
|
}
|
|
|
|
private void ResetPresentationState()
|
|
{
|
|
if (fadeMaterial != null)
|
|
SetFloatIfExists(DirectionalDistortionFadeId, FadeMaterialStartValue);
|
|
ResetSpriteTuning();
|
|
SetMemoryColor(Color.black);
|
|
SetVolumeWeight(memoryProcessVolume, 0f);
|
|
_activePunchTape = null;
|
|
StartStandbyOverlay();
|
|
|
|
SetPlayBackgroundVisible(false, 0f);
|
|
}
|
|
|
|
[YarnCommand("MemoryFinished")]
|
|
public IEnumerator MemoryFinished()
|
|
{
|
|
isProcessing = false;
|
|
ResetSlidersAndTuning();
|
|
|
|
// 短闪雪花再淡出黑幕,避免记忆画面硬切
|
|
SetMemoryColor(Color.black);
|
|
ShowSearchOverlay();
|
|
yield return new WaitForSeconds(MemoryFinishSnowDuration);
|
|
|
|
if (playBackground != null)
|
|
{
|
|
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, ColorFadeStartValue);
|
|
DG.Tweening.Sequence sequence = DOTween.Sequence();
|
|
AppendMaterialFloatTween(sequence, fadeMaterial, 0f, DirectionalDistortionFadeProperty, ColorFadeDuration);
|
|
if (runtimeNoColorMemoryMaterial != null && runtimeNoColorMemoryMaterial != fadeMaterial)
|
|
AppendMaterialFloatTween(sequence, runtimeNoColorMemoryMaterial, 0f, DirectionalDistortionFadeProperty, ColorFadeDuration);
|
|
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;
|
|
}
|
|
}
|
|
|
|
SetPlayBackgroundVisible(true, 0f);
|
|
AudioManager.Instance.PlaySfx("event:/ActionFB/mem_play");
|
|
if (playBackground != null)
|
|
yield return playBackground.DOFade(1f, DefaultFadeDuration).WaitForCompletion();
|
|
|
|
if (!useYarn)
|
|
{
|
|
if (!PunchTapeManager.Instance.TryGetDefinition(punchTapeSlot.punchTape, out var definition))
|
|
yield break;
|
|
|
|
DialogController.Instance.StartDialogNode(definition.DialogNode);
|
|
}
|
|
}
|
|
|
|
public IEnumerator SearchMemory(PunchTape punchTape, bool isSkipDialogue = false)
|
|
{
|
|
if (isPlayingFaderSound)
|
|
{
|
|
AudioManager.Instance.StopSfx("event:/ActionFB/mem_fader");
|
|
isPlayingFaderSound = false;
|
|
}
|
|
|
|
frequencySlider.TargetValue = GetRandomSearchTarget(frequencySlider);
|
|
claritySlider.TargetValue = GetRandomSearchTarget(claritySlider);
|
|
PunchTapeManager.Instance.CloseFavoritesPanel();
|
|
|
|
if (punchTape == null)
|
|
{
|
|
Debug.Log("no punchTape");
|
|
StartStandbyOverlay();
|
|
yield break;
|
|
}
|
|
|
|
_activePunchTape = punchTape;
|
|
|
|
if (!PunchTapeManager.Instance.TryGetDefinition(_activePunchTape, out var definition))
|
|
{
|
|
StartStandbyOverlay();
|
|
yield break;
|
|
}
|
|
|
|
if (isProcessing)
|
|
{
|
|
Debug.LogWarning("Already processing a memory. Please wait until the process is finished.");
|
|
yield break;
|
|
}
|
|
|
|
StopStandbyOverlay();
|
|
ShowSearchOverlay();
|
|
|
|
var searchMemoryKey = string.Format(MemorySpritePath, definition.MemoryResourceKey);
|
|
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);
|
|
}
|
|
|
|
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);
|
|
isProcessing = true;
|
|
frequencySlider.PrepareForSearch();
|
|
claritySlider.PrepareForSearch();
|
|
ClearSearchOverlay();
|
|
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 (!spriteShaderTuningAvailable || fadeMaterial == null)
|
|
return;
|
|
|
|
float blurCap = Application.isMobilePlatform ? MobileMaxFrequencyBlur : MaxFrequencyBlur;
|
|
float minPixelDensity = Application.isMobilePlatform ? MobileMinPixelDensity : MinPixelDensity;
|
|
|
|
SetFloatIfExists(FrequencyTuningId, strength);
|
|
SetFloatIfExists(PixelateFadeId, strength);
|
|
SetFloatIfExists(PixelatePixelDensityId, Mathf.Lerp(MaxPixelDensity, minPixelDensity, strength));
|
|
SetFloatIfExists(GaussianBlurFadeId, strength * blurCap);
|
|
SetFloatIfExists(GaussianBlurOffsetId, Mathf.Lerp(0.05f, 0.35f, strength));
|
|
}
|
|
|
|
private void ApplyClarityTuning(float strength)
|
|
{
|
|
if (!spriteShaderTuningAvailable || fadeMaterial == null)
|
|
return;
|
|
|
|
SetFloatIfExists(ClarityTuningId, strength);
|
|
SetFloatIfExists(UvDistortFadeId, strength * MaxClarityDistortion);
|
|
}
|
|
|
|
private void ResetSpriteTuning()
|
|
{
|
|
if (!spriteShaderTuningAvailable || 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()
|
|
{
|
|
spriteShaderTuningAvailable = false;
|
|
if (!useSpriteShaderTuning || !HasMemoryGraphic())
|
|
return;
|
|
|
|
var baseMaterial = GetColorMemoryMaterial();
|
|
if (baseMaterial == null)
|
|
baseMaterial = fadeMaterial;
|
|
|
|
var shader = memoryTuningShader;
|
|
if (shader == null && baseMaterial != null)
|
|
shader = baseMaterial.shader;
|
|
if (shader == null || shader.name != "AibisDream/MemoryTuningSprite")
|
|
shader = Shader.Find("AibisDream/MemoryTuningSprite");
|
|
if (shader == null)
|
|
{
|
|
Debug.LogWarning("[MemoryProcess] 未找到 AibisDream/MemoryTuningSprite,调频将回退到 Volume 后处理。", this);
|
|
return;
|
|
}
|
|
|
|
if (!shader.isSupported)
|
|
{
|
|
Debug.LogWarning("[MemoryProcess] MemoryTuningSprite 在当前平台不受支持,调频将回退到 Volume 后处理。", 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);
|
|
}
|
|
|
|
spriteShaderTuningAvailable = true;
|
|
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 StartStandbyOverlay()
|
|
{
|
|
if (standbyOverlay == null || isProcessing)
|
|
return;
|
|
|
|
overlayTween?.Kill();
|
|
SetOverlayAlpha(standbyOverlayMinAlpha);
|
|
overlayTween = standbyOverlay
|
|
.DOFade(standbyOverlayMaxAlpha, Mathf.Max(0.01f, standbyOverlayPulseDuration))
|
|
.SetLoops(-1, LoopType.Yoyo)
|
|
.SetEase(Ease.InOutSine);
|
|
}
|
|
|
|
private void StopStandbyOverlay()
|
|
{
|
|
overlayTween?.Kill();
|
|
overlayTween = null;
|
|
}
|
|
|
|
private void ShowSearchOverlay()
|
|
{
|
|
StopStandbyOverlay();
|
|
SetOverlayAlpha(searchOverlayAlpha);
|
|
}
|
|
|
|
private void ClearSearchOverlay()
|
|
{
|
|
StopStandbyOverlay();
|
|
SetOverlayAlpha(0f);
|
|
}
|
|
|
|
private void SetOverlayAlpha(float alpha)
|
|
{
|
|
if (standbyOverlay == null)
|
|
return;
|
|
|
|
Color color = standbyOverlay.color;
|
|
color.a = Mathf.Clamp01(alpha);
|
|
standbyOverlay.color = color;
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
}
|