狗屎Minimax坏我代码

This commit is contained in:
SoulliesOfficial
2026-04-18 13:57:19 -04:00
parent 41140a2017
commit 7379583165
473 changed files with 34480 additions and 8069 deletions

View File

@@ -16,6 +16,7 @@ namespace SLSUtilities.Feedback
/// </summary>
[Title("Curve Shake")]
[LabelText("Shake Curve")]
[ShakeCurvePreset]
public AnimationCurve shakeCurve = new AnimationCurve(
new Keyframe(0f, 0f),
new Keyframe(0.5f, 1f),
@@ -40,24 +41,10 @@ namespace SLSUtilities.Feedback
[LabelText("Relative to Initial")]
public bool relativeToInitial;
/// <summary>
/// 根据归一化时间采样曲线并映射到实际值范围。
/// 如果 relativeToInitial 为 true结果会叠加在 initialValue 上。
/// </summary>
/// <param name="normalizedTime">归一化时间 [0,1]</param>
/// <param name="initialValue">初始值OnStart 时记录)</param>
/// <returns>映射后的最终数值</returns>
protected float EvaluateShake(float normalizedTime, float initialValue)
protected virtual float EvaluateShake(float normalizedTime, float initialValue)
{
float curveValue = shakeCurve.Evaluate(normalizedTime);
float remappedValue = Mathf.LerpUnclamped(remapMin, remapMax, curveValue);
if (relativeToInitial)
{
return initialValue + remappedValue;
}
return remappedValue;
return base.EvaluateShake(shakeCurve, remapMin, remapMax, relativeToInitial, normalizedTime, initialValue);
}
}
}

View File

@@ -33,6 +33,16 @@ namespace SLSUtilities.Feedback
/// Clip 总时长(秒)。
/// </summary>
public float duration;
/// <summary>
/// 当前 Clip 的综合时间缩放系数(含 Global/Group/Local由 FeedbackPlayer 每帧动态计算。
/// </summary>
public float timeScale;
/// <summary>
/// 当前 Clip 是否动态获取当前的时间缩放
/// </summary>
public FeedbackTimeSettings timeSettings;
}
/// <summary>
@@ -48,6 +58,11 @@ namespace SLSUtilities.Feedback
/// </summary>
public virtual string DisplayName => GetType().Name;
/// <summary>
/// 是否忽略时间缩放。如果为true此Action将使用原始deltaTime不受TimeScale影响。
/// </summary>
public virtual bool IgnoreTimeScale => false;
/// <summary>
/// 初始化FeedbackPlayer 开始播放此 Clip 时调用。
/// </summary>
@@ -81,5 +96,30 @@ namespace SLSUtilities.Feedback
/// 用于 Editor 预览Runtime 也可用)。
/// </summary>
public virtual void Preview() { }
/// <summary>
/// 根据归一化时间采样曲线并映射到实际值范围。
/// 如果 relativeToInitial 为 true结果会叠加在 initialValue 上。
/// </summary>
/// <param name="shakeCurve">震动曲线X 轴为归一化时间 [0,1]Y 轴为震动强度 [0,1]。</param>
/// <param name="remapMin">曲线值 0 对应的实际数值。</param>
/// <param name="remapMax">曲线值 1 对应的实际数值。</param>
/// <param name="relativeToInitial">是否在初始值上叠加(而非替换)。</param>
/// <param name="normalizedTime">归一化时间 [0,1]</param>
/// <param name="initialValue">初始值OnStart 时记录)</param>
/// <returns>映射后的最终数值</returns>
protected virtual float EvaluateShake(AnimationCurve shakeCurve, float remapMin, float remapMax, bool relativeToInitial,
float normalizedTime, float initialValue)
{
float curveValue = shakeCurve.Evaluate(normalizedTime);
float remappedValue = Mathf.LerpUnclamped(remapMin, remapMax, curveValue);
if (relativeToInitial)
{
return initialValue + remappedValue;
}
return remappedValue;
}
}
}

View File

