feat(peipei): 支持 Eye/UF 演出倍速与音效 pitch

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-07-26 23:44:00 +08:00
co-authored by Cursor
parent 9dab57ab87
commit add9891934
5 changed files with 106 additions and 41 deletions
@@ -388,7 +388,7 @@ namespace AibisDream.Kit
}
}
public void PlaySfx(string eventName, string audioKey = null)
public void PlaySfx(string eventName, string audioKey = null, float pitch = 1f)
{
var eventPath = FormatEventPath(eventName);
var key = string.IsNullOrEmpty(audioKey) ? eventName : audioKey;
@@ -405,6 +405,11 @@ namespace AibisDream.Kit
AddSfx(key, newSfx);
}
if (!Mathf.Approximately(pitch, 1f))
{
newSfx.setPitch(Mathf.Max(0.01f, pitch));
}
newSfx.start();
}
+42 -17
View File
@@ -50,7 +50,17 @@ namespace AibisDream
public EyeColorState GetCurrentColorState() => _currentState;
public float GetEffectTransitionDuration() => visualSettings.GetDurationForState(_currentState);
/// <summary>Eye/UF 演出倍速(默认见 Visual Tuning → Presentation Speed)。</summary>
public float PresentationSpeed => Mathf.Max(0.01f, visualSettings != null ? visualSettings.presentationSpeed : 1f);
public float ScaleDuration(float duration) => duration / PresentationSpeed;
public float GetEffectTransitionDuration() => ScaleDuration(visualSettings.GetDurationForState(_currentState));
private void PlayPresentationSfx(string eventName)
{
AudioManager.Instance.PlaySfx(eventName, pitch: PresentationSpeed);
}
/// <summary>仅「可想象色彩」上色时眨眼;无 autoBlink / 失焦眨眼。</summary>
private bool ShouldBlinkForImagine()
@@ -85,7 +95,8 @@ namespace AibisDream
visualSettings,
() => eyeStateEffect?.ApplyFocusedEffect(target, this),
visualSettings.imagineEffectAtBlinkOpen,
visualSettings.imagineBlinkGap);
visualSettings.imagineBlinkGap,
PresentationSpeed);
}
else
{
@@ -97,6 +108,7 @@ namespace AibisDream
private IEnumerator RunBlurOut(EyeTarget target, float duration)
{
duration = ScaleDuration(duration);
if (target != null)
{
target.BlurOut(duration);
@@ -279,12 +291,12 @@ namespace AibisDream
if (duration > 0.01f)
{
AudioManager.Instance.PlaySfx("event:/ActionFB/visualturn");
PlayPresentationSfx("event:/ActionFB/visualturn");
}
mouseFollowAndZoom.MoveFocusTo(
focusTransform,
duration,
ScaleDuration(duration),
focusMinBounds,
focusMaxBounds,
GetFocusViewportSize());
@@ -404,7 +416,10 @@ namespace AibisDream
[YarnCommand("EyeBlink")]
public void EyeBlink(float duration = 0f)
{
EnsureViewportOverlay()?.PlayBlink(duration > 0f ? duration : null, restartAutoBlinkAfter: true);
EnsureViewportOverlay()?.PlayBlink(
duration > 0f ? duration : null,
restartAutoBlinkAfter: true,
presentationSpeed: PresentationSpeed);
}
[YarnCommand("set_Eyetarget")]
@@ -444,9 +459,10 @@ namespace AibisDream
}
const float focusMoveDuration = 1f;
float scaledFocusMove = ScaleDuration(focusMoveDuration);
FocusOnTarget(target, focusMoveDuration);
yield return WaitForFocusMovement(focusMoveDuration);
yield return WaitForFocusMovement(scaledFocusMove);
currentTarget = target;
mouseFollowAndZoom.SetIndicatorTarget(currentTarget);
@@ -483,7 +499,7 @@ namespace AibisDream
yield break;
}
AudioManager.Instance.PlaySfx("event:/ActionFB/color_imagine");
PlayPresentationSfx("event:/ActionFB/color_imagine");
yield return RunFocusedEffectWithPreBlink(currentTarget);
}
@@ -501,7 +517,7 @@ namespace AibisDream
yield break;
}
AudioManager.Instance.PlaySfx("event:/ActionFB/color_imagine");
PlayPresentationSfx("event:/ActionFB/color_imagine");
yield return RunUnfocusedEffect(currentTarget);
}
@@ -550,6 +566,7 @@ namespace AibisDream
public IEnumerator Set_Saturation(float targetSaturation, float duration)
{
targetSaturation = Mathf.Clamp01(targetSaturation);
duration = ScaleDuration(duration);
Sequence saturationSequence = DOTween.Sequence();
if (!TryJoinTargetSaturation(saturationSequence, targetSaturation, duration))
@@ -577,13 +594,14 @@ namespace AibisDream
Debug.LogWarning("[EyeSystem] 无可用 EyeTarget Glitch tween。", this);
}
yield return new WaitForSeconds(visualSettings.glitchInDuration);
yield return new WaitForSeconds(ScaleDuration(visualSettings.glitchInDuration));
}
[YarnCommand("EyeGlitch_out")]
public void EyeGlitch_out(float duration)
{
TryStopUnifiedGlitch(duration > 0f ? duration : visualSettings.glitchOutDuration);
float resolved = duration > 0f ? duration : visualSettings.glitchOutDuration;
TryStopUnifiedGlitch(ScaleDuration(resolved));
}
[YarnCommand("set_EyeColor")]
@@ -614,11 +632,17 @@ namespace AibisDream
memoryRender.sprite = memoryHandle.Result;
memoryRender.gameObject.SetActive(true);
float fadeIn = ScaleDuration(1.5f);
float hold = ScaleDuration(1.5f);
float squeeze = ScaleDuration(2f);
float fadeOut = ScaleDuration(0.5f);
float fadeOutAt = fadeIn + hold + squeeze;
Sequence memorySequence = DOTween.Sequence();
memorySequence.Append(memoryMaterial.DOFloat(0.85f, "_FullDistortionFade", 1.5f));
memorySequence.AppendInterval(1.5f);
memorySequence.Append(memoryMaterial.DOFloat(0f, "_SqueezePower", 2f));
memorySequence.Insert(4.5f, memoryMaterial.DOFloat(0f, "_FullDistortionFade", 0.5f).OnComplete(() =>
memorySequence.Append(memoryMaterial.DOFloat(0.85f, "_FullDistortionFade", fadeIn));
memorySequence.AppendInterval(hold);
memorySequence.Append(memoryMaterial.DOFloat(0f, "_SqueezePower", squeeze));
memorySequence.Insert(fadeOutAt, memoryMaterial.DOFloat(0f, "_FullDistortionFade", fadeOut).OnComplete(() =>
{
memoryRender.gameObject.SetActive(false);
}));
@@ -628,7 +652,7 @@ namespace AibisDream
Sequence sequence = DOTween.Sequence();
Sequence waveIn = CreateTargetRoundWaveSequence(1f, visualSettings.roundWaveInDuration);
Sequence waveIn = CreateTargetRoundWaveSequence(1f, ScaleDuration(visualSettings.roundWaveInDuration));
if (waveIn != null)
{
sequence.Append(waveIn);
@@ -636,7 +660,7 @@ namespace AibisDream
sequence.AppendCallback(() => { EyeGlitch_out(visualSettings.glitchOutDuration); });
Sequence waveOut = CreateTargetRoundWaveSequence(0f, visualSettings.roundWaveOutDuration);
Sequence waveOut = CreateTargetRoundWaveSequence(0f, ScaleDuration(visualSettings.roundWaveOutDuration));
if (waveOut != null)
{
sequence.Append(waveOut);
@@ -666,6 +690,7 @@ namespace AibisDream
public Tween TweenUnifiedUfWeights(float ascii, float dream, float memory, float duration)
{
duration = ScaleDuration(duration);
Sequence sequence = DOTween.Sequence();
bool hasTween = false;
@@ -710,7 +735,7 @@ namespace AibisDream
foreach (EyeTarget target in targets)
{
Sequence targetGlitch = target.StartUnifiedGlitch(visualSettings.glitchInDuration);
Sequence targetGlitch = target.StartUnifiedGlitch(ScaleDuration(visualSettings.glitchInDuration));
if (targetGlitch == null)
{
continue;
@@ -180,7 +180,10 @@ namespace AibisDream
}
}
public Tween PlayBlink(float? totalDuration = null, bool restartAutoBlinkAfter = false)
public Tween PlayBlink(
float? totalDuration = null,
bool restartAutoBlinkAfter = false,
float presentationSpeed = 1f)
{
if (instanceMaterial == null || activeSettings == null)
{
@@ -189,11 +192,12 @@ namespace AibisDream
blinkTween?.Kill();
float closeDuration = activeSettings.blinkCloseDuration;
float openDuration = activeSettings.blinkOpenDuration;
float durationScale = 1f / Mathf.Max(0.01f, presentationSpeed);
float closeDuration = activeSettings.blinkCloseDuration * durationScale;
float openDuration = activeSettings.blinkOpenDuration * durationScale;
if (totalDuration.HasValue)
{
float total = Mathf.Max(0.05f, totalDuration.Value);
float total = Mathf.Max(0.05f, totalDuration.Value) * durationScale;
closeDuration = total * 0.42f;
openDuration = total * 0.58f;
}
@@ -221,7 +225,8 @@ namespace AibisDream
EyeVisualSettings settings,
System.Action onEffectStart,
float effectAtOpenRatio,
float betweenBlinkGap = 0f)
float betweenBlinkGap = 0f,
float presentationSpeed = 1f)
{
if (!ShouldPlayTransitionBlink(settings))
{
@@ -234,11 +239,12 @@ namespace AibisDream
bool effectStarted = false;
blinkCount = Mathf.Max(1, blinkCount);
effectAtOpenRatio = Mathf.Clamp01(effectAtOpenRatio);
float durationScale = 1f / Mathf.Max(0.01f, presentationSpeed);
for (int i = 0; i < blinkCount; i++)
{
bool isLastBlink = i == blinkCount - 1;
Tween blink = PlayBlink();
Tween blink = PlayBlink(presentationSpeed: presentationSpeed);
if (blink == null)
{
if (isLastBlink && !effectStarted)
@@ -252,7 +258,8 @@ namespace AibisDream
if (isLastBlink)
{
float effectDelay = settings.blinkCloseDuration + settings.blinkOpenDuration * effectAtOpenRatio;
float effectDelay = (settings.blinkCloseDuration + settings.blinkOpenDuration * effectAtOpenRatio)
* durationScale;
yield return new WaitForSeconds(effectDelay);
if (!effectStarted)
{
@@ -267,7 +274,7 @@ namespace AibisDream
yield return blink.WaitForCompletion();
if (betweenBlinkGap > 0f)
{
yield return new WaitForSeconds(betweenBlinkGap);
yield return new WaitForSeconds(betweenBlinkGap * durationScale);
}
}
}
@@ -16,6 +16,10 @@ namespace AibisDream
[Tooltip("Focused / imagined color state.")]
[Range(0f, 1f)] public float colorRevealMax = 1f;
[Header("Presentation Speed")]
[Tooltip("Eye/UF 演出整体倍速(等待、Tween、眨眼、关联 SFX pitch)。2.5 = 快 2.5 倍。")]
[Min(0.01f)] public float presentationSpeed = 2.5f;
[Header("Transition Duration (seconds)")]
public float imagineColorDuration = 1f;
public float cannotImagineDuration = 1f;
+39 -15
View File
@@ -40,6 +40,16 @@ public class UFSystem : MonoBehaviour
memory
}
private float PresentationSpeed => eyeSystem != null ? eyeSystem.PresentationSpeed : 1f;
private float ScaleDuration(float duration) =>
eyeSystem != null ? eyeSystem.ScaleDuration(duration) : duration;
private void PlayPresentationSfx(string eventName)
{
AudioManager.Instance.PlaySfx(eventName, pitch: PresentationSpeed);
}
private void Awake()
{
FixSystemCenter.SystemDic.Register(this);
@@ -87,10 +97,11 @@ public class UFSystem : MonoBehaviour
[YarnCommand("UF_spriteIn")]
public IEnumerator UF_spriteIn(float duration = 1.5f)
{
duration = ScaleDuration(duration);
var target = ResolveSpriteTarget();
if (target != null)
{
AudioManager.Instance.PlaySfx("event:/ActionFB/color_imagine");
PlayPresentationSfx("event:/ActionFB/color_imagine");
target.ImageIn(duration);
}
@@ -100,10 +111,11 @@ public class UFSystem : MonoBehaviour
[YarnCommand("UF_spriteOut")]
public IEnumerator UF_spriteOut(float duration = 1.5f)
{
duration = ScaleDuration(duration);
var target = ResolveSpriteTarget();
if (target != null)
{
AudioManager.Instance.PlaySfx("event:/ActionFB/color_imagine");
PlayPresentationSfx("event:/ActionFB/color_imagine");
target.ImageOut(duration);
}
@@ -113,6 +125,7 @@ public class UFSystem : MonoBehaviour
[YarnCommand("UF_AllspriteIn")]
public IEnumerator UF_AllspriteIn(float duration = 1.5f)
{
duration = ScaleDuration(duration);
foreach (EyeTarget target in eyeSystem.Targets)
{
target.ImageIn(duration);
@@ -124,6 +137,7 @@ public class UFSystem : MonoBehaviour
[YarnCommand("UF_AllspriteOut")]
public IEnumerator UF_AllspriteOut(float duration = 1.5f)
{
duration = ScaleDuration(duration);
foreach (EyeTarget target in eyeSystem.Targets)
{
target.ImageOut(duration);
@@ -146,6 +160,7 @@ public class UFSystem : MonoBehaviour
Debug.LogWarning("[UFSystem] UF_faceIn: face.sprite 为空(可能是 aseprite 重导入后子资源 ID 失效)。", this);
}
duration = ScaleDuration(duration);
face.gameObject.SetActive(true);
face.DOFade(1f, duration);
yield return new WaitForSeconds(duration);
@@ -159,6 +174,7 @@ public class UFSystem : MonoBehaviour
yield break;
}
duration = ScaleDuration(duration);
face.DOFade(0f, duration);
yield return new WaitForSeconds(duration);
face.gameObject.SetActive(false);
@@ -167,7 +183,8 @@ public class UFSystem : MonoBehaviour
[YarnCommand("UF_ImaginationEffectIn")]
public IEnumerator UF_ImaginationEffectIn(float duration = 1.5f)
{
AudioManager.Instance.PlaySfx("event:/ActionFB/color_imagine");
duration = ScaleDuration(duration);
PlayPresentationSfx("event:/ActionFB/color_imagine");
ClearUnifiedUfSpriteWeights();
TweenWeight(imageVolume, 1f, duration);
yield return new WaitForSeconds(0f);
@@ -176,15 +193,17 @@ public class UFSystem : MonoBehaviour
[YarnCommand("UF_ImaginationEffectOut")]
public IEnumerator UF_ImaginationEffectOut(float duration = 1.5f)
{
duration = ScaleDuration(duration);
TweenWeight(imageVolume, 0f, duration);
AudioManager.Instance.PlaySfx("event:/ActionFB/color_imagine");
PlayPresentationSfx("event:/ActionFB/color_imagine");
yield return new WaitForSeconds(0f);
}
[YarnCommand("UF_MemoryEffectIn")]
public IEnumerator UF_MemoryEffectIn(float duration = 1.5f)
{
AudioManager.Instance.PlaySfx("event:/ActionFB/color_imagine");
duration = ScaleDuration(duration);
PlayPresentationSfx("event:/ActionFB/color_imagine");
ClearUnifiedUfSpriteWeights();
TweenWeight(finalVolume, 1f, duration);
yield return new WaitForSeconds(0f);
@@ -193,15 +212,17 @@ public class UFSystem : MonoBehaviour
[YarnCommand("UF_MemoryEffectOut")]
public IEnumerator UF_MemoryEffectOut(float duration = 1.5f)
{
duration = ScaleDuration(duration);
TweenWeight(finalVolume, 0f, duration);
AudioManager.Instance.PlaySfx("event:/ActionFB/color_imagine");
PlayPresentationSfx("event:/ActionFB/color_imagine");
yield return new WaitForSeconds(0f);
}
[YarnCommand("UF_EyeTargetAscillIn")]
public IEnumerator UF_EyeTargetAscillIn(float duration = 1.5f)
{
AudioManager.Instance.PlaySfx("event:/ActionFB/color_imagine");
duration = ScaleDuration(duration);
PlayPresentationSfx("event:/ActionFB/color_imagine");
ClearUnifiedUfSpriteWeights();
TweenWeight(imageAscillVolume, 1f, duration);
yield return new WaitForSeconds(0f);
@@ -210,8 +231,9 @@ public class UFSystem : MonoBehaviour
[YarnCommand("UF_EyeTargetAscillOut")]
public IEnumerator UF_EyeTargetAscillOut(float duration = 1.5f)
{
duration = ScaleDuration(duration);
TweenWeight(imageAscillVolume, 0f, duration);
AudioManager.Instance.PlaySfx("event:/ActionFB/color_imagine");
PlayPresentationSfx("event:/ActionFB/color_imagine");
yield return new WaitForSeconds(0f);
}
@@ -251,6 +273,7 @@ public class UFSystem : MonoBehaviour
public void UF_fightEffect()
{
ClearUnifiedUfSpriteWeights();
float loopDuration = ScaleDuration(1f);
scaleTween = DOTween.To(() => 0f, t =>
{
if (imageVolume != null)
@@ -267,7 +290,7 @@ public class UFSystem : MonoBehaviour
{
finalVolume.weight = Mathf.Lerp(1f, 0f, t);
}
}, 1f, 1f)
}, 1f, loopDuration)
.SetEase(Ease.InOutSine)
.SetLoops(-1, LoopType.Yoyo);
}
@@ -285,15 +308,15 @@ public class UFSystem : MonoBehaviour
while (isLoopImage)
{
eyeSystem.StartCoroutine(eyeSystem.SetTargetCoroutine(eyeSystem.Targets[index].name, false));
yield return new WaitForSeconds(0.5f);
yield return new WaitForSeconds(ScaleDuration(0.5f));
StartCoroutine(UF_spriteIn(0.5f));
StartCoroutine(UF_ImaginationEffectIn(0.5f));
eyeSystem.Targets[index].ImageIn(0.5f);
yield return new WaitForSeconds(0.5f);
eyeSystem.Targets[index].ImageIn(ScaleDuration(0.5f));
yield return new WaitForSeconds(ScaleDuration(0.5f));
StartCoroutine(UF_ImaginationEffectOut(0.5f));
StartCoroutine(UF_MemoryEffectIn(0.5f));
yield return new WaitForSeconds(1f);
eyeSystem.Targets[index].ImageOut(0.5f);
yield return new WaitForSeconds(ScaleDuration(1f));
eyeSystem.Targets[index].ImageOut(ScaleDuration(0.5f));
StartCoroutine(UF_MemoryEffectOut(0.5f));
index++;
if (index >= eyeSystem.Targets.Count - 2)
@@ -385,7 +408,8 @@ public class UFSystem : MonoBehaviour
yield break;
}
AudioManager.Instance.PlaySfx("event:/ActionFB/color_imagine");
duration = ScaleDuration(duration);
PlayPresentationSfx("event:/ActionFB/color_imagine");
ClearUnifiedUfSpriteWeights();
switch (state)