狗屎Minimax坏我代码
This commit is contained in:
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8b6df685fb7fcb144a2f10822632a9f5
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,43 @@
|
||||
using System;
|
||||
using Sirenix.OdinInspector;
|
||||
using SLSUtilities.Feedback;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Cielonos.MainGame.Effects.Feedback
|
||||
{
|
||||
/// <summary>
|
||||
/// Cinemachine摄像机震动Action的基类。
|
||||
/// 封装了统一的触发逻辑和参数定义。
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public abstract class CinemachineActionBase : FeedbackActionBase
|
||||
{
|
||||
public override void OnStart(FeedbackContext context)
|
||||
{
|
||||
TriggerEvent(context);
|
||||
}
|
||||
|
||||
public override void OnUpdate(FeedbackContext context, float normalizedTime)
|
||||
{
|
||||
}
|
||||
|
||||
public override void OnEnd(FeedbackContext context)
|
||||
{
|
||||
}
|
||||
|
||||
public override void OnInterrupt(FeedbackContext context)
|
||||
{
|
||||
StopEvent(context);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 触发震动事件(由子类实现)。
|
||||
/// </summary>
|
||||
protected abstract void TriggerEvent(FeedbackContext context);
|
||||
|
||||
/// <summary>
|
||||
/// 停止震动事件(由子类实现)。
|
||||
/// </summary>
|
||||
protected abstract void StopEvent(FeedbackContext context);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bfcba53df80e9ad468aa639ef88e9c57
|
||||
@@ -0,0 +1,181 @@
|
||||
using System;
|
||||
using Sirenix.OdinInspector;
|
||||
using SLSUtilities.Feedback;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Cielonos.MainGame.Effects.Feedback
|
||||
{
|
||||
/// <summary>
|
||||
/// 曲线通道模块,用于定义一个可复用的曲线震动参数。
|
||||
/// 包含激活状态、曲线定义、重映射范围。
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public struct FloatCurveChannel
|
||||
{
|
||||
/// <summary>
|
||||
/// 是否启用此通道。
|
||||
/// </summary>
|
||||
public bool active;
|
||||
|
||||
/// <summary>
|
||||
/// 震动曲线,X轴为归一化时间[0,1],Y轴为强度[0,1]。
|
||||
/// </summary>
|
||||
[ShowIf("active")]
|
||||
[ShakeCurvePreset]
|
||||
public AnimationCurve curve;
|
||||
|
||||
/// <summary>
|
||||
/// 曲线值0对应的实际数值。
|
||||
/// </summary>
|
||||
[ShowIf("active")]
|
||||
[LabelText("Remap Min")]
|
||||
public float remapMin;
|
||||
|
||||
/// <summary>
|
||||
/// 曲线值1对应的实际数值。
|
||||
/// </summary>
|
||||
[ShowIf("active")]
|
||||
[LabelText("Remap Max")]
|
||||
public float remapMax;
|
||||
|
||||
/// <summary>
|
||||
/// 是否相对初始值叠加。
|
||||
/// </summary>
|
||||
[Tooltip("开启时,结果叠加在初始值上;关闭时,结果为绝对值")]
|
||||
public bool relativeToInitial;
|
||||
|
||||
/// <summary>
|
||||
/// 创建默认的曲线通道。
|
||||
/// </summary>
|
||||
public static FloatCurveChannel CreateDefault(bool active = true, float remapMin = 0f, float remapMax = 1f, bool relativeToInitial = true)
|
||||
{
|
||||
return new FloatCurveChannel
|
||||
{
|
||||
active = active,
|
||||
curve = new AnimationCurve(
|
||||
new Keyframe(0f, 0f),
|
||||
new Keyframe(0.5f, 1f),
|
||||
new Keyframe(1f, 0f)
|
||||
),
|
||||
remapMin = remapMin,
|
||||
remapMax = remapMax,
|
||||
relativeToInitial = relativeToInitial
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据归一化时间计算当前值。
|
||||
/// </summary>
|
||||
public readonly float Evaluate(float normalizedTime)
|
||||
{
|
||||
if (!active || curve == null) return 0f;
|
||||
float t = Mathf.Clamp01(normalizedTime);
|
||||
float curveValue = curve.Evaluate(t);
|
||||
return Mathf.LerpUnclamped(remapMin, remapMax, curveValue);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 带颜色选项的曲线通道。
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public struct ColorCurveChannel
|
||||
{
|
||||
/// <summary>
|
||||
/// 是否启用此通道。
|
||||
/// </summary>
|
||||
public bool active;
|
||||
|
||||
/// <summary>
|
||||
/// 颜色渐变。
|
||||
/// </summary>
|
||||
[ShowIf("active")]
|
||||
[LabelText("颜色渐变")]
|
||||
public Gradient gradient;
|
||||
|
||||
/// <summary>
|
||||
/// 创建默认的颜色曲线通道。
|
||||
/// </summary>
|
||||
public static ColorCurveChannel CreateDefault()
|
||||
{
|
||||
return new ColorCurveChannel
|
||||
{
|
||||
active = true,
|
||||
gradient = new Gradient()
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据归一化时间获取颜色。
|
||||
/// </summary>
|
||||
public Color Evaluate(float normalizedTime)
|
||||
{
|
||||
if (!active || gradient == null) return Color.white;
|
||||
return gradient.Evaluate(Mathf.Clamp01(normalizedTime));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 带Vector2选项的曲线通道(用于中心点等)。
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public struct Vector2CurveChannel
|
||||
{
|
||||
public bool active;
|
||||
|
||||
[ShowIf("active")]
|
||||
[LabelText("曲线 X")]
|
||||
[ShakeCurvePreset]
|
||||
public AnimationCurve curveX;
|
||||
|
||||
[ShowIf("active")]
|
||||
[LabelText("曲线 Y")]
|
||||
[ShakeCurvePreset]
|
||||
public AnimationCurve curveY;
|
||||
|
||||
[ShowIf("active")]
|
||||
[LabelText("Remap Min")]
|
||||
public Vector2 remapMin;
|
||||
|
||||
[ShowIf("active")]
|
||||
[LabelText("Remap Max")]
|
||||
public Vector2 remapMax;
|
||||
|
||||
/// <summary>
|
||||
/// 是否相对初始值叠加。
|
||||
/// </summary>
|
||||
[TitleGroup("高级设置")]
|
||||
[LabelText("相对初始值")]
|
||||
[Tooltip("开启时,结果叠加在初始值上;关闭时,结果为绝对值")]
|
||||
public bool relativeToInitial;
|
||||
|
||||
public static Vector2CurveChannel CreateDefault()
|
||||
{
|
||||
return new Vector2CurveChannel
|
||||
{
|
||||
active = true,
|
||||
curveX = new AnimationCurve(new Keyframe(0f, 0f), new Keyframe(1f, 1f)),
|
||||
curveY = new AnimationCurve(new Keyframe(0f, 0f), new Keyframe(1f, 1f)),
|
||||
remapMin = Vector2.zero,
|
||||
remapMax = Vector2.one
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据归一化时间计算Vector2值。
|
||||
/// </summary>
|
||||
public Vector2 Evaluate(float normalizedTime, Vector2 initialValue)
|
||||
{
|
||||
if (!active) return Vector2.zero;
|
||||
float t = Mathf.Clamp01(normalizedTime);
|
||||
float x = curveX?.Evaluate(t) ?? 0f;
|
||||
float y = curveY?.Evaluate(t) ?? 0f;
|
||||
Vector2 remappedValue = new Vector2(
|
||||
Mathf.LerpUnclamped(remapMin.x, remapMax.x, x),
|
||||
Mathf.LerpUnclamped(remapMin.y, remapMax.y, y)
|
||||
);
|
||||
|
||||
return relativeToInitial ? initialValue + remappedValue : remappedValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e453f5e1c6b44764f8284800f9f46479
|
||||
@@ -0,0 +1,44 @@
|
||||
using System;
|
||||
using Sirenix.OdinInspector;
|
||||
using SLSUtilities.Feedback;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Cielonos.MainGame.Effects.Feedback
|
||||
{
|
||||
/// <summary>
|
||||
/// 后处理震动Action的基类。
|
||||
/// 封装了统一的曲线参数定义和生命周期管理。
|
||||
/// 子类需要实现TriggerEvent和StopEvent抽象方法。
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public abstract class PostprocessingActionBase : FeedbackActionBase
|
||||
{
|
||||
public override void OnStart(FeedbackContext context)
|
||||
{
|
||||
TriggerEvent(context);
|
||||
}
|
||||
|
||||
public override void OnUpdate(FeedbackContext context, float normalizedTime)
|
||||
{
|
||||
}
|
||||
|
||||
public override void OnEnd(FeedbackContext context)
|
||||
{
|
||||
}
|
||||
|
||||
public override void OnInterrupt(FeedbackContext context)
|
||||
{
|
||||
StopEvent(context);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 触发震动事件(由子类实现)。
|
||||
/// </summary>
|
||||
protected abstract void TriggerEvent(FeedbackContext context);
|
||||
|
||||
/// <summary>
|
||||
/// 停止震动事件(由子类实现)。
|
||||
/// </summary>
|
||||
protected abstract void StopEvent(FeedbackContext context);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a4a098e68b58d5e4487a805af4437aea
|
||||
@@ -1,102 +0,0 @@
|
||||
using System;
|
||||
using MoreMountains.Feedbacks;
|
||||
using Sirenix.OdinInspector;
|
||||
using SLSUtilities.Feedback;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Cielonos.MainGame.Effects.Feedback
|
||||
{
|
||||
/// <summary>
|
||||
/// 摄像机位移震动反馈,通过 MMCinemachinePositionShakeEvent 触发现有的 Shaker。
|
||||
/// Shaker 负责处理多个震动的叠加混合。
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class CameraPositionShakeAction : FeedbackActionBase
|
||||
{
|
||||
public override string DisplayName => "Camera Position Shake";
|
||||
|
||||
/// <summary>
|
||||
/// 震动曲线,定义震动强度随时间的变化。
|
||||
/// </summary>
|
||||
[Title("Position Shake")]
|
||||
[LabelText("Shake Curve")]
|
||||
public AnimationCurve shakeCurve = new AnimationCurve(
|
||||
new Keyframe(0f, 0f),
|
||||
new Keyframe(0.2f, 1f),
|
||||
new Keyframe(1f, 0f)
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// 最大位移振幅(本地空间)。
|
||||
/// </summary>
|
||||
[LabelText("Amplitude")]
|
||||
public Vector3 positionAmplitude = new Vector3(0.5f, 0.5f, 0f);
|
||||
|
||||
/// <summary>
|
||||
/// 方向影响设置。
|
||||
/// </summary>
|
||||
[Title("Direction")]
|
||||
public CameraDirectionSettings directionSettings = new CameraDirectionSettings();
|
||||
|
||||
/// <summary>
|
||||
/// 距离衰减:根据摄像机与 owner 的距离衰减震动强度。
|
||||
/// </summary>
|
||||
[Title("Distance Attenuation")]
|
||||
[LabelText("Use Attenuation")]
|
||||
public bool useAttenuation;
|
||||
|
||||
/// <summary>
|
||||
/// 全强度的最大距离。
|
||||
/// </summary>
|
||||
[ShowIf("useAttenuation")]
|
||||
[LabelText("Attenuation Range")]
|
||||
public float attenuationRange = 50f;
|
||||
|
||||
/// <summary>
|
||||
/// 距离-强度衰减曲线(0=近处/全强度,1=远处/无强度)。
|
||||
/// </summary>
|
||||
[ShowIf("useAttenuation")]
|
||||
[LabelText("Attenuation Curve")]
|
||||
public AnimationCurve attenuationCurve = new AnimationCurve(
|
||||
new Keyframe(0f, 1f),
|
||||
new Keyframe(1f, 0f)
|
||||
);
|
||||
|
||||
public override void OnStart(FeedbackContext context)
|
||||
{
|
||||
Vector3 finalAmplitude = directionSettings.TransformAmplitude(positionAmplitude, context.owner);
|
||||
float intensityMultiplier = ComputeAttenuation(context);
|
||||
|
||||
MMCinemachinePositionShakeEvent.Trigger(
|
||||
null,
|
||||
shakeCurve,
|
||||
context.duration,
|
||||
finalAmplitude,
|
||||
intensityMultiplier
|
||||
);
|
||||
}
|
||||
|
||||
public override void OnInterrupt(FeedbackContext context)
|
||||
{
|
||||
MMCinemachinePositionShakeEvent.Trigger(
|
||||
null, shakeCurve, 0f, Vector3.zero, 0f,
|
||||
stop: true
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 计算距离衰减系数。
|
||||
/// </summary>
|
||||
private float ComputeAttenuation(FeedbackContext context)
|
||||
{
|
||||
if (!useAttenuation || context.owner == null) return 1f;
|
||||
|
||||
Camera mainCamera = Camera.main;
|
||||
if (mainCamera == null) return 1f;
|
||||
|
||||
float distance = Vector3.Distance(context.owner.position, mainCamera.transform.position);
|
||||
float normalizedDistance = Mathf.Clamp01(distance / attenuationRange);
|
||||
return attenuationCurve.Evaluate(normalizedDistance);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,98 +0,0 @@
|
||||
using System;
|
||||
using MoreMountains.FeedbacksForThirdParty;
|
||||
using Sirenix.OdinInspector;
|
||||
using SLSUtilities.Feedback;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Cielonos.MainGame.Effects.Feedback
|
||||
{
|
||||
/// <summary>
|
||||
/// 摄像机旋转震动反馈,通过 MMCinemachineRotationShakeEvent 触发现有的 Shaker。
|
||||
/// X/Y 作用于 FollowTarget 旋转,Z 作用于 Dutch 倾斜。
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class CameraRotationShakeAction : FeedbackActionBase
|
||||
{
|
||||
public override string DisplayName => "Camera Rotation Shake";
|
||||
|
||||
/// <summary>
|
||||
/// 震动曲线,定义震动强度随时间的变化。
|
||||
/// </summary>
|
||||
[Title("Rotation Shake")]
|
||||
[LabelText("Shake Curve")]
|
||||
public AnimationCurve shakeCurve = new AnimationCurve(
|
||||
new Keyframe(0f, 0f),
|
||||
new Keyframe(0.2f, 1f),
|
||||
new Keyframe(1f, 0f)
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// 最大旋转角度振幅(度)。X/Y -> FollowTarget, Z -> Dutch。
|
||||
/// </summary>
|
||||
[LabelText("Rotation Amplitude")]
|
||||
public Vector3 rotationAmplitude = new Vector3(2f, 2f, 5f);
|
||||
|
||||
/// <summary>
|
||||
/// 方向影响设置。
|
||||
/// </summary>
|
||||
[Title("Direction")]
|
||||
public CameraDirectionSettings directionSettings = new CameraDirectionSettings();
|
||||
|
||||
/// <summary>
|
||||
/// 距离衰减。
|
||||
/// </summary>
|
||||
[Title("Distance Attenuation")]
|
||||
[LabelText("Use Attenuation")]
|
||||
public bool useAttenuation;
|
||||
|
||||
[ShowIf("useAttenuation")]
|
||||
[LabelText("Attenuation Range")]
|
||||
public float attenuationRange = 50f;
|
||||
|
||||
[ShowIf("useAttenuation")]
|
||||
[LabelText("Attenuation Curve")]
|
||||
public AnimationCurve attenuationCurve = new AnimationCurve(
|
||||
new Keyframe(0f, 1f),
|
||||
new Keyframe(1f, 0f)
|
||||
);
|
||||
|
||||
public override void OnStart(FeedbackContext context)
|
||||
{
|
||||
Vector3 finalAmplitude = directionSettings.TransformAmplitude(rotationAmplitude, context.owner);
|
||||
float intensityMultiplier = ComputeAttenuation(context);
|
||||
|
||||
MMCinemachineRotationShakeEvent.Trigger(
|
||||
null,
|
||||
shakeCurve,
|
||||
context.duration,
|
||||
finalAmplitude,
|
||||
0f, 1f, false,
|
||||
intensityMultiplier
|
||||
);
|
||||
}
|
||||
|
||||
public override void OnInterrupt(FeedbackContext context)
|
||||
{
|
||||
MMCinemachineRotationShakeEvent.Trigger(
|
||||
null, shakeCurve, 0f, Vector3.zero,
|
||||
0f, 1f, false,
|
||||
stop: true
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 计算距离衰减系数。
|
||||
/// </summary>
|
||||
private float ComputeAttenuation(FeedbackContext context)
|
||||
{
|
||||
if (!useAttenuation || context.owner == null) return 1f;
|
||||
|
||||
Camera mainCamera = Camera.main;
|
||||
if (mainCamera == null) return 1f;
|
||||
|
||||
float distance = Vector3.Distance(context.owner.position, mainCamera.transform.position);
|
||||
float normalizedDistance = Mathf.Clamp01(distance / attenuationRange);
|
||||
return attenuationCurve.Evaluate(normalizedDistance);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,126 +0,0 @@
|
||||
using System;
|
||||
using Cielonos;
|
||||
using Sirenix.OdinInspector;
|
||||
using SLSUtilities.Feedback;
|
||||
using SLSUtilities.Rendering.PostProcessing;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Cielonos.MainGame.Effects.Feedback
|
||||
{
|
||||
/// <summary>
|
||||
/// 高级色散反馈动作,通过 PostProcessingManager 驱动 AdvancedChromaticAberration Volume 参数。
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class ChromaticAberrationAction : CurveShakeAction
|
||||
{
|
||||
public override string DisplayName => "Chromatic Aberration";
|
||||
|
||||
/// <summary>
|
||||
/// 是否同时修改中心点。
|
||||
/// </summary>
|
||||
[Title("Chromatic Aberration Settings")]
|
||||
[LabelText("Modify Center")]
|
||||
public bool modifyCenter;
|
||||
|
||||
[ShowIf("modifyCenter")]
|
||||
[LabelText("Center")]
|
||||
public Vector2 center = new Vector2(0.5f, 0.5f);
|
||||
|
||||
/// <summary>
|
||||
/// 是否同时修改抖动强度。
|
||||
/// </summary>
|
||||
[LabelText("Modify Jitter")]
|
||||
public bool modifyJitter;
|
||||
|
||||
[ShowIf("modifyJitter")]
|
||||
[LabelText("Jitter Curve")]
|
||||
public AnimationCurve jitterCurve = new AnimationCurve(
|
||||
new Keyframe(0f, 0f),
|
||||
new Keyframe(0.5f, 1f),
|
||||
new Keyframe(1f, 0f)
|
||||
);
|
||||
|
||||
[ShowIf("modifyJitter")]
|
||||
[LabelText("Jitter Remap Min")]
|
||||
public float jitterRemapMin;
|
||||
|
||||
[ShowIf("modifyJitter")]
|
||||
[LabelText("Jitter Remap Max")]
|
||||
public float jitterRemapMax = 0.5f;
|
||||
|
||||
[NonSerialized] private AdvancedChromaticAberration _aca;
|
||||
[NonSerialized] private float _initialIntensity;
|
||||
[NonSerialized] private Vector2 _initialCenter;
|
||||
[NonSerialized] private float _initialJitter;
|
||||
[NonSerialized] private bool _resolved;
|
||||
|
||||
public override void OnStart(FeedbackContext context)
|
||||
{
|
||||
_resolved = TryResolveComponent();
|
||||
if (!_resolved) return;
|
||||
|
||||
_initialIntensity = _aca.intensity.value;
|
||||
_initialCenter = _aca.center.value;
|
||||
_initialJitter = _aca.jitterIntensity.value;
|
||||
|
||||
if (modifyCenter)
|
||||
{
|
||||
_aca.center.value = center;
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnUpdate(FeedbackContext context, float normalizedTime)
|
||||
{
|
||||
if (!_resolved) return;
|
||||
|
||||
float newIntensity = EvaluateShake(normalizedTime, _initialIntensity);
|
||||
_aca.intensity.value = newIntensity;
|
||||
|
||||
if (modifyJitter)
|
||||
{
|
||||
float jitterValue = jitterCurve.Evaluate(normalizedTime);
|
||||
float mappedJitter = Mathf.LerpUnclamped(jitterRemapMin, jitterRemapMax, jitterValue);
|
||||
_aca.jitterIntensity.value = relativeToInitial ? _initialJitter + mappedJitter : mappedJitter;
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnEnd(FeedbackContext context)
|
||||
{
|
||||
RestoreValues();
|
||||
}
|
||||
|
||||
public override void OnInterrupt(FeedbackContext context)
|
||||
{
|
||||
RestoreValues();
|
||||
}
|
||||
|
||||
private bool TryResolveComponent()
|
||||
{
|
||||
if (_aca != null) return true;
|
||||
|
||||
if (PostProcessingManager.Instance == null)
|
||||
{
|
||||
Debug.LogWarning("[ChromaticAberrationAction] PostProcessingManager instance not found.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!PostProcessingManager.Instance.GetVolumeComponent(out _aca))
|
||||
{
|
||||
Debug.LogWarning("[ChromaticAberrationAction] AdvancedChromaticAberration not found in Volume Profile.");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void RestoreValues()
|
||||
{
|
||||
if (!_resolved) return;
|
||||
|
||||
_aca.intensity.value = _initialIntensity;
|
||||
_aca.center.value = _initialCenter;
|
||||
_aca.jitterIntensity.value = _initialJitter;
|
||||
_resolved = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5b5dc09ccaea7ab41847c4a59492bc44
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -51,7 +51,7 @@ namespace Cielonos.MainGame.Effects.Feedback
|
||||
|
||||
if (affectedByCameraDirection)
|
||||
{
|
||||
Camera mainCamera = Camera.main;
|
||||
Camera mainCamera = MainGameManager.Instance.player.viewSc.playerCamera;
|
||||
if (mainCamera != null)
|
||||
{
|
||||
return mainCamera.transform.TransformDirection(localAmplitude);
|
||||
@@ -0,0 +1,42 @@
|
||||
using System;
|
||||
using Sirenix.OdinInspector;
|
||||
using SLSUtilities.Feedback;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Cielonos.MainGame.Effects.Feedback
|
||||
{
|
||||
/// <summary>
|
||||
/// 摄像机视野角(FOV)反馈动作,通过 CameraFovShakeEvent 触发 CameraFovShaker。
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
[FeedbackActionColor(0.2f, 0.8f, 0.9f)]
|
||||
public class CameraFieldOfViewAction : CinemachineActionBase
|
||||
{
|
||||
public override string DisplayName => "Camera Field of View";
|
||||
|
||||
[TitleGroup("FOV设置")]
|
||||
[LabelText("FOV曲线")]
|
||||
public FloatCurveChannel fovCurve = FloatCurveChannel.CreateDefault(remapMax: 10f);
|
||||
|
||||
protected override void TriggerEvent(FeedbackContext context)
|
||||
{
|
||||
CameraFovShakeEvent.Trigger(context, fovCurve);
|
||||
}
|
||||
|
||||
protected override void StopEvent(FeedbackContext context)
|
||||
{
|
||||
CameraFovShakeEvent.Trigger(context, fovCurve, true);
|
||||
}
|
||||
|
||||
public override bool Validate(out string error)
|
||||
{
|
||||
if (!fovCurve.active)
|
||||
{
|
||||
error = "FOV curve is not enabled.";
|
||||
return false;
|
||||
}
|
||||
error = null;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6b01df3a292fc2748ab56454e289de8f
|
||||
@@ -0,0 +1,78 @@
|
||||
using System;
|
||||
using Sirenix.OdinInspector;
|
||||
using SLSUtilities.Feedback;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Cielonos.MainGame.Effects.Feedback
|
||||
{
|
||||
/// <summary>
|
||||
/// 摄像机位移震动反馈,通过 CameraPositionShakeEvent 触发 CinemachinePositionShaker。
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
[FeedbackActionColor(0.4f, 0.8f, 0.4f)]
|
||||
public class CameraPositionShakeAction : CinemachineActionBase
|
||||
{
|
||||
public override string DisplayName => "Camera Position Shake";
|
||||
|
||||
[TitleGroup("位移震动设置")]
|
||||
[LabelText("震动曲线")]
|
||||
public FloatCurveChannel intensityCurve = FloatCurveChannel.CreateDefault();
|
||||
|
||||
[TitleGroup("位移震动设置")]
|
||||
[LabelText("振幅")]
|
||||
public Vector3 amplitude = new Vector3(0.5f, 0.5f, 0f);
|
||||
|
||||
[TitleGroup("方向设置")]
|
||||
public CameraDirectionSettings directionSettings = new CameraDirectionSettings();
|
||||
|
||||
[TitleGroup("距离衰减")]
|
||||
[LabelText("启用衰减")]
|
||||
public bool useAttenuation;
|
||||
|
||||
[ShowIf("useAttenuation")]
|
||||
[LabelText("衰减范围")]
|
||||
public float attenuationRange = 50f;
|
||||
|
||||
[ShowIf("useAttenuation")]
|
||||
[LabelText("衰减曲线")]
|
||||
public AnimationCurve attenuationCurve = new AnimationCurve(
|
||||
new Keyframe(0f, 1f),
|
||||
new Keyframe(1f, 0f)
|
||||
);
|
||||
|
||||
protected override void TriggerEvent(FeedbackContext context)
|
||||
{
|
||||
Vector3 finalAmplitude = directionSettings.TransformAmplitude(amplitude, context.owner);
|
||||
float intensityMultiplier = ComputeAttenuation(context);
|
||||
CameraRotationShakeEvent.Trigger(context, intensityCurve, finalAmplitude * intensityMultiplier);
|
||||
}
|
||||
|
||||
protected override void StopEvent(FeedbackContext context)
|
||||
{
|
||||
CameraPositionShakeEvent.Trigger(context, intensityCurve, Vector3.zero, true);
|
||||
}
|
||||
|
||||
private float ComputeAttenuation(FeedbackContext context)
|
||||
{
|
||||
if (!useAttenuation || context.owner == null) return 1f;
|
||||
|
||||
Camera mainCamera = Camera.main;
|
||||
if (mainCamera == null) return 1f;
|
||||
|
||||
float distance = Vector3.Distance(context.owner.position, mainCamera.transform.position);
|
||||
float normalizedDistance = Mathf.Clamp01(distance / attenuationRange);
|
||||
return attenuationCurve.Evaluate(normalizedDistance);
|
||||
}
|
||||
|
||||
public override bool Validate(out string error)
|
||||
{
|
||||
if (!intensityCurve.active)
|
||||
{
|
||||
error = "Intensity curve is not enabled.";
|
||||
return false;
|
||||
}
|
||||
error = null;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
using System;
|
||||
using Sirenix.OdinInspector;
|
||||
using SLSUtilities.Feedback;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Cielonos.MainGame.Effects.Feedback
|
||||
{
|
||||
/// <summary>
|
||||
/// 摄像机旋转震动反馈,通过 CameraRotationShakeEvent 触发 CinemachineRotationShaker。
|
||||
/// X/Y 作用于 FollowTarget 旋转,Z 作用于 Dutch 倾斜。
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
[FeedbackActionColor(0.3f, 0.7f, 0.3f)]
|
||||
public class CameraRotationShakeAction : CinemachineActionBase
|
||||
{
|
||||
public override string DisplayName => "Camera Rotation Shake";
|
||||
|
||||
public Vector3 amplitude;
|
||||
|
||||
[LabelText("X轴曲线")]
|
||||
public FloatCurveChannel intensityCurve = FloatCurveChannel.CreateDefault(remapMax: 5f);
|
||||
|
||||
public CameraDirectionSettings directionSettings = new CameraDirectionSettings();
|
||||
|
||||
[TitleGroup("距离衰减")]
|
||||
[LabelText("启用衰减")]
|
||||
public bool useAttenuation;
|
||||
|
||||
[ShowIf("useAttenuation")]
|
||||
[LabelText("衰减范围")]
|
||||
public float attenuationRange = 50f;
|
||||
|
||||
[ShowIf("useAttenuation")]
|
||||
[LabelText("衰减曲线")]
|
||||
public AnimationCurve attenuationCurve = new AnimationCurve(
|
||||
new Keyframe(0f, 1f),
|
||||
new Keyframe(1f, 0f)
|
||||
);
|
||||
|
||||
protected override void TriggerEvent(FeedbackContext context)
|
||||
{
|
||||
Vector3 finalAmplitude = directionSettings.TransformAmplitude(amplitude, context.owner);
|
||||
float intensityMultiplier = ComputeAttenuation(context);
|
||||
CameraRotationShakeEvent.Trigger(context, intensityCurve, finalAmplitude * intensityMultiplier);
|
||||
}
|
||||
|
||||
protected override void StopEvent(FeedbackContext context)
|
||||
{
|
||||
CameraRotationShakeEvent.Trigger(context, intensityCurve, Vector3.zero, true);
|
||||
}
|
||||
|
||||
private float ComputeAttenuation(FeedbackContext context)
|
||||
{
|
||||
if (!useAttenuation || context.owner == null) return 1f;
|
||||
|
||||
Camera mainCamera = Camera.main;
|
||||
if (mainCamera == null) return 1f;
|
||||
|
||||
float distance = Vector3.Distance(context.owner.position, mainCamera.transform.position);
|
||||
float normalizedDistance = Mathf.Clamp01(distance / attenuationRange);
|
||||
return attenuationCurve.Evaluate(normalizedDistance);
|
||||
}
|
||||
|
||||
public override bool Validate(out string error)
|
||||
{
|
||||
error = null;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
using System;
|
||||
using Sirenix.OdinInspector;
|
||||
using SLSUtilities.Feedback;
|
||||
using Unity.Cinemachine;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Cielonos.MainGame.Effects.Feedback
|
||||
{
|
||||
/// <summary>
|
||||
/// Cinemachine Impulse 反馈,通过 CinemachineImpulseDefinition 直接创建脉冲事件。
|
||||
/// 需要场景中 Cinemachine Camera 上有 CinemachineImpulseListener 组件。
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class CinemachineImpulseAction : FeedbackActionBase
|
||||
{
|
||||
public override string DisplayName => "Cinemachine Impulse";
|
||||
|
||||
/// <summary>
|
||||
/// Impulse 定义,包含信号形状、衰减模式、持续时间等。
|
||||
/// </summary>
|
||||
[Title("Impulse Settings")]
|
||||
public CinemachineImpulseDefinition impulseDefinition = new CinemachineImpulseDefinition();
|
||||
|
||||
/// <summary>
|
||||
/// 脉冲速度向量。
|
||||
/// </summary>
|
||||
[LabelText("Velocity")]
|
||||
public Vector3 velocity = new Vector3(5f, 5f, 5f);
|
||||
|
||||
/// <summary>
|
||||
/// Stop 时是否清除所有 impulse。
|
||||
/// </summary>
|
||||
[LabelText("Clear Impulse on Stop")]
|
||||
public bool clearImpulseOnStop;
|
||||
|
||||
/// <summary>
|
||||
/// 方向影响设置。
|
||||
/// </summary>
|
||||
[Title("Direction")]
|
||||
public CameraDirectionSettings directionSettings = new CameraDirectionSettings();
|
||||
|
||||
public override void OnStart(FeedbackContext context)
|
||||
{
|
||||
Vector3 finalVelocity = directionSettings.TransformAmplitude(velocity, context.owner);
|
||||
Vector3 position = context.owner != null ? context.owner.position : Vector3.zero;
|
||||
|
||||
CinemachineImpulseManager.Instance.IgnoreTimeScale = true;
|
||||
impulseDefinition.CreateEvent(position, finalVelocity);
|
||||
}
|
||||
|
||||
public override void OnInterrupt(FeedbackContext context)
|
||||
{
|
||||
if (clearImpulseOnStop)
|
||||
{
|
||||
CinemachineImpulseManager.Instance.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4e30410247dced6409fff042f9c8828a
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 84b87c262bd85754b9840ceb6830c8ef
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,85 @@
|
||||
using System;
|
||||
using Sirenix.OdinInspector;
|
||||
using SLSUtilities.Feedback;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Cielonos.MainGame.Effects.Feedback
|
||||
{
|
||||
/// <summary>
|
||||
/// Anime ACES 反馈动作,通过 AnimeACESShakeEvent 触发 AnimeACESShaker。
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
[FeedbackActionColor(0.9f, 0.4f, 0.2f)]
|
||||
public class AnimeACESAction : PostprocessingActionBase
|
||||
{
|
||||
public override string DisplayName => "Anime ACES Tone";
|
||||
|
||||
[TitleGroup("曝光度")]
|
||||
[LabelText("修改曝光度")]
|
||||
public bool modifyExposure;
|
||||
|
||||
[ShowIf("modifyExposure")]
|
||||
[LabelText("曝光度曲线")]
|
||||
public FloatCurveChannel exposureChannel = FloatCurveChannel.CreateDefault();
|
||||
|
||||
[TitleGroup("对比度")]
|
||||
[LabelText("修改对比度")]
|
||||
public bool modifyContrast;
|
||||
|
||||
[ShowIf("modifyContrast")]
|
||||
[LabelText("对比度曲线")]
|
||||
public FloatCurveChannel contrastChannel = FloatCurveChannel.CreateDefault();
|
||||
|
||||
[TitleGroup("饱和度")]
|
||||
[LabelText("修改饱和度")]
|
||||
public bool modifySaturation;
|
||||
|
||||
[ShowIf("modifySaturation")]
|
||||
[LabelText("饱和度曲线")]
|
||||
public FloatCurveChannel saturationChannel = FloatCurveChannel.CreateDefault();
|
||||
|
||||
[TitleGroup("色相")]
|
||||
[LabelText("修改色相")]
|
||||
public bool modifyHue;
|
||||
|
||||
[ShowIf("modifyHue")]
|
||||
[LabelText("色相曲线")]
|
||||
public FloatCurveChannel hueChannel = FloatCurveChannel.CreateDefault();
|
||||
|
||||
[TitleGroup("颜色滤镜")]
|
||||
[LabelText("修改颜色滤镜")]
|
||||
public bool modifyColorFilter;
|
||||
|
||||
[ShowIf("modifyColorFilter")]
|
||||
[LabelText("颜色滤镜渐变")]
|
||||
public ColorCurveChannel colorFilterChannel = ColorCurveChannel.CreateDefault();
|
||||
|
||||
protected override void TriggerEvent(FeedbackContext context)
|
||||
{
|
||||
AnimeACESShakeEvent.Trigger(
|
||||
context,
|
||||
exposureChannel,
|
||||
contrastChannel,
|
||||
saturationChannel,
|
||||
hueChannel,
|
||||
colorFilterChannel
|
||||
);
|
||||
}
|
||||
|
||||
protected override void StopEvent(FeedbackContext context)
|
||||
{
|
||||
AnimeACESShakeEvent.Trigger(context, stop: true);
|
||||
}
|
||||
|
||||
public override bool Validate(out string error)
|
||||
{
|
||||
if (!modifyExposure && !modifyContrast && !modifySaturation && !modifyHue && !modifyColorFilter)
|
||||
{
|
||||
error = "No channel is enabled. Enable at least one channel.";
|
||||
return false;
|
||||
}
|
||||
error = null;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 81ca349cbc0a67e40bb42b89d4702a3c
|
||||
@@ -0,0 +1,10 @@
|
||||
using SLSUtilities.Feedback;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Cielonos.MainGame.Effects.Feedback
|
||||
{
|
||||
public class BloomAction : CurveShakeAction
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f30cb59f5ebeee44e99100865ced4e94
|
||||
@@ -0,0 +1,56 @@
|
||||
using System;
|
||||
using Sirenix.OdinInspector;
|
||||
using SLSUtilities.Feedback;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Cielonos.MainGame.Effects.Feedback
|
||||
{
|
||||
/// <summary>
|
||||
/// 高级色散反馈动作,通过 ChromaticAberrationShakeEvent 触发 ChromaticAberrationShaker。
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
[FeedbackActionColor(0.8f, 0.4f, 0.8f)]
|
||||
public class ChromaticAberrationAction : PostprocessingActionBase
|
||||
{
|
||||
public override string DisplayName => "Chromatic Aberration";
|
||||
|
||||
public FloatCurveChannel intensityCurve = FloatCurveChannel.CreateDefault(remapMax: 1f);
|
||||
|
||||
/// <summary>
|
||||
/// 是否同时修改中心点。
|
||||
/// </summary>
|
||||
[LabelText("修改中心点")]
|
||||
public bool modifyCenter;
|
||||
|
||||
[ShowIf("modifyCenter")]
|
||||
[LabelText("中心点曲线")]
|
||||
public Vector2CurveChannel centerCurve = Vector2CurveChannel.CreateDefault();
|
||||
|
||||
/// <summary>
|
||||
/// 是否同时修改抖动强度。
|
||||
/// </summary>
|
||||
[LabelText("修改抖动")]
|
||||
public bool modifyJitter;
|
||||
|
||||
[ShowIf("modifyJitter")]
|
||||
[LabelText("抖动曲线")]
|
||||
public FloatCurveChannel jitterCurve = FloatCurveChannel.CreateDefault(remapMax: 0.5f);
|
||||
|
||||
protected override void TriggerEvent(FeedbackContext context)
|
||||
{
|
||||
ChromaticAberrationShakeEvent.Trigger(
|
||||
context,
|
||||
intensityCurve,
|
||||
modifyCenter,
|
||||
centerCurve,
|
||||
modifyJitter,
|
||||
jitterCurve
|
||||
);
|
||||
}
|
||||
|
||||
protected override void StopEvent(FeedbackContext context)
|
||||
{
|
||||
ChromaticAberrationShakeEvent.Trigger(context, intensityCurve, stop: true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
using System;
|
||||
using Sirenix.OdinInspector;
|
||||
using SLSUtilities.Feedback;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Cielonos.MainGame.Effects.Feedback
|
||||
{
|
||||
/// <summary>
|
||||
/// 径向模糊反馈动作,通过 RadialBlurShakeEvent 触发 RadialBlurShaker。
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
[FeedbackActionColor(0.6f, 0.4f, 0.9f)]
|
||||
public class RadialBlurAction : PostprocessingActionBase
|
||||
{
|
||||
public override string DisplayName => "Radial Blur";
|
||||
|
||||
public FloatCurveChannel intensityCurve = FloatCurveChannel.CreateDefault(remapMax: 1f);
|
||||
|
||||
/// <summary>
|
||||
/// 是否修改模糊中心点。关闭时保持 Volume 当前设置。
|
||||
/// </summary>
|
||||
[LabelText("修改中心点")]
|
||||
public bool modifyCenter;
|
||||
|
||||
/// <summary>
|
||||
/// 模糊中心的屏幕坐标 (0-1)。(0.5, 0.5) 为屏幕正中心。
|
||||
/// </summary>
|
||||
[ShowIf("modifyCenter")]
|
||||
[LabelText("中心点")]
|
||||
public Vector2 center = new Vector2(0.5f, 0.5f);
|
||||
|
||||
protected override void TriggerEvent(FeedbackContext context)
|
||||
{
|
||||
RadialBlurShakeEvent.Trigger(context, intensityCurve, modifyCenter, center);
|
||||
}
|
||||
|
||||
protected override void StopEvent(FeedbackContext context)
|
||||
{
|
||||
RadialBlurShakeEvent.Trigger(context, default, stop: true);
|
||||
}
|
||||
|
||||
public override bool Validate(out string error)
|
||||
{
|
||||
error = null;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
using System;
|
||||
using Sirenix.OdinInspector;
|
||||
using SLSUtilities.Feedback;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Cielonos.MainGame.Effects.Feedback
|
||||
{
|
||||
/// <summary>
|
||||
/// 黑白闪反馈动作,通过 StrobeFlashShakeEvent 触发 StrobeFlashShaker。
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
[FeedbackActionColor(1.0f, 0.9f, 0.3f)]
|
||||
public class StrobeFlashAction : PostprocessingActionBase
|
||||
{
|
||||
public override string DisplayName => "Strobe Flash";
|
||||
|
||||
/// <summary>
|
||||
/// 是否修改频率和颜色参数。
|
||||
/// </summary>
|
||||
[TitleGroup("闪烁设置")]
|
||||
[LabelText("修改额外参数")]
|
||||
public bool modifyExtra;
|
||||
|
||||
[ShowIf("modifyExtra")]
|
||||
[LabelText("频率曲线")]
|
||||
public FloatCurveChannel frequencyCurve = FloatCurveChannel.CreateDefault(remapMax: 15f);
|
||||
|
||||
[ShowIf("modifyExtra")]
|
||||
[LabelText("高颜色")]
|
||||
public ColorCurveChannel colorHigh = ColorCurveChannel.CreateDefault();
|
||||
|
||||
[ShowIf("modifyExtra")]
|
||||
[LabelText("低颜色")]
|
||||
public ColorCurveChannel colorLow = ColorCurveChannel.CreateDefault();
|
||||
|
||||
protected override void TriggerEvent(FeedbackContext context)
|
||||
{
|
||||
StrobeFlashShakeEvent.Trigger(
|
||||
context,
|
||||
context.duration,
|
||||
modifyExtra,
|
||||
frequencyCurve,
|
||||
colorHigh,
|
||||
colorLow
|
||||
);
|
||||
}
|
||||
|
||||
protected override void StopEvent(FeedbackContext context)
|
||||
{
|
||||
StrobeFlashShakeEvent.Trigger(context, 0f, stop: true);
|
||||
}
|
||||
|
||||
public override bool Validate(out string error)
|
||||
{
|
||||
error = null;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
using System;
|
||||
using Sirenix.OdinInspector;
|
||||
using SLSUtilities.Feedback;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Cielonos.MainGame.Effects.Feedback
|
||||
{
|
||||
/// <summary>
|
||||
/// 高级暗角反馈动作,通过 VignetteShakeEvent 触发 VignetteShaker。
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
[FeedbackActionColor(0.9f, 0.5f, 0.3f)]
|
||||
public class VignetteAction : PostprocessingActionBase
|
||||
{
|
||||
public override string DisplayName => "Vignette";
|
||||
|
||||
|
||||
public FloatCurveChannel intensityCurve = FloatCurveChannel.CreateDefault(remapMax: 1f);
|
||||
|
||||
/// <summary>
|
||||
/// 是否修改暗角中心点。
|
||||
/// </summary>
|
||||
[LabelText("修改中心点")]
|
||||
public bool modifyCenter;
|
||||
|
||||
/// <summary>
|
||||
/// 模糊中心的屏幕坐标 (0-1)。(0.5, 0.5) 为屏幕正中心。
|
||||
/// </summary>
|
||||
[HideIf("modifyCenter")]
|
||||
[LabelText("中心点")]
|
||||
public Vector2 center = new Vector2(0.5f, 0.5f);
|
||||
|
||||
[ShowIf("modifyCenter")]
|
||||
public Vector2CurveChannel centerCurve = Vector2CurveChannel.CreateDefault();
|
||||
|
||||
/// <summary>
|
||||
/// 是否修改颜色。
|
||||
/// </summary>
|
||||
[LabelText("修改颜色")]
|
||||
public bool modifyColors;
|
||||
|
||||
[HideIf("modifyColors")]
|
||||
public Color outColor;
|
||||
|
||||
[HideIf("modifyColors")]
|
||||
public Color innerColor;
|
||||
|
||||
/// <summary>
|
||||
/// 外圈颜色。
|
||||
/// </summary>
|
||||
[ShowIf("modifyColors")]
|
||||
[LabelText("外圈颜色")]
|
||||
public ColorCurveChannel outerColorCurve = ColorCurveChannel.CreateDefault();
|
||||
|
||||
/// <summary>
|
||||
/// 内圈颜色。
|
||||
/// </summary>
|
||||
[ShowIf("modifyColors")]
|
||||
[LabelText("内圈颜色")]
|
||||
public ColorCurveChannel innerColorCurve = ColorCurveChannel.CreateDefault();
|
||||
|
||||
/// <summary>
|
||||
/// 是否修改形状。
|
||||
/// </summary>
|
||||
[LabelText("修改形状")]
|
||||
public bool modifyShape;
|
||||
|
||||
[HideIf("modifyShape")]
|
||||
public float smoothness;
|
||||
|
||||
[HideIf("modifyShape")]
|
||||
public float roundness;
|
||||
|
||||
/// <summary>
|
||||
/// 柔和度曲线。
|
||||
/// </summary>
|
||||
[ShowIf("modifyShape")]
|
||||
[LabelText("柔和度曲线")]
|
||||
public FloatCurveChannel smoothnessCurve = FloatCurveChannel.CreateDefault(remapMax: 0.5f);
|
||||
|
||||
/// <summary>
|
||||
/// 圆度曲线。
|
||||
/// </summary>
|
||||
[ShowIf("modifyShape")]
|
||||
[LabelText("圆度曲线")]
|
||||
public FloatCurveChannel roundnessCurve = FloatCurveChannel.CreateDefault(remapMax: 1f);
|
||||
|
||||
protected override void TriggerEvent(FeedbackContext context)
|
||||
{
|
||||
VignetteShakeEvent.Trigger(
|
||||
context,
|
||||
intensityCurve,
|
||||
modifyCenter,
|
||||
center,
|
||||
modifyColors,
|
||||
outerColorCurve,
|
||||
innerColorCurve,
|
||||
modifyShape,
|
||||
smoothnessCurve,
|
||||
roundnessCurve
|
||||
);
|
||||
}
|
||||
|
||||
protected override void StopEvent(FeedbackContext context)
|
||||
{
|
||||
VignetteShakeEvent.Trigger(context, intensityCurve, stop: true);
|
||||
}
|
||||
|
||||
public override bool Validate(out string error)
|
||||
{
|
||||
error = null;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,123 +0,0 @@
|
||||
using System;
|
||||
using Cielonos;
|
||||
using Sirenix.OdinInspector;
|
||||
using SLSUtilities.Feedback;
|
||||
using SLSUtilities.Rendering.PostProcessing;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Cielonos.MainGame.Effects.Feedback
|
||||
{
|
||||
/// <summary>
|
||||
/// 径向模糊反馈动作,通过 PostProcessingManager 驱动 RadialBlur Volume 参数。
|
||||
/// 继承 CurveShakeAction 获得曲线采样和初始值管理能力。
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class RadialBlurAction : CurveShakeAction
|
||||
{
|
||||
public override string DisplayName => "Radial Blur";
|
||||
|
||||
/// <summary>
|
||||
/// 是否修改模糊中心点。关闭时保持 Volume 当前设置(通常为 0.5, 0.5)。
|
||||
/// </summary>
|
||||
[Title("Radial Blur Settings")]
|
||||
[LabelText("Modify Center")]
|
||||
public bool modifyCenter;
|
||||
|
||||
/// <summary>
|
||||
/// 模糊中心的屏幕坐标 (0-1)。(0.5, 0.5) 为屏幕正中心。
|
||||
/// </summary>
|
||||
[ShowIf("modifyCenter")]
|
||||
[LabelText("Center")]
|
||||
public Vector2 center = new Vector2(0.5f, 0.5f);
|
||||
|
||||
// 运行时缓存
|
||||
[NonSerialized] private RadialBlur _radialBlur;
|
||||
[NonSerialized] private float _initialBlurRadius;
|
||||
[NonSerialized] private float _initialCenterX;
|
||||
[NonSerialized] private float _initialCenterY;
|
||||
[NonSerialized] private bool _resolved;
|
||||
|
||||
public override void OnStart(FeedbackContext context)
|
||||
{
|
||||
_resolved = TryResolveComponent();
|
||||
if (!_resolved) return;
|
||||
|
||||
// 记录初始值用于复位
|
||||
_initialBlurRadius = _radialBlur.blurRadius.value;
|
||||
_initialCenterX = _radialBlur.radialCenterX.value;
|
||||
_initialCenterY = _radialBlur.radialCenterY.value;
|
||||
|
||||
// 设置中心点(整个 Clip 期间保持不变)
|
||||
if (modifyCenter)
|
||||
{
|
||||
_radialBlur.radialCenterX.value = center.x;
|
||||
_radialBlur.radialCenterY.value = center.y;
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnUpdate(FeedbackContext context, float normalizedTime)
|
||||
{
|
||||
if (!_resolved) return;
|
||||
|
||||
float newRadius = EvaluateShake(normalizedTime, _initialBlurRadius);
|
||||
_radialBlur.blurRadius.value = newRadius;
|
||||
}
|
||||
|
||||
public override void OnEnd(FeedbackContext context)
|
||||
{
|
||||
RestoreValues();
|
||||
}
|
||||
|
||||
public override void OnInterrupt(FeedbackContext context)
|
||||
{
|
||||
RestoreValues();
|
||||
}
|
||||
|
||||
public override bool Validate(out string error)
|
||||
{
|
||||
if (PostProcessingManager.Instance == null)
|
||||
{
|
||||
error = "PostProcessingManager instance not found in scene.";
|
||||
return false;
|
||||
}
|
||||
|
||||
error = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 尝试从 PostProcessingManager 获取 RadialBlur Volume 组件。
|
||||
/// </summary>
|
||||
private bool TryResolveComponent()
|
||||
{
|
||||
if (_radialBlur != null) return true;
|
||||
|
||||
if (PostProcessingManager.Instance == null)
|
||||
{
|
||||
Debug.LogWarning("[RadialBlurAction] PostProcessingManager instance not found.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!PostProcessingManager.Instance.GetVolumeComponent(out _radialBlur))
|
||||
{
|
||||
Debug.LogWarning("[RadialBlurAction] RadialBlur component not found in Volume Profile.");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 恢复到 OnStart 时记录的初始值。
|
||||
/// </summary>
|
||||
private void RestoreValues()
|
||||
{
|
||||
if (!_resolved) return;
|
||||
|
||||
_radialBlur.blurRadius.value = _initialBlurRadius;
|
||||
_radialBlur.radialCenterX.value = _initialCenterX;
|
||||
_radialBlur.radialCenterY.value = _initialCenterY;
|
||||
_resolved = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,115 +0,0 @@
|
||||
using System;
|
||||
using Cielonos;
|
||||
using Sirenix.OdinInspector;
|
||||
using SLSUtilities.Feedback;
|
||||
using SLSUtilities.Rendering.PostProcessing;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Cielonos.MainGame.Effects.Feedback
|
||||
{
|
||||
/// <summary>
|
||||
/// 黑白闪反馈动作,在 Clip 持续时间内开启 StrobeFlash 的 AutoFlash,
|
||||
/// Clip 结束或被打断时自动关闭。
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class StrobeFlashAction : FeedbackActionBase
|
||||
{
|
||||
public override string DisplayName => "Strobe Flash";
|
||||
|
||||
/// <summary>
|
||||
/// 是否修改频率和颜色参数。
|
||||
/// </summary>
|
||||
[Title("Strobe Settings")]
|
||||
[LabelText("Modify Extra")]
|
||||
public bool modifyExtra;
|
||||
|
||||
[ShowIf("modifyExtra")]
|
||||
[LabelText("Frequency")]
|
||||
public float frequency = 15f;
|
||||
|
||||
[ShowIf("modifyExtra")]
|
||||
[LabelText("Color High")]
|
||||
public Color colorHigh = Color.white;
|
||||
|
||||
[ShowIf("modifyExtra")]
|
||||
[LabelText("Color Low")]
|
||||
public Color colorLow = Color.black;
|
||||
|
||||
[NonSerialized] private StrobeFlash _strobeFlash;
|
||||
[NonSerialized] private bool _initialEnable;
|
||||
[NonSerialized] private bool _initialAutoFlash;
|
||||
[NonSerialized] private float _initialFrequency;
|
||||
[NonSerialized] private Color _initialColorHigh;
|
||||
[NonSerialized] private Color _initialColorLow;
|
||||
[NonSerialized] private bool _resolved;
|
||||
|
||||
public override void OnStart(FeedbackContext context)
|
||||
{
|
||||
_resolved = TryResolveComponent();
|
||||
if (!_resolved) return;
|
||||
|
||||
_initialEnable = _strobeFlash.enableEffect.value;
|
||||
_initialAutoFlash = _strobeFlash.autoFlash.value;
|
||||
_initialFrequency = _strobeFlash.frequency.value;
|
||||
_initialColorHigh = _strobeFlash.colorHigh.value;
|
||||
_initialColorLow = _strobeFlash.colorLow.value;
|
||||
|
||||
_strobeFlash.enableEffect.value = true;
|
||||
_strobeFlash.autoFlash.value = true;
|
||||
|
||||
if (modifyExtra)
|
||||
{
|
||||
_strobeFlash.frequency.value = frequency;
|
||||
_strobeFlash.colorHigh.value = colorHigh;
|
||||
_strobeFlash.colorLow.value = colorLow;
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnUpdate(FeedbackContext context, float normalizedTime)
|
||||
{
|
||||
// StrobeFlash 由 Shader 内部的 _Time 驱动自动闪烁,
|
||||
// Action 只负责开关控制,不需要每帧更新。
|
||||
}
|
||||
|
||||
public override void OnEnd(FeedbackContext context)
|
||||
{
|
||||
RestoreValues();
|
||||
}
|
||||
|
||||
public override void OnInterrupt(FeedbackContext context)
|
||||
{
|
||||
RestoreValues();
|
||||
}
|
||||
|
||||
private bool TryResolveComponent()
|
||||
{
|
||||
if (_strobeFlash != null) return true;
|
||||
|
||||
if (PostProcessingManager.Instance == null)
|
||||
{
|
||||
Debug.LogWarning("[StrobeFlashAction] PostProcessingManager instance not found.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!PostProcessingManager.Instance.GetVolumeComponent(out _strobeFlash))
|
||||
{
|
||||
Debug.LogWarning("[StrobeFlashAction] StrobeFlash not found in Volume Profile.");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void RestoreValues()
|
||||
{
|
||||
if (!_resolved) return;
|
||||
|
||||
_strobeFlash.enableEffect.value = _initialEnable;
|
||||
_strobeFlash.autoFlash.value = _initialAutoFlash;
|
||||
_strobeFlash.frequency.value = _initialFrequency;
|
||||
_strobeFlash.colorHigh.value = _initialColorHigh;
|
||||
_strobeFlash.colorLow.value = _initialColorLow;
|
||||
_resolved = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e33332fa07bdfcd459b3bf3350c11299
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using Cielonos.MainGame;
|
||||
using Sirenix.OdinInspector;
|
||||
using SLSUtilities.Feedback;
|
||||
using UnityEngine;
|
||||
@@ -31,12 +30,14 @@ namespace Cielonos.MainGame.Effects.Feedback
|
||||
/// <summary>
|
||||
/// 是否激活此通道。
|
||||
/// </summary>
|
||||
[HorizontalGroup("Channel")]
|
||||
public bool active;
|
||||
|
||||
/// <summary>
|
||||
/// 通道工作模式。
|
||||
/// </summary>
|
||||
[ShowIf("active")]
|
||||
[HorizontalGroup("Channel")]
|
||||
public TimeScaleMode mode = TimeScaleMode.Fixed;
|
||||
|
||||
/// <summary>
|
||||
@@ -51,6 +52,7 @@ namespace Cielonos.MainGame.Effects.Feedback
|
||||
/// </summary>
|
||||
[ShowIf("@active && mode == TimeScaleMode.Dynamic")]
|
||||
[LabelText("Curve")]
|
||||
[ShakeCurvePreset]
|
||||
public AnimationCurve curve = new AnimationCurve(
|
||||
new Keyframe(0f, 0f),
|
||||
new Keyframe(0.5f, 1f),
|
||||
@@ -62,6 +64,7 @@ namespace Cielonos.MainGame.Effects.Feedback
|
||||
/// </summary>
|
||||
[ShowIf("@active && mode == TimeScaleMode.Dynamic")]
|
||||
[LabelText("Remap Zero")]
|
||||
[HorizontalGroup("Ramp")]
|
||||
public float remapZero;
|
||||
|
||||
/// <summary>
|
||||
@@ -69,11 +72,25 @@ namespace Cielonos.MainGame.Effects.Feedback
|
||||
/// </summary>
|
||||
[ShowIf("@active && mode == TimeScaleMode.Dynamic")]
|
||||
[LabelText("Remap One")]
|
||||
[HorizontalGroup("Ramp")]
|
||||
public float remapOne = 1f;
|
||||
|
||||
/// <summary>
|
||||
/// 根据归一化进度计算当前通道的时间缩放值。
|
||||
/// 将此通道的配置转换为事件传输用的 TimeScaleChannelData。
|
||||
/// </summary>
|
||||
public TimeScaleChannelData ToChannelData()
|
||||
{
|
||||
return new TimeScaleChannelData
|
||||
{
|
||||
active = active,
|
||||
mode = mode,
|
||||
fixedValue = fixedValue,
|
||||
curve = curve,
|
||||
remapZero = remapZero,
|
||||
remapOne = remapOne
|
||||
};
|
||||
}
|
||||
|
||||
public float Evaluate(float normalizedTime)
|
||||
{
|
||||
if (!active) return 1f;
|
||||
@@ -83,94 +100,114 @@ namespace Cielonos.MainGame.Effects.Feedback
|
||||
return fixedValue;
|
||||
}
|
||||
|
||||
float curveValue = curve.Evaluate(normalizedTime);
|
||||
float curveValue = curve?.Evaluate(normalizedTime) ?? 0f;
|
||||
return Mathf.LerpUnclamped(remapZero, remapOne, curveValue);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 时间缩放修改器反馈,直接驱动 TimeManager 的各个通道。
|
||||
/// 时间缩放修改器反馈,通过 TimeScaleShakeEvent 触发 TimeScaleShaker。
|
||||
/// Shaker 负责管理多个并发时间缩放实例的叠加混合和初始值恢复。
|
||||
///
|
||||
/// 重要:此 Action 只应使用游戏的 unscaledDeltaTime 驱动。
|
||||
/// 重要:此 Action 会忽略时间缩放,使用未缩放的 deltaTime 驱动。
|
||||
/// 当 Time.timeScale == 0 时,此 Action 也会暂停。
|
||||
/// 不要在包含此 Action 的 Clip 上启用自定义 overrideTimeSettings,
|
||||
/// FeedbackData 的 defaultTimeSettings.useTimeScale 也应保持为 false。
|
||||
/// 我们的自定义时间参数绝不能影响时间缩放修改器本身。
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
[FeedbackActionColor(0.3f, 0.7f, 1.0f)]
|
||||
public class TimeScaleModifierAction : FeedbackActionBase
|
||||
{
|
||||
public override string DisplayName => "Time Scale Modifier";
|
||||
|
||||
/// <summary>
|
||||
/// 忽略时间缩放,使用未缩放的 deltaTime。
|
||||
/// </summary>
|
||||
public override bool IgnoreTimeScale => true;
|
||||
|
||||
public TimeScaleChannel globalChannel = new TimeScaleChannel { active = true, fixedValue = 0.1f };
|
||||
|
||||
[Title("Global Time Scale")]
|
||||
public TimeScaleChannel globalChannel = new TimeScaleChannel { active = true, fixedValue = 0f };
|
||||
|
||||
[Title("Player Time Scale")]
|
||||
public bool advancedSettings = false;
|
||||
|
||||
[ShowIf("advancedSettings")]
|
||||
public TimeScaleChannel playerChannel = new TimeScaleChannel();
|
||||
|
||||
[Title("Enemy Time Scale")]
|
||||
|
||||
[ShowIf("advancedSettings")]
|
||||
public TimeScaleChannel enemyChannel = new TimeScaleChannel();
|
||||
|
||||
[Title("Allied Time Scale")]
|
||||
|
||||
[ShowIf("advancedSettings")]
|
||||
public TimeScaleChannel alliedChannel = new TimeScaleChannel();
|
||||
|
||||
[Title("Non-Player Time Scale")]
|
||||
|
||||
[ShowIf("advancedSettings")]
|
||||
public TimeScaleChannel nonPlayerChannel = new TimeScaleChannel();
|
||||
|
||||
[NonSerialized] private float _initialGlobal;
|
||||
[NonSerialized] private float _initialPlayer;
|
||||
[NonSerialized] private float _initialEnemy;
|
||||
[NonSerialized] private float _initialAllied;
|
||||
[NonSerialized] private float _initialNonPlayer;
|
||||
|
||||
public override void OnStart(FeedbackContext context)
|
||||
{
|
||||
if (TimeManager.Instance == null)
|
||||
// 通过事件触发,让TimeScaleShaker注册这个实例
|
||||
TimeScaleShakeEvent.Trigger(
|
||||
duration: context.duration,
|
||||
global: globalChannel.ToChannelData(),
|
||||
player: playerChannel.ToChannelData(),
|
||||
enemy: enemyChannel.ToChannelData(),
|
||||
allied: alliedChannel.ToChannelData(),
|
||||
nonPlayer: nonPlayerChannel.ToChannelData()
|
||||
);
|
||||
|
||||
// 立即执行一次TimeScaleShaker的更新
|
||||
// 这样在同一帧内,TimeScaleModifierAction修改的globalTimeScale就能立即生效
|
||||
ImmediateApplyTimeScale();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 立即应用时间缩放,确保在同一帧内立即生效
|
||||
/// </summary>
|
||||
private void ImmediateApplyTimeScale()
|
||||
{
|
||||
if (TimeManager.Instance == null) return;
|
||||
|
||||
if (globalChannel.active)
|
||||
{
|
||||
Debug.LogWarning("[TimeScaleModifierAction] TimeManager instance not found.");
|
||||
return;
|
||||
TimeManager.Instance.globalTimeScale.Value = globalChannel.Evaluate(0);
|
||||
}
|
||||
|
||||
if (playerChannel.active)
|
||||
{
|
||||
TimeManager.Instance.playerTimeScale.Value = playerChannel.Evaluate(0);
|
||||
}
|
||||
|
||||
if (enemyChannel.active)
|
||||
{
|
||||
TimeManager.Instance.enemyTimeScale.Value = enemyChannel.Evaluate(0);
|
||||
}
|
||||
|
||||
if (alliedChannel.active)
|
||||
{
|
||||
TimeManager.Instance.alliedMinionTimeScale.Value = alliedChannel.Evaluate(0);
|
||||
}
|
||||
|
||||
if (nonPlayerChannel.active)
|
||||
{
|
||||
TimeManager.Instance.nonPlayerTimeScale.Value = nonPlayerChannel.Evaluate(0);
|
||||
}
|
||||
|
||||
_initialGlobal = TimeManager.Instance.globalTimeScale.Value;
|
||||
_initialPlayer = TimeManager.Instance.playerTimeScale.Value;
|
||||
_initialEnemy = TimeManager.Instance.enemyTimeScale.Value;
|
||||
_initialAllied = TimeManager.Instance.alliedMinionTimeScale.Value;
|
||||
_initialNonPlayer = TimeManager.Instance.nonPlayerTimeScale.Value;
|
||||
}
|
||||
|
||||
public override void OnUpdate(FeedbackContext context, float normalizedTime)
|
||||
{
|
||||
if (TimeManager.Instance == null) return;
|
||||
|
||||
if (globalChannel.active)
|
||||
TimeManager.Instance.globalTimeScale.Value = globalChannel.Evaluate(normalizedTime);
|
||||
|
||||
if (playerChannel.active)
|
||||
TimeManager.Instance.playerTimeScale.Value = playerChannel.Evaluate(normalizedTime);
|
||||
|
||||
if (enemyChannel.active)
|
||||
TimeManager.Instance.enemyTimeScale.Value = enemyChannel.Evaluate(normalizedTime);
|
||||
|
||||
if (alliedChannel.active)
|
||||
TimeManager.Instance.alliedMinionTimeScale.Value = alliedChannel.Evaluate(normalizedTime);
|
||||
|
||||
if (nonPlayerChannel.active)
|
||||
TimeManager.Instance.nonPlayerTimeScale.Value = nonPlayerChannel.Evaluate(normalizedTime);
|
||||
// Shaker 自行每帧驱动所有活跃实例。
|
||||
}
|
||||
|
||||
public override void OnEnd(FeedbackContext context)
|
||||
{
|
||||
RestoreValues();
|
||||
// Shaker 自动管理实例生命周期和初始值恢复。
|
||||
}
|
||||
|
||||
public override void OnInterrupt(FeedbackContext context)
|
||||
{
|
||||
RestoreValues();
|
||||
TimeScaleShakeEvent.Trigger(0f, stop: true);
|
||||
}
|
||||
|
||||
public override bool Validate(out string error)
|
||||
{
|
||||
// 防呆检查:时间缩放修改器不应受自定义时间缩放影响
|
||||
// 此检查在 Editor 中调用,完整的 Inspector 防呆将在后续版本中添加
|
||||
bool anyActive = globalChannel.active || playerChannel.active ||
|
||||
enemyChannel.active || alliedChannel.active ||
|
||||
nonPlayerChannel.active;
|
||||
@@ -184,25 +221,5 @@ namespace Cielonos.MainGame.Effects.Feedback
|
||||
error = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
private void RestoreValues()
|
||||
{
|
||||
if (TimeManager.Instance == null) return;
|
||||
|
||||
if (globalChannel.active)
|
||||
TimeManager.Instance.globalTimeScale.Value = _initialGlobal;
|
||||
|
||||
if (playerChannel.active)
|
||||
TimeManager.Instance.playerTimeScale.Value = _initialPlayer;
|
||||
|
||||
if (enemyChannel.active)
|
||||
TimeManager.Instance.enemyTimeScale.Value = _initialEnemy;
|
||||
|
||||
if (alliedChannel.active)
|
||||
TimeManager.Instance.alliedMinionTimeScale.Value = _initialAllied;
|
||||
|
||||
if (nonPlayerChannel.active)
|
||||
TimeManager.Instance.nonPlayerTimeScale.Value = _initialNonPlayer;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,147 +0,0 @@
|
||||
using System;
|
||||
using Cielonos;
|
||||
using Sirenix.OdinInspector;
|
||||
using SLSUtilities.Feedback;
|
||||
using SLSUtilities.Rendering.PostProcessing;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Cielonos.MainGame.Effects.Feedback
|
||||
{
|
||||
/// <summary>
|
||||
/// 高级暗角反馈动作,通过 PostProcessingManager 驱动 AdvancedVignette Volume 参数。
|
||||
/// 可用于受击暗角、环境压抑等效果。
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class VignetteAction : CurveShakeAction
|
||||
{
|
||||
public override string DisplayName => "Vignette";
|
||||
|
||||
/// <summary>
|
||||
/// 是否修改暗角中心点。
|
||||
/// </summary>
|
||||
[Title("Vignette Settings")]
|
||||
[LabelText("Modify Center")]
|
||||
public bool modifyCenter;
|
||||
|
||||
[ShowIf("modifyCenter")]
|
||||
[LabelText("Center")]
|
||||
public Vector2 center = new Vector2(0.5f, 0.5f);
|
||||
|
||||
/// <summary>
|
||||
/// 是否修改颜色。
|
||||
/// </summary>
|
||||
[LabelText("Modify Colors")]
|
||||
public bool modifyColors;
|
||||
|
||||
[ShowIf("modifyColors")]
|
||||
[LabelText("Color Outer")]
|
||||
public Color colorOuter = Color.black;
|
||||
|
||||
[ShowIf("modifyColors")]
|
||||
[LabelText("Color Inner")]
|
||||
public Color colorInner = Color.black;
|
||||
|
||||
/// <summary>
|
||||
/// 是否修改柔和度和圆度。
|
||||
/// </summary>
|
||||
[LabelText("Modify Shape")]
|
||||
public bool modifyShape;
|
||||
|
||||
[ShowIf("modifyShape")]
|
||||
[LabelText("Smoothness")]
|
||||
[Range(0.01f, 1f)]
|
||||
public float smoothness = 0.5f;
|
||||
|
||||
[ShowIf("modifyShape")]
|
||||
[LabelText("Roundness")]
|
||||
[Range(0f, 1f)]
|
||||
public float roundness = 1f;
|
||||
|
||||
[NonSerialized] private AdvancedVignette _vignette;
|
||||
[NonSerialized] private float _initialIntensity;
|
||||
[NonSerialized] private Vector2 _initialCenter;
|
||||
[NonSerialized] private Color _initialColorOuter;
|
||||
[NonSerialized] private Color _initialColorInner;
|
||||
[NonSerialized] private float _initialSmoothness;
|
||||
[NonSerialized] private float _initialRoundness;
|
||||
[NonSerialized] private bool _resolved;
|
||||
|
||||
public override void OnStart(FeedbackContext context)
|
||||
{
|
||||
_resolved = TryResolveComponent();
|
||||
if (!_resolved) return;
|
||||
|
||||
_initialIntensity = _vignette.intensity.value;
|
||||
_initialCenter = _vignette.center.value;
|
||||
_initialColorOuter = _vignette.colorOuter.value;
|
||||
_initialColorInner = _vignette.colorInner.value;
|
||||
_initialSmoothness = _vignette.smoothness.value;
|
||||
_initialRoundness = _vignette.roundness.value;
|
||||
|
||||
if (modifyCenter)
|
||||
_vignette.center.value = center;
|
||||
|
||||
if (modifyColors)
|
||||
{
|
||||
_vignette.colorOuter.value = colorOuter;
|
||||
_vignette.colorInner.value = colorInner;
|
||||
}
|
||||
|
||||
if (modifyShape)
|
||||
{
|
||||
_vignette.smoothness.value = smoothness;
|
||||
_vignette.roundness.value = roundness;
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnUpdate(FeedbackContext context, float normalizedTime)
|
||||
{
|
||||
if (!_resolved) return;
|
||||
|
||||
float newIntensity = EvaluateShake(normalizedTime, _initialIntensity);
|
||||
_vignette.intensity.value = newIntensity;
|
||||
}
|
||||
|
||||
public override void OnEnd(FeedbackContext context)
|
||||
{
|
||||
RestoreValues();
|
||||
}
|
||||
|
||||
public override void OnInterrupt(FeedbackContext context)
|
||||
{
|
||||
RestoreValues();
|
||||
}
|
||||
|
||||
private bool TryResolveComponent()
|
||||
{
|
||||
if (_vignette != null) return true;
|
||||
|
||||
if (PostProcessingManager.Instance == null)
|
||||
{
|
||||
Debug.LogWarning("[VignetteAction] PostProcessingManager instance not found.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!PostProcessingManager.Instance.GetVolumeComponent(out _vignette))
|
||||
{
|
||||
Debug.LogWarning("[VignetteAction] AdvancedVignette not found in Volume Profile.");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void RestoreValues()
|
||||
{
|
||||
if (!_resolved) return;
|
||||
|
||||
_vignette.intensity.value = _initialIntensity;
|
||||
_vignette.center.value = _initialCenter;
|
||||
_vignette.colorOuter.value = _initialColorOuter;
|
||||
_vignette.colorInner.value = _initialColorInner;
|
||||
_vignette.smoothness.value = _initialSmoothness;
|
||||
_vignette.roundness.value = _initialRoundness;
|
||||
_resolved = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user