Files
zeling_v2/Assets/Feel/MMFeedbacks/MMFeedbacks/Shakers/MMScaleShaker.cs
2026-05-08 11:04:00 +08:00

376 lines
16 KiB
C#

using System;
using System.Collections;
using MoreMountains.Tools;
using UnityEngine;
using Random = UnityEngine.Random;
namespace MoreMountains.Feedbacks
{
/// <summary>
/// This shaker will let you move the scale of a transform, either once or permanently, shaking its scale for the specified duration and within the specified range.
/// You can apply that shake along a direction, randomized or not, with optional noise and attenuation
/// </summary>
public class MMScaleShaker : MMShaker
{
public enum Modes { Transform, RectTransform }
[MMInspectorGroup("Target", true, 41)]
/// whether this shaker should target Transforms or RectTransforms
[Tooltip("whether this shaker should target Transforms or RectTransforms")]
public Modes Mode = Modes.Transform;
/// the transform to shake the scale of. If left blank, this component will target the transform it's put on.
[Tooltip("the transform to shake the scale of. If left blank, this component will target the transform it's put on.")]
[MMEnumCondition("Mode", (int)Modes.Transform)]
public Transform TargetTransform;
/// the rect transform to shake the scale of. If left blank, this component will target the transform it's put on.
[Tooltip("the rect transform to shake the scale of. If left blank, this component will target the transform it's put on.")]
[MMEnumCondition("Mode", (int)Modes.RectTransform)]
public RectTransform TargetRectTransform;
[MMInspectorGroup("Shake Settings", true, 42)]
/// the speed at which the transform should shake
[Tooltip("the speed at which the transform should shake")]
public float ShakeSpeed = 20f;
/// the maximum distance from its initial scale the transform will move to during the shake
[Tooltip("the maximum distance from its initial scale the transform will move to during the shake")]
public float ShakeRange = 0.5f;
[MMInspectorGroup("Direction", true, 43)]
/// the direction along which to shake the transform's scale
[Tooltip("the direction along which to shake the transform's scale")]
public Vector3 ShakeMainDirection = Vector3.up;
/// if this is true, instead of using ShakeMainDirection as the direction of the shake, a random vector3 will be generated, randomized between ShakeMainDirection and ShakeAltDirection
[Tooltip("if this is true, instead of using ShakeMainDirection as the direction of the shake, a random vector3 will be generated, randomized between ShakeMainDirection and ShakeAltDirection")]
public bool RandomizeDirection = false;
/// when in RandomizeDirection mode, a vector against which to randomize the main direction
[Tooltip("when in RandomizeDirection mode, a vector against which to randomize the main direction")]
[MMCondition("RandomizeDirection", true)]
public Vector3 ShakeAltDirection = Vector3.up;
/// if this is true, a new direction will be randomized every time a shake happens
[Tooltip("if this is true, a new direction will be randomized every time a shake happens")]
public bool RandomizeDirectionOnPlay = false;
[MMInspectorGroup("Directional Noise", true, 47)]
/// whether or not to add noise to the main direction
[Tooltip("whether or not to add noise to the main direction")]
public bool AddDirectionalNoise = true;
/// when adding directional noise, noise strength will be randomized between this value and DirectionalNoiseStrengthMax
[Tooltip("when adding directional noise, noise strength will be randomized between this value and DirectionalNoiseStrengthMax")]
[MMCondition("AddDirectionalNoise", true)]
public Vector3 DirectionalNoiseStrengthMin = new Vector3(0.25f, 0.25f, 0.25f);
/// when adding directional noise, noise strength will be randomized between this value and DirectionalNoiseStrengthMin
[Tooltip("when adding directional noise, noise strength will be randomized between this value and DirectionalNoiseStrengthMin")]
[MMCondition("AddDirectionalNoise", true)]
public Vector3 DirectionalNoiseStrengthMax = new Vector3(0.25f, 0.25f, 0.25f);
[MMInspectorGroup("Randomness", true, 44)]
/// a unique seed you can use to get different outcomes when shaking more than one transform at once
[Tooltip("a unique seed you can use to get different outcomes when shaking more than one transform at once")]
public Vector3 RandomnessSeed;
/// whether or not to generate a unique seed automatically on every shake
[Tooltip("whether or not to generate a unique seed automatically on every shake")]
public bool RandomizeSeedOnShake = true;
[MMInspectorGroup("One Time", true, 45)]
/// whether or not to use attenuation, which will impact the amplitude of the shake, along the defined curve
[Tooltip("whether or not to use attenuation, which will impact the amplitude of the shake, along the defined curve")]
public bool UseAttenuation = true;
/// the animation curve used to define attenuation, impacting the amplitude of the shake
[Tooltip("the animation curve used to define attenuation, impacting the amplitude of the shake")]
[MMCondition("UseAttenuation", true)]
public AnimationCurve AttenuationCurve = new AnimationCurve(new Keyframe(0, 0), new Keyframe(0.5f, 1), new Keyframe(1, 0));
[MMInspectorGroup("Test", true, 46)]
[MMInspectorButton("StartShaking")]
public bool StartShakingButton;
public virtual float Randomness => RandomnessSeed.x + RandomnessSeed.y + RandomnessSeed.z;
protected float _attenuation = 1f;
protected float _oscillation;
protected Vector3 _initialScale;
protected Vector3 _workDirection;
protected Vector3 _noiseVector;
protected Vector3 _newScale;
protected Vector3 _randomNoiseStrength;
protected Vector3 _noNoise = Vector3.zero;
protected Vector3 _randomizedDirection;
/// <summary>
/// On init we initialize our values
/// </summary>
protected override void Initialization()
{
base.Initialization();
if (TargetTransform == null) { TargetTransform = this.transform; }
if (TargetRectTransform == null) { TargetRectTransform = this.GetComponent<RectTransform>(); }
GrabInitialScale();
}
public virtual void GrabInitialScale()
{
switch (Mode)
{
case Modes.Transform:
_initialScale = TargetTransform.localScale;
break;
case Modes.RectTransform:
_initialScale = TargetRectTransform.localScale;
break;
}
}
/// <summary>
/// When that shaker gets added, we initialize its shake duration
/// </summary>
protected virtual void Reset()
{
ShakeDuration = 0.5f;
}
protected override void ShakeStarts()
{
GrabInitialScale();
if (RandomizeSeedOnShake)
{
RandomnessSeed = Random.insideUnitSphere;
}
if (RandomizeDirectionOnPlay)
{
ShakeMainDirection = Random.insideUnitSphere;
ShakeAltDirection = Random.insideUnitSphere;
}
_randomizedDirection = RandomizeDirection ? MMMaths.RandomVector3(ShakeMainDirection, ShakeAltDirection) : ShakeMainDirection;
}
protected override void Shake()
{
_oscillation = Mathf.Sin(ShakeSpeed * (Randomness + _journey));
float remappedTime = MMFeedbacksHelpers.Remap(_journey, 0f, ShakeDuration, 0f, 1f);
_attenuation = ComputeAttenuation(remappedTime);
_workDirection = ShakeMainDirection + ComputeNoise(_journey);
_workDirection.Normalize();
_newScale = ComputeNewScale();
ApplyNewScale(_newScale);
}
protected override void ShakeComplete()
{
base.ShakeComplete();
_attenuation = 0f;
_newScale = ComputeNewScale();
if (TargetTransform != null)
{
ApplyNewScale(_newScale);
}
}
protected virtual void ApplyNewScale(Vector3 newScale)
{
switch (Mode)
{
case Modes.Transform:
TargetTransform.localScale = newScale;
break;
case Modes.RectTransform:
TargetRectTransform.localScale = newScale;
break;
}
}
protected virtual Vector3 ComputeNewScale()
{
return _initialScale + _workDirection * _oscillation * ShakeRange * _attenuation;
}
protected virtual float ComputeAttenuation(float remappedTime)
{
return (UseAttenuation && !PermanentShake) ? AttenuationCurve.Evaluate(remappedTime) : 1f;
}
protected virtual Vector3 ComputeNoise(float time)
{
if (!AddDirectionalNoise)
{
return _noNoise;
}
_randomNoiseStrength = MMMaths.RandomVector3(DirectionalNoiseStrengthMin, DirectionalNoiseStrengthMax);
_noiseVector.x = _randomNoiseStrength.x * (Mathf.PerlinNoise(RandomnessSeed.x, time) - 0.5f) ;
_noiseVector.y = _randomNoiseStrength.y * (Mathf.PerlinNoise(RandomnessSeed.y, time) - 0.5f);
_noiseVector.z = _randomNoiseStrength.z * (Mathf.PerlinNoise(RandomnessSeed.z, time) - 0.5f);
return _noiseVector;
}
protected float _originalDuration;
protected float _originalShakeSpeed;
protected float _originalShakeRange;
protected Vector3 _originalShakeMainDirection;
protected bool _originalRandomizeDirection;
protected Vector3 _originalShakeAltDirection;
protected bool _originalRandomizeDirectionOnPlay;
protected bool _originalAddDirectionalNoise;
protected Vector3 _originalDirectionalNoiseStrengthMin;
protected Vector3 _originalDirectionalNoiseStrengthMax;
protected Vector3 _originalRandomnessSeed;
protected bool _originalRandomizeSeedOnShake;
protected bool _originalUseAttenuation;
protected AnimationCurve _originalAttenuationCurve;
public virtual void OnMMScaleShakeEvent(float duration, float shakeSpeed, float shakeRange, Vector3 shakeMainDirection, bool randomizeDirection, Vector3 shakeAltDirection, bool randomizeDirectionOnPlay, bool addDirectionalNoise,
Vector3 directionalNoiseStrengthMin, Vector3 directionalNoiseStrengthMax, Vector3 randomnessSeed, bool randomizeSeedOnShake, bool useAttenuation, AnimationCurve attenuationCurve,
bool useRange = false, float rangeDistance = 0f, bool useRangeFalloff = false, AnimationCurve rangeFalloff = null, Vector2 remapRangeFalloff = default(Vector2), Vector3 rangePosition = default(Vector3),
float feedbacksIntensity = 1.0f, MMChannelData channelData = null, bool resetShakerValuesAfterShake = true,
bool resetTargetValuesAfterShake = true, bool forwardDirection = true, TimescaleModes timescaleMode = TimescaleModes.Scaled, bool stop = false, bool restore = false)
{
if (!CheckEventAllowed(channelData) || (!Interruptible && Shaking))
{
return;
}
if (stop)
{
Stop();
return;
}
if (restore)
{
ResetTargetValues();
return;
}
_resetShakerValuesAfterShake = resetShakerValuesAfterShake;
_resetTargetValuesAfterShake = resetTargetValuesAfterShake;
if (resetShakerValuesAfterShake)
{
_originalDuration = ShakeDuration;
_originalShakeSpeed = ShakeSpeed;
_originalShakeRange = ShakeRange;
_originalShakeMainDirection = ShakeMainDirection;
_originalRandomizeDirection = RandomizeDirection;
_originalShakeAltDirection = ShakeAltDirection;
_originalRandomizeDirectionOnPlay = RandomizeDirectionOnPlay;
_originalAddDirectionalNoise = AddDirectionalNoise;
_originalDirectionalNoiseStrengthMin = DirectionalNoiseStrengthMin;
_originalDirectionalNoiseStrengthMax = DirectionalNoiseStrengthMax;
_originalRandomnessSeed = RandomnessSeed;
_originalRandomizeSeedOnShake = RandomizeSeedOnShake;
_originalUseAttenuation = UseAttenuation;
_originalAttenuationCurve = AttenuationCurve;
}
if (!OnlyUseShakerValues)
{
TimescaleMode = timescaleMode;
ShakeDuration = duration;
ShakeSpeed = shakeSpeed;
ShakeRange = shakeRange * feedbacksIntensity * ComputeRangeIntensity(useRange, rangeDistance,
useRangeFalloff, rangeFalloff, remapRangeFalloff, rangePosition);
ShakeMainDirection = shakeMainDirection;
RandomizeDirection = randomizeDirection;
ShakeAltDirection = shakeAltDirection;
RandomizeDirectionOnPlay = randomizeDirectionOnPlay;
AddDirectionalNoise = addDirectionalNoise;
DirectionalNoiseStrengthMin = directionalNoiseStrengthMin;
DirectionalNoiseStrengthMax = directionalNoiseStrengthMax;
RandomnessSeed = randomnessSeed;
RandomizeSeedOnShake = randomizeSeedOnShake;
UseAttenuation = useAttenuation;
AttenuationCurve = attenuationCurve;
ForwardDirection = forwardDirection;
}
Play();
}
/// <summary>
/// Resets the target's values
/// </summary>
protected override void ResetTargetValues()
{
base.ResetTargetValues();
switch (Mode)
{
case Modes.Transform:
TargetTransform.localScale = _initialScale;
break;
case Modes.RectTransform:
TargetRectTransform.localScale = _initialScale;
break;
}
}
/// <summary>
/// Resets the shaker's values
/// </summary>
protected override void ResetShakerValues()
{
base.ResetShakerValues();
ShakeDuration = _originalDuration;
ShakeSpeed = _originalShakeSpeed;
ShakeRange = _originalShakeRange;
ShakeMainDirection = _originalShakeMainDirection;
RandomizeDirection = _originalRandomizeDirection;
ShakeAltDirection = _originalShakeAltDirection;
RandomizeDirectionOnPlay = _originalRandomizeDirectionOnPlay;
AddDirectionalNoise = _originalAddDirectionalNoise;
DirectionalNoiseStrengthMin = _originalDirectionalNoiseStrengthMin;
DirectionalNoiseStrengthMax = _originalDirectionalNoiseStrengthMax;
RandomnessSeed = _originalRandomnessSeed;
RandomizeSeedOnShake = _originalRandomizeSeedOnShake;
UseAttenuation = _originalUseAttenuation;
AttenuationCurve = _originalAttenuationCurve;
}
/// <summary>
/// Starts listening for events
/// </summary>
public override void StartListening()
{
base.StartListening();
MMScaleShakeEvent.Register(OnMMScaleShakeEvent);
}
/// <summary>
/// Stops listening for events
/// </summary>
public override void StopListening()
{
base.StopListening();
MMScaleShakeEvent.Unregister(OnMMScaleShakeEvent);
}
}
public struct MMScaleShakeEvent
{
static private event Delegate OnEvent;
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)] private static void RuntimeInitialization() { OnEvent = null; }
static public void Register(Delegate callback) { OnEvent += callback; }
static public void Unregister(Delegate callback) { OnEvent -= callback; }
public delegate void Delegate(float duration, float shakeSpeed, float shakeRange, Vector3 shakeMainDirection, bool randomizeDirection, Vector3 shakeAltDirection, bool randomizeDirectionOnPlay, bool addDirectionalNoise,
Vector3 directionalNoiseStrengthMin, Vector3 directionalNoiseStrengthMax, Vector3 randomnessSeed, bool randomizeSeedOnShake, bool useAttenuation, AnimationCurve attenuationCurve,
bool useRange = false, float rangeDistance = 0f, bool useRangeFalloff = false, AnimationCurve rangeFalloff = null, Vector2 remapRangeFalloff = default(Vector2), Vector3 rangePosition = default(Vector3),
float feedbacksIntensity = 1.0f, MMChannelData channelData = null, bool resetShakerValuesAfterShake = true,
bool resetTargetValuesAfterShake = true, bool forwardDirection = true, TimescaleModes timescaleMode = TimescaleModes.Scaled, bool stop = false, bool restore = false);
static public void Trigger(float duration, float shakeSpeed, float shakeRange, Vector3 shakeMainDirection, bool randomizeDirection, Vector3 shakeAltDirection, bool randomizeDirectionOnPlay, bool addDirectionalNoise,
Vector3 directionalNoiseStrengthMin, Vector3 directionalNoiseStrengthMax, Vector3 randomnessSeed, bool randomizeSeedOnShake, bool useAttenuation, AnimationCurve attenuationCurve,
bool useRange = false, float rangeDistance = 0f, bool useRangeFalloff = false, AnimationCurve rangeFalloff = null, Vector2 remapRangeFalloff = default(Vector2), Vector3 rangePosition = default(Vector3),
float feedbacksIntensity = 1.0f, MMChannelData channelData = null, bool resetShakerValuesAfterShake = true,
bool resetTargetValuesAfterShake = true, bool forwardDirection = true, TimescaleModes timescaleMode = TimescaleModes.Scaled, bool stop = false, bool restore = false)
{
OnEvent?.Invoke( duration, shakeSpeed, shakeRange, shakeMainDirection, randomizeDirection, shakeAltDirection, randomizeDirectionOnPlay, addDirectionalNoise,
directionalNoiseStrengthMin, directionalNoiseStrengthMax, randomnessSeed, randomizeSeedOnShake, useAttenuation, attenuationCurve,
useRange, rangeDistance, useRangeFalloff, rangeFalloff, remapRangeFalloff, rangePosition,
feedbacksIntensity, channelData, resetShakerValuesAfterShake, resetTargetValuesAfterShake, forwardDirection, timescaleMode, stop, restore);
}
}
}