@@ -0,0 +1,19 @@
using System;
using UnityEngine;
namespace SLSUtilities.Feedback
{
/// <summary>
/// 为 FeedbackActionBase 子类指定在时间轴编辑器中的显示颜色。
/// </summary>
[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
public sealed class FeedbackActionColorAttribute : Attribute
{
public Color Color { get; }
public FeedbackActionColorAttribute(float r, float g, float b, float a = 0.8f)
{
Color = new Color(r, g, b, a);
}
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 460e3071059bce248806e5bb6f81d27e

View File

@@ -11,17 +11,21 @@ namespace SLSUtilities.Feedback
[Serializable]
public class FeedbackClip
{
public string clipName;
/// <summary>
/// 片段开始时间(秒)。
/// </summary>
[MinValue(0f)]
[HorizontalGroup("Time"), LabelWidth(60)]
public float startTime;
/// <summary>
/// 片段持续时间(秒)。
/// </summary>
[MinValue(0.01f)]
public float duration = 0.1f;
[HorizontalGroup("Time"), LabelWidth(60)]
public float duration = 0.2f;
/// <summary>
/// 片段结束时间(秒)。
@@ -31,7 +35,7 @@ namespace SLSUtilities.Feedback
/// <summary>
/// 是否覆盖 FeedbackData 的时间设置。
/// </summary>
[Title("Time Override")]
[LabelWidth(150)]
public bool overrideTimeSettings;
/// <summary>
@@ -43,7 +47,7 @@ namespace SLSUtilities.Feedback
/// <summary>
/// 具体反馈动作Odin 自动显示多态类型选择器。
/// </summary>
[Title("Action"), SerializeReference]
[SerializeReference]
public FeedbackActionBase action;
}
}

View File

@@ -1,6 +1,8 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Sirenix.OdinInspector;
using UniRx;
using UnityEngine;
namespace SLSUtilities.Feedback
@@ -10,7 +12,7 @@ namespace SLSUtilities.Feedback
/// 包含多条轨道Track每条轨道包含按时间排列的片段Clip
/// </summary>
[CreateAssetMenu(fileName = "NewFeedbackData", menuName = "SLS/Feedback/FeedbackData")]
public class FeedbackData : SerializedScriptableObject
public partial class FeedbackData : SerializedScriptableObject
{
/// <summary>
/// 父级集合引用,由 FeedbackDataCollection 自动维护。
@@ -27,7 +29,6 @@ namespace SLSUtilities.Feedback
/// <summary>
/// 全局默认的时间设置。Clip 可选择覆盖此设置。
/// </summary>
[Title("Time Settings (Default)")]
public FeedbackTimeSettings defaultTimeSettings = new FeedbackTimeSettings();
/// <summary>
@@ -67,4 +68,47 @@ namespace SLSUtilities.Feedback
Debug.Log($"[FeedbackData] Previewing '{feedbackName}' (Duration: {TotalDuration:F2}s)");
}
}
public partial class FeedbackData
{
public FeedbackTrack Track(string name)
{
FeedbackTrack track = tracks.FirstOrDefault(t => t.trackName == name);
if (track == null)
{
Debug.LogWarning($"[FeedbackData] Track '{name}' not found in FeedbackData '{feedbackName}'.");
}
return track;
}
public FeedbackClip Clip(string trackName, Func<FeedbackClip, bool> predicate)
{
FeedbackTrack track = Track(trackName);
if (track == null) return null;
FeedbackClip clip = track.clips.FirstOrDefault(predicate);
if (clip == null)
{
Debug.LogWarning($"[FeedbackData] Clip matching predicate not found in Track '{trackName}' of FeedbackData '{feedbackName}'.");
}
return clip;
}
public FeedbackClip Clip<T>(string trackName) where T : FeedbackActionBase
{
return Clip(trackName, c => c.action.GetType() == typeof(T));
}
public FeedbackClip Clip(string trackName, string clipName)
{
return Clip(trackName, c => c.clipName == clipName);
}
public T Action<T>(string trackName) where T : FeedbackActionBase
{
FeedbackTrack track = Track(trackName);
FeedbackClip clip = track?.clips.FirstOrDefault(c => c.action.GetType() == typeof(T));
return clip?.action as T;
}
}
}

