Files
2026-07-30 23:15:58 +08:00

376 lines
12 KiB
C#

using SmoothShakeFree;
using System.Collections;
using UnityEngine;
using UnityEngine.UI;
public class globalNoteEffect : MonoBehaviour
{
public static globalNoteEffect Instance { get; private set; }
[Header("Camera Shake Event")]
public float cameraShakeAmplitude = 0.12f;
[Header("Flash Blink Event")]
public Image flashBlinkImage;
public float flashBlinkFadeDuration = 0.2f;
[Header("Camera Dash Event")]
public float targetCameraZPosition = -6.5f;
public AnimationCurve cameraDashCurve = AnimationCurve.EaseInOut(0f, 0f, 1f, 1f);
public float cameraDashDuration = 0.12f;
[Header("Camera Wave FX")]
[Range(0f, 1f)]
public float cameraWaveIntensity = 1f;
public float cameraWaveEnterDuration = 0.12f;
public float cameraWaveRecoverDuration = 0.12f;
[Header("Camera Glitch FX")]
public float cameraGlitchEnterDuration = 0.08f;
public float cameraGlitchRecoverDuration = 0.12f;
[Header("Camera Monochrome FX")]
public float cameraMonochromeEnterDuration = 0.1f;
public float cameraMonochromeRecoverDuration = 0.14f;
[Header("Screenshot Scale FX")]
public float screenshotScaleStartOpacity = 0.5f;
public float screenshotScaleFadeDuration = 0.22f;
public float screenshotScaleEndScale = 1.12f;
private Transform cachedCameraTransform;
private Coroutine flashBlinkCoroutine;
private Coroutine cameraDashCoroutine;
private float cameraBaseLocalZ;
private bool hasCameraBaseLocalZ;
private void Awake()
{
if (Instance == null)
{
Instance = this;
}
else if (Instance != this)
{
Destroy(gameObject);
return;
}
CacheCameraTransform();
PrewarmRuntime();
}
private void Start()
{
SetFlashImageAlpha(0f);
}
private void OnEnable()
{
CacheCameraTransform();
SetFlashImageAlpha(0f);
PrewarmRuntime();
}
private void LateUpdate()
{
if (cachedCameraTransform == null)
{
CacheCameraTransform();
}
if (cachedCameraTransform != null && cameraDashCoroutine == null)
{
cameraBaseLocalZ = cachedCameraTransform.localPosition.z;
hasCameraBaseLocalZ = true;
}
}
private void CacheCameraTransform()
{
if (Camera.main != null)
{
cachedCameraTransform = Camera.main.transform;
cameraBaseLocalZ = cachedCameraTransform.localPosition.z;
hasCameraBaseLocalZ = true;
}
}
public void PrewarmRuntime()
{
CacheCameraTransform();
if (cachedCameraTransform != null)
{
SmoothShake shake = cachedCameraTransform.GetComponent<SmoothShake>();
if (shake == null)
{
shake = cachedCameraTransform.gameObject.AddComponent<SmoothShake>();
}
if (shake.positionShake == null) shake.positionShake = new Shaker();
if (shake.rotationShake == null) shake.rotationShake = new Shaker();
if (shake.shakers == null || shake.shakers.Length != 2)
shake.shakers = new[] { shake.positionShake, shake.rotationShake };
if (shake.sum == null || shake.sum.Length != 2)
shake.sum = new Vector3[2];
}
GlobalDistortionRuntimeController.EnsureInstance();
GlobalGlitchRuntimeController.EnsureInstance();
GlobalMonochromeRuntimeController.EnsureInstance();
GlobalAfterimageRuntimeController.EnsureInstance();
}
public static void TryTrigger(string noteFunction)
{
if (string.IsNullOrWhiteSpace(noteFunction))
return;
globalNoteEffect controller = GetOrCreateInstance();
if (controller == null)
return;
controller.TriggerInternal(noteFunction.Trim());
}
private static globalNoteEffect GetOrCreateInstance()
{
if (Instance != null)
return Instance;
globalNoteEffect found = SceneObjectLookupCache.FindAny<globalNoteEffect>();
if (found != null)
{
Instance = found;
return found;
}
GameObject runtimeObject = new GameObject("__global_note_effect_runtime");
return runtimeObject.AddComponent<globalNoteEffect>();
}
private void TriggerInternal(string noteFunction)
{
switch (noteFunction)
{
case "CameraShakeEvent":
TriggerCameraShake();
break;
case "FlashBlinkEvent":
TriggerFlashBlink();
break;
case "CameraScaleEvent":
TriggerCameraDash();
break;
case "CameraWaveFX":
TriggerCameraWaveFx();
break;
case "CameraGlitchFX":
TriggerCameraGlitchFx();
break;
case "CameraMonochromeFX":
TriggerCameraMonochromeFx();
break;
case "ScreenshotScale":
TriggerScreenshotScaleFx();
break;
}
}
private void TriggerCameraShake()
{
CacheCameraTransform();
if (cachedCameraTransform == null)
return;
float amplitudeValue = Mathf.Max(0f, cameraShakeAmplitude);
if (amplitudeValue <= 0f)
return;
SmoothShake shake = cachedCameraTransform.GetComponent<SmoothShake>();
if (shake == null)
{
shake = cachedCameraTransform.gameObject.AddComponent<SmoothShake>();
}
if (shake.positionShake == null) shake.positionShake = new Shaker();
if (shake.rotationShake == null) shake.rotationShake = new Shaker();
shake.positionShake.noiseType = Shaker.NoiseType.SineWave;
shake.positionShake.amplitude = new Vector3(amplitudeValue, amplitudeValue, 0f);
shake.positionShake.frequency = new Vector3(18f, 18f, 0f);
shake.rotationShake.amplitude = Vector3.zero;
shake.rotationShake.frequency = Vector3.zero;
shake.timeSettings.constantShake = false;
shake.timeSettings.fadeInDuration = 0.02f;
shake.timeSettings.holdDuration = 0.04f;
shake.timeSettings.fadeOutDuration = 0.05f;
if (shake.timeSettings.fadeInCurve == null || shake.timeSettings.fadeInCurve.length == 0)
shake.timeSettings.fadeInCurve = AnimationCurve.Linear(0f, 0f, 1f, 1f);
if (shake.timeSettings.fadeOutCurve == null || shake.timeSettings.fadeOutCurve.length == 0)
shake.timeSettings.fadeOutCurve = AnimationCurve.Linear(0f, 1f, 1f, 0f);
// 复用内部数组,仅在缺失/尺寸不符时重建,避免每次(近乎每音符)相机震动都分配两个数组。
if (shake.shakers == null || shake.shakers.Length != 2)
shake.shakers = new[] { shake.positionShake, shake.rotationShake };
if (shake.sum == null || shake.sum.Length != 2)
shake.sum = new Vector3[2];
try
{
shake.StartShake();
}
catch (System.Exception ex)
{
Debug.LogWarning($"[globalNoteEffect] Camera shake failed (check shake asset references in Inspector): {ex.Message}");
}
}
private void TriggerFlashBlink()
{
if (flashBlinkImage == null)
return;
if (flashBlinkCoroutine != null)
{
StopCoroutine(flashBlinkCoroutine);
}
flashBlinkCoroutine = StartCoroutine(FlashBlinkCoroutine());
}
private IEnumerator FlashBlinkCoroutine()
{
SetFlashImageAlpha(1f);
float duration = Mathf.Max(0.01f, flashBlinkFadeDuration);
float elapsed = 0f;
while (elapsed < duration)
{
elapsed += Time.deltaTime;
float t = Mathf.Clamp01(elapsed / duration);
SetFlashImageAlpha(1f - t);
yield return null;
}
SetFlashImageAlpha(0f);
flashBlinkCoroutine = null;
}
private void TriggerCameraDash()
{
CacheCameraTransform();
if (cachedCameraTransform == null)
return;
if (cameraDashCoroutine != null)
{
StopCoroutine(cameraDashCoroutine);
if (hasCameraBaseLocalZ)
{
Vector3 resetPosition = cachedCameraTransform.localPosition;
resetPosition.z = cameraBaseLocalZ;
cachedCameraTransform.localPosition = resetPosition;
}
}
cameraDashCoroutine = StartCoroutine(CameraDashCoroutine());
}
private IEnumerator CameraDashCoroutine()
{
if (!hasCameraBaseLocalZ)
{
cameraBaseLocalZ = cachedCameraTransform.localPosition.z;
hasCameraBaseLocalZ = true;
}
float baseLocalZ = cameraBaseLocalZ;
float targetZ = targetCameraZPosition;
if (Mathf.Approximately(targetZ, baseLocalZ))
{
targetZ = baseLocalZ + 1.5f;
}
float duration = Mathf.Max(0.01f, cameraDashDuration);
AnimationCurve curve = cameraDashCurve == null || cameraDashCurve.length == 0
? AnimationCurve.EaseInOut(0f, 0f, 1f, 1f)
: cameraDashCurve;
float elapsed = 0f;
while (elapsed < duration)
{
elapsed += Time.deltaTime;
float t = Mathf.Clamp01(elapsed / duration);
float curvedT = curve.Evaluate(t);
Vector3 position = cachedCameraTransform.localPosition;
position.z = Mathf.LerpUnclamped(baseLocalZ, targetZ, curvedT);
cachedCameraTransform.localPosition = position;
yield return null;
}
elapsed = 0f;
while (elapsed < duration)
{
elapsed += Time.deltaTime;
float t = Mathf.Clamp01(elapsed / duration);
float curvedT = curve.Evaluate(t);
Vector3 position = cachedCameraTransform.localPosition;
position.z = Mathf.LerpUnclamped(targetZ, baseLocalZ, curvedT);
cachedCameraTransform.localPosition = position;
yield return null;
}
Vector3 finalPosition = cachedCameraTransform.localPosition;
finalPosition.z = baseLocalZ;
cachedCameraTransform.localPosition = finalPosition;
cameraDashCoroutine = null;
}
private void TriggerCameraWaveFx()
{
GlobalDistortionRuntimeController.TriggerWaveEffect(
Mathf.Clamp01(cameraWaveIntensity),
Mathf.Max(0.0001f, cameraWaveEnterDuration),
Mathf.Max(0.0001f, cameraWaveRecoverDuration)
);
}
private void TriggerCameraGlitchFx()
{
GlobalGlitchRuntimeController.TriggerWaveEffect(
Mathf.Max(0.0001f, cameraGlitchEnterDuration),
Mathf.Max(0.0001f, cameraGlitchRecoverDuration)
);
}
private void TriggerCameraMonochromeFx()
{
GlobalMonochromeRuntimeController.Trigger(
Mathf.Max(0.0001f, cameraMonochromeEnterDuration),
Mathf.Max(0.0001f, cameraMonochromeRecoverDuration)
);
}
private void TriggerScreenshotScaleFx()
{
GlobalAfterimageRuntimeController.Trigger(
Mathf.Clamp01(screenshotScaleStartOpacity),
Mathf.Max(0.0001f, screenshotScaleFadeDuration),
Mathf.Max(1f, screenshotScaleEndScale)
);
}
private void SetFlashImageAlpha(float alpha)
{
if (flashBlinkImage == null)
return;
Color color = flashBlinkImage.color;
color.a = Mathf.Clamp01(alpha);
flashBlinkImage.color = color;
}
}