谱面改进
This commit is contained in:
@@ -1,40 +1,92 @@
|
||||
using Ichni.RhythmGame.Beatmap;
|
||||
using Lean.Pool;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Ichni.RhythmGame
|
||||
{
|
||||
public class HighPassFilterEffect : EffectBase
|
||||
/// <summary>
|
||||
/// 高通滤波效果(Wwise RTPC 驱动版)。
|
||||
/// 在 Wwise 中需要创建名为 "Song_Highpass" 的 RTPC(范围 0~100),
|
||||
/// 并将其连接到 Music Bus 的 High-Pass Filter 参数(Built-in Parameter)。
|
||||
///
|
||||
/// BM 中存储的 peak 是 Unity AudioHighPassFilter.cutoffFrequency(Hz),
|
||||
/// 运行时使用对数公式转换为 Wwise RTPC(0~100),保持与 Unity 编辑器中一致的感知效果。
|
||||
/// 转换公式:rtpc = (ln(currentFreq) - ln(minFreq)) / (ln(maxFreq) - ln(minFreq)) * 100
|
||||
///
|
||||
/// 示例:3500 Hz → RTPC ≈ 76(与编辑器中 Unity HPF 3500Hz 感知等效)
|
||||
/// </summary>
|
||||
public partial class HighPassFilterEffect : EffectBase
|
||||
{
|
||||
#region [效果参数] Effect Parameters
|
||||
// peak:BM 中存储的目标截止频率(Hz),语义与 Unity AudioHighPassFilter.cutoffFrequency 一致
|
||||
public float peak;
|
||||
public bool useEQ;
|
||||
public AnimationCurve intensityCurve;
|
||||
|
||||
private const string RtpcName = "Song_Highpass";
|
||||
|
||||
// 人耳可听范围边界
|
||||
private const float MinFrequency = 20f; // 对应 RTPC = 0(无滤波)
|
||||
private const float MaxFrequency = 20000f; // 对应 RTPC = 100(完全截止)
|
||||
|
||||
// 幂次指数:调节频率到 RTPC 的曲线弯曲程度
|
||||
// 1.0 = 纯线性(效果太弱)
|
||||
// 0.5 = 接近对数(效果太强)
|
||||
// 0.65 = 推荐起点,可根据实际测试微调(向 1.0 调弱,向 0.5 调强)
|
||||
private const float PowerExponent = 0.25f;
|
||||
#endregion
|
||||
|
||||
|
||||
#region [初始化] Initialization
|
||||
public HighPassFilterEffect(float effectTime, float peak, AnimationCurve intensityCurve)
|
||||
public HighPassFilterEffect(float effectTime, float peak, bool useEQ, AnimationCurve intensityCurve)
|
||||
: base(effectTime)
|
||||
{
|
||||
this.effectTime = 0;
|
||||
this.effectTime = effectTime;
|
||||
this.peak = peak;
|
||||
this.useEQ = useEQ;
|
||||
this.intensityCurve = intensityCurve;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region [效果逻辑覆盖] Effect Pattern Overrides
|
||||
public override void Adjust()
|
||||
|
||||
#region [频率转换] Hz → Wwise RTPC
|
||||
/// <summary>
|
||||
/// 将频率值(Hz)通过幂次曲线映射到 Wwise HPF RTPC(0~100)。
|
||||
/// 10 Hz → 0(无滤波),22000 Hz → 100(完全截止)。
|
||||
/// 调整 PowerExponent 可改变曲线弯曲程度:越小越像对数(强),越接近 1 越线性(弱)。
|
||||
/// </summary>
|
||||
private static float FrequencyToRtpc(float frequencyHz)
|
||||
{
|
||||
/*MMF_Player effect = LeanPool.Spawn(GameManager.Instance.basePrefabs.highPassFilterEffect).GetComponent<MMF_Player>();
|
||||
effect.GetFeedbackOfType<MMF_AudioFilterHighPass>().effectTime = effectTime;
|
||||
effect.GetFeedbackOfType<MMF_AudioFilterHighPass>().RemapHighPassZero = 10;
|
||||
effect.GetFeedbackOfType<MMF_AudioFilterHighPass>().RemapHighPassOne = peak;
|
||||
effect.GetFeedbackOfType<MMF_AudioFilterHighPass>().ShakeHighPass = intensityCurve;
|
||||
effect.PlayFeedbacks();
|
||||
LeanPool.Despawn(effect.gameObject, effectTime);*/
|
||||
float clampedFreq = Mathf.Clamp(frequencyHz, MinFrequency, MaxFrequency);
|
||||
// 先归一化到 0~1(线性)
|
||||
float normalized = (clampedFreq - MinFrequency) / (MaxFrequency - MinFrequency);
|
||||
// 幂次变换:compressed = normalized^exponent,使曲线在低频段更灵敏
|
||||
float shaped = Mathf.Pow(normalized, PowerExponent);
|
||||
return Mathf.Clamp(shaped * 100f, 0f, 100f);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region [效果逻辑覆盖] Effect Pattern Overrides
|
||||
public override void PreExecute() { }
|
||||
|
||||
public override void Execute()
|
||||
{
|
||||
// 在 10Hz(无效果)与 peak Hz(最强截止)之间基于曲线插值
|
||||
float intensity = intensityCurve != null ? intensityCurve.Evaluate(effectProgressPercent) : 0f;
|
||||
float currentFreq = Mathf.Lerp(MinFrequency, peak, intensity);
|
||||
|
||||
// 对数映射到 Wwise RTPC
|
||||
float rtpcValue = FrequencyToRtpc(currentFreq);
|
||||
GameManager.Instance.songPlayer.HighPassFilter.SetValue(GameManager.Instance.songPlayer.gameObject, rtpcValue);
|
||||
}
|
||||
|
||||
public override void Adjust() { ResetEffect(); }
|
||||
public override void Recover() { ResetEffect(); }
|
||||
public override void Disrupt() { ResetEffect(); }
|
||||
|
||||
private void ResetEffect()
|
||||
{
|
||||
// RTPC = 0 → 10Hz → 无滤波
|
||||
GameManager.Instance.songPlayer.HighPassFilter.SetValue(GameManager.Instance.songPlayer.gameObject, 0f);
|
||||
Debug.Log("HighPassFilterEffect reset: RTPC set to 0 (10Hz, no filter)");
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -1,42 +1,89 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Ichni.RhythmGame.Beatmap;
|
||||
using Lean.Pool;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Ichni.RhythmGame
|
||||
{
|
||||
public class LowPassFilterEffect : EffectBase
|
||||
/// <summary>
|
||||
/// 低通滤波效果(Wwise RTPC 驱动版)。
|
||||
/// 在 Wwise 中需要创建名为 "Song_Lowpass" 的 RTPC(范围 0~100),
|
||||
/// 并将其连接到 Music Bus 的 Low-Pass Filter 参数(Built-in Parameter)。
|
||||
///
|
||||
/// BM 中存储的 bottom 是 Unity AudioLowPassFilter.cutoffFrequency(Hz),
|
||||
/// 运行时使用对数公式转换为 Wwise RTPC(0~100),保持与 Unity 编辑器中一致的感知效果。
|
||||
/// 转换公式:rtpc = (ln(maxFreq) - ln(currentFreq)) / (ln(maxFreq) - ln(minFreq)) * 100
|
||||
/// </summary>
|
||||
public partial class LowPassFilterEffect : EffectBase
|
||||
{
|
||||
#region [效果参数] Effect Parameters
|
||||
// bottom:BM 中存储的目标截止频率(Hz),语义与 Unity AudioLowPassFilter.cutoffFrequency 一致
|
||||
public float bottom;
|
||||
public bool useEQ;
|
||||
public AnimationCurve intensityCurve;
|
||||
|
||||
private const string RtpcName = "Song_Lowpass";
|
||||
|
||||
// 人耳可听范围边界
|
||||
private const float MinFrequency = 20f; // 对应 RTPC = 100(完全截止)
|
||||
private const float MaxFrequency = 20000f; // 对应 RTPC = 0(无滤波)
|
||||
|
||||
// 幂次指数:调节频率到 RTPC 的曲线弯曲程度
|
||||
// 1.0 = 纯线性(效果太弱)
|
||||
// 0.5 = 接近对数(效果太强)
|
||||
// 0.65 = 推荐起点,可根据实际测试微调(向 1.0 调弱,向 0.5 调强)
|
||||
private const float PowerExponent = 0.25f;
|
||||
#endregion
|
||||
|
||||
|
||||
#region [初始化] Initialization
|
||||
public LowPassFilterEffect(float effectTime, float bottom, AnimationCurve intensityCurve)
|
||||
public LowPassFilterEffect(float effectTime, float bottom, bool useEQ, AnimationCurve intensityCurve)
|
||||
: base(effectTime)
|
||||
{
|
||||
this.effectTime = 0;
|
||||
this.effectTime = effectTime;
|
||||
this.bottom = bottom;
|
||||
this.useEQ = useEQ;
|
||||
this.intensityCurve = intensityCurve;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region [效果逻辑覆盖] Effect Pattern Overrides
|
||||
public override void Adjust()
|
||||
|
||||
#region [频率转换] Hz → Wwise RTPC
|
||||
/// <summary>
|
||||
/// 将频率值(Hz)通过幂次曲线映射到 Wwise LPF RTPC(0~100)。
|
||||
/// 22000 Hz → 0(无滤波),趋近 10 Hz → 100(完全截止)。
|
||||
/// 调整 PowerExponent 可改变曲线弯曲程度:越小越像对数(强),越接近 1 越线性(弱)。
|
||||
/// </summary>
|
||||
private static float FrequencyToRtpc(float frequencyHz)
|
||||
{
|
||||
/*MMF_Player effect = LeanPool.Spawn(GameManager.Instance.basePrefabs.lowPassFilterEffect).GetComponent<MMF_Player>();
|
||||
effect.GetFeedbackOfType<MMF_AudioFilterLowPass>().effectTime = effectTime;
|
||||
effect.GetFeedbackOfType<MMF_AudioFilterLowPass>().RemapLowPassZero = 22000;
|
||||
effect.GetFeedbackOfType<MMF_AudioFilterLowPass>().RemapLowPassOne = bottom;
|
||||
effect.GetFeedbackOfType<MMF_AudioFilterLowPass>().ShakeLowPass = intensityCurve;
|
||||
effect.PlayFeedbacks();
|
||||
LeanPool.Despawn(effect.gameObject, effectTime);*/
|
||||
float clampedFreq = Mathf.Clamp(frequencyHz, MinFrequency, MaxFrequency);
|
||||
// 先归一化:频率大 = 房内线性值大(无滤波方向)
|
||||
float normalized = (clampedFreq - MinFrequency) / (MaxFrequency - MinFrequency);
|
||||
// 幂次变换,然后反转(频率越低 = RTPC 越高 = 截止越强)
|
||||
float shaped = Mathf.Pow(normalized, PowerExponent);
|
||||
return Mathf.Clamp((1f - shaped) * 100f, 0f, 100f);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region [效果逻辑覆盖] Effect Pattern Overrides
|
||||
public override void PreExecute() { }
|
||||
|
||||
public override void Execute()
|
||||
{
|
||||
// 在 22000Hz(无效果)与 bottom Hz(最强截止)之间基于曲线插值
|
||||
float intensity = intensityCurve != null ? intensityCurve.Evaluate(effectProgressPercent) : 0f;
|
||||
float currentFreq = Mathf.Lerp(MaxFrequency, bottom, intensity);
|
||||
|
||||
// 对数映射到 Wwise RTPC
|
||||
float rtpcValue = FrequencyToRtpc(currentFreq);
|
||||
GameManager.Instance.songPlayer.LowPassFilter.SetValue(GameManager.Instance.songPlayer.gameObject, rtpcValue);
|
||||
}
|
||||
|
||||
public override void Adjust() { ResetEffect(); }
|
||||
public override void Recover() { ResetEffect(); }
|
||||
public override void Disrupt() { ResetEffect(); }
|
||||
|
||||
private void ResetEffect()
|
||||
{
|
||||
// RTPC = 0 → 22000Hz → 无滤波
|
||||
GameManager.Instance.songPlayer.LowPassFilter.SetValue(GameManager.Instance.songPlayer.gameObject, 0f);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user