View File

@@ -1,5 +1,7 @@
using System;
using Sirenix.OdinInspector;
using UnityEngine;
using UnityEngine.Serialization;
namespace SLSUtilities.Feedback
{
@@ -9,26 +11,26 @@ namespace SLSUtilities.Feedback
/// Clip 级设置如果 useTimeScale = true 则覆盖 Data 级设置。
/// </summary>
[Serializable]
public class FeedbackTimeSettings
public partial class FeedbackTimeSettings
{
/// <summary>
/// 是否使用时间缩放。默认 false 表示不受任何时间缩放影响。
/// </summary>
public bool useTimeScale;
[EnumButtons]
public TimeScaleType timeScaleType = TimeScaleType.Global;
[FormerlySerializedAs("useDynamicTimeScale")]
[Tooltip("是否动态获取当前的时间缩放。启用后FeedbackPlayer 每帧计算当前综合时间缩放系数并传递给 FeedbackAction。")]
[HideIf("timeScaleType", TimeScaleType.Unscaled)]
[LabelText("Apply Dynamic")]
public bool applyDynamicTimeScale = true;
}
/// <summary>
/// 受 TimeManager.globalTimeScale 影响。
/// </summary>
public bool affectedByGlobalTimeScale;
/// <summary>
/// 受 TimeManager 的分组时间影响player/enemy 等)。
/// </summary>
public bool affectedByGroupTimeScale;
/// <summary>
/// 受角色本地 localTimeScale 影响。
/// </summary>
public bool affectedByLocalTimeScale;
public partial class FeedbackTimeSettings
{
public enum TimeScaleType
{
Unscaled = 0,
Global = 1,
Group = 2,
Local = 3
}
}
}

View File

@@ -10,12 +10,13 @@ namespace SLSUtilities.Feedback
/// 多个 Track 天然并行播放Track 内的 Clip 按时间顺序排列,不重叠。
/// </summary>
[Serializable]
public class FeedbackTrack
public partial class FeedbackTrack
{
/// <summary>
/// 轨道名称,用于调试和 Inspector 显示。
/// </summary>
[LabelText("Track Name")]
[ValueDropdown("GetTrackNamesList")]
public string trackName = "New Track";
/// <summary>
@@ -43,4 +44,15 @@ namespace SLSUtilities.Feedback
/// </summary>
public float TotalDuration => clips.Count > 0 ? clips.Max(c => c.EndTime) : 0f;
}
public partial class FeedbackTrack
{
private List<string> GetTrackNamesList()
{
return new List<string>()
{
"Camera", "Time", "Postprocessing", "Audio"
};
}
}
}

View File

@@ -22,8 +22,14 @@ namespace SLSUtilities.Feedback
float LocalTimeScale { get; }
/// <summary>
/// 根据时间设置计算实际 deltaTime。
/// 根据时间设置计算实际 deltaTime(秒)
/// </summary>
float GetDeltaTime(FeedbackTimeSettings settings);
/// <summary>
/// 根据时间设置计算综合时间缩放系数(无 deltaTime 乘入)。
/// 返回 1.0 表示正常速度。
/// </summary>
float GetTimeScale(FeedbackTimeSettings settings);
}
}

View File

@@ -0,0 +1,12 @@
using System;
namespace SLSUtilities.Feedback
{
/// <summary>
/// 标记 AnimationCurve 字段,使其在 Inspector 中显示震动曲线预设按钮组。
/// </summary>
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
public class ShakeCurvePresetAttribute : Attribute
{
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 8d647fdd1e522b249b302cc16ccc365d

View File

@@ -0,0 +1,177 @@
using UnityEngine;
namespace SLSUtilities.Feedback
{
/// <summary>
/// 震动曲线预设集合。每个方法返回一条全新的 AnimationCurve 实例。
/// 所有曲线的 X 轴为归一化时间 [0, 1]Y 轴为强度系数。
/// </summary>
public static class ShakeCurvePresets
{
/// <summary>
/// 预设名 → 工厂方法。
/// </summary>
public static readonly (string name, System.Func<AnimationCurve> factory)[] All = new[]
{
("Impact", (System.Func<AnimationCurve>)QuickImpact),
("Punch", (System.Func<AnimationCurve>)Punch),
("Bump", (System.Func<AnimationCurve>)SmoothBump),
("Fade Out", (System.Func<AnimationCurve>)FadeOut),
("Oscillate", (System.Func<AnimationCurve>)Oscillate),
("Dense Osc.", (System.Func<AnimationCurve>)DenseOscillate),
("Anticipation", (System.Func<AnimationCurve>)Anticipation),
("Recoil", (System.Func<AnimationCurve>)Recoil),
("Double Hit", (System.Func<AnimationCurve>)DoubleHit),
("Flash", (System.Func<AnimationCurve>)Flash),
};
/// <summary>
/// 快速冲击 — 峰值出现在 10~15%,之后快速衰减至零。
/// 适合:打击确认、子弹命中、轻攻击。
/// </summary>
public static AnimationCurve QuickImpact()
{
return new AnimationCurve(
new Keyframe(0f, 0f, 0f, 8f),
new Keyframe(0.12f, 1f, 0f, 0f),
new Keyframe(0.5f, 0.12f, -0.6f, -0.3f),
new Keyframe(1f, 0f, -0.1f, 0f)
);
}
/// <summary>
/// 重拳 — 第 1 帧即达峰值,线性衰减至零。
/// 适合:重击、格挡冲击、爆炸瞬间。
/// </summary>
public static AnimationCurve Punch()
{
return new AnimationCurve(
new Keyframe(0f, 0f, 0f, float.PositiveInfinity),
new Keyframe(0.02f, 1f, 0f, -1.02f),
new Keyframe(1f, 0f, -1.02f, 0f)
);
}
/// <summary>
/// 平滑铃形 — 对称的升/降曲线EaseInOut
/// 适合:脚步震动、跳跃落地、节奏性效果。
/// </summary>
public static AnimationCurve SmoothBump()
{
return new AnimationCurve(
new Keyframe(0f, 0f, 0f, 0f),
new Keyframe(0.5f, 1f, 0f, 0f),
new Keyframe(1f, 0f, 0f, 0f)
);
}
/// <summary>
/// 渐弱 — 起始即为峰值EaseOut 衰减至零。
/// 适合:爆炸余波、效果消散、技能冷却过渡。
/// </summary>
public static AnimationCurve FadeOut()
{
return new AnimationCurve(
new Keyframe(0f, 1f, 0f, 0f),
new Keyframe(1f, 0f, -2f, 0f)
);
}
/// <summary>
/// 衰减振荡 — 2~3 个波峰逐渐衰减。
/// 适合:爆炸余震、碰撞后振荡、弹跳。
/// </summary>
public static AnimationCurve Oscillate()
{
return new AnimationCurve(
new Keyframe(0f, 0f, 0f, 12f),
new Keyframe(0.08f, 1f, 0f, 0f),
new Keyframe(0.24f, -0.55f, 0f, 0f),
new Keyframe(0.42f, 0.3f, 0f, 0f),
new Keyframe(0.62f, -0.12f, 0f, 0f),
new Keyframe(1f, 0f, 0f, 0f)
);
}
/// <summary>
/// 密集衰减振荡 — 5~6 个波峰,频率更高,适合更猛烈的效果。
/// 适合:大型爆炸持续余震、引擎振动、电击。
/// </summary>
public static AnimationCurve DenseOscillate()
{
return new AnimationCurve(
new Keyframe(0f, 0f, 0f, 20f),
new Keyframe(0.05f, 1f, 0f, 0f),
new Keyframe(0.13f, -0.72f, 0f, 0f),
new Keyframe(0.21f, 0.52f, 0f, 0f),
new Keyframe(0.29f, -0.36f, 0f, 0f),
new Keyframe(0.38f, 0.24f, 0f, 0f),
new Keyframe(0.48f, -0.15f, 0f, 0f),
new Keyframe(0.60f, 0.08f, 0f, 0f),
new Keyframe(0.75f, -0.03f, 0f, 0f),
new Keyframe(1f, 0f, 0f, 0f)
);
}
/// <summary>
/// 预兆 + 冲击 — 先小幅反向蓄力,再大幅正向爆发。
/// 适合:重型近战预备 + 冲击,与动画预备帧配合。
/// </summary>
public static AnimationCurve Anticipation()
{
return new AnimationCurve(
new Keyframe(0f, 0f, 0f, 0f),
new Keyframe(0.18f, -0.2f, 0f, 0f),
new Keyframe(0.32f, 0f, 0f, 4f),
new Keyframe(0.5f, 1f, 0f, 0f),
new Keyframe(1f, 0f, -1.2f, 0f)
);
}
/// <summary>
/// 后坐/反弹 — 快速达到峰值后超调回弹,逐渐收敛。
/// 适合:枪械后坐力、刀击挥出后的回摆。
/// </summary>
public static AnimationCurve Recoil()
{
return new AnimationCurve(
new Keyframe(0f, 0f, 0f, 10f),
new Keyframe(0.1f, 1f, 0f, 0f),
new Keyframe(0.32f, -0.3f, 0f, 0f),
new Keyframe(0.52f, 0.12f, 0f, 0f),
new Keyframe(0.72f, -0.04f, 0f, 0f),
new Keyframe(1f, 0f, 0f, 0f)
);
}
/// <summary>
/// 双击峰 — 两个依次递减的波峰。
/// 适合:连击、二段攻击、双弹命中。
/// </summary>
public static AnimationCurve DoubleHit()
{
return new AnimationCurve(
new Keyframe(0f, 0f, 0f, 8f),
new Keyframe(0.12f, 1f, 0f, 0f),
new Keyframe(0.32f, 0.1f, 0f, 0f),
new Keyframe(0.52f, 0.7f, 0f, 0f),
new Keyframe(1f, 0f, -0.6f, 0f)
);
}
/// <summary>
/// 方波闪光 — 近乎即时上升/下降,中间保持峰值。
/// 适合:描边闪光、即时视觉强调、全屏闪白。
/// </summary>
public static AnimationCurve Flash()
{
// 使用极陡切线模拟方波:在极短时间内完成升/降
return new AnimationCurve(
new Keyframe(0f, 0f, 0f, float.PositiveInfinity),
new Keyframe(0.02f, 1f, float.PositiveInfinity, 0f),
new Keyframe(0.98f, 1f, 0f, float.NegativeInfinity),
new Keyframe(1f, 0f, float.NegativeInfinity, 0f)
);
}
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 260284d08797fb74d9922b06fd61db55

View File

@@ -0,0 +1,34 @@
using UnityEngine;
namespace SLSUtilities.Feedback
{
/// <summary>
/// 后处理震动实例的运行时状态。
/// 由 Shaker 维护,支持多个实例的叠加混合。
/// </summary>
public class ShakeInstanceBase
{
public FeedbackTimeSettings timeSettings;
public IFeedbackTimeProvider timeProvider;
public float timer;
public float duration;
public ShakeInstanceBase(FeedbackTimeSettings timeSettings, IFeedbackTimeProvider timeProvider, float duration)
{
this.timeSettings = timeSettings;
this.timeProvider = timeProvider;
this.duration = duration;
timer = 0f;
}
public void Tick()
{
timer += timeProvider.GetDeltaTime(timeSettings);
}
/// <summary>
/// 当前震动是否已结束。
/// </summary>
public bool IsFinished => timer >= duration;
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 0e5361f026a60804a9f494f514e810cb