阶段性完成
This commit is contained in:
@@ -0,0 +1,125 @@
|
||||
using MoreMountains.FeedbacksForThirdParty;
|
||||
using MoreMountains.Tools;
|
||||
using Sirenix.OdinInspector;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Scripting.APIUpdating;
|
||||
|
||||
namespace MoreMountains.Feedbacks
|
||||
{
|
||||
/// <summary>
|
||||
/// 这个 Feedback 用于控制 Cinemachine 相机的旋转抖动。
|
||||
/// X/Y 轴旋转将作用于 Shaker 上的 FollowTarget,Z 轴旋转将作用于相机的 Dutch。
|
||||
/// 需要在 Virtual Camera 上挂载 MMCinemachineRotationShaker。
|
||||
/// </summary>
|
||||
[AddComponentMenu("")]
|
||||
[FeedbackPath("Camera/Camera Rotation Shake")]
|
||||
[FeedbackHelp("控制 Cinemachine 相机的旋转抖动。X/Y 轴作用于 Shaker 指定的 FollowTarget,Z 轴作用于相机的 Dutch。需要在 Virtual Camera 上挂载 MMCinemachineRotationShaker。")]
|
||||
public class MMF_CameraRotation : MMF_Feedback
|
||||
{
|
||||
/// 用于禁用此类 Feedback 的静态开关
|
||||
public static bool FeedbackTypeAuthorized = true;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
public override Color FeedbackColor => MMFeedbacksInspectorColors.CameraColor;
|
||||
public override bool HasCustomInspectors => true;
|
||||
public override bool HasAutomaticShakerSetup => true;
|
||||
#endif
|
||||
|
||||
public override float FeedbackDuration { get { return ApplyTimeMultiplier(Duration); } set { Duration = value; } }
|
||||
public override bool HasChannel => true;
|
||||
public override bool HasRandomness => true;
|
||||
|
||||
[MMFInspectorGroup("Rotation Shake", true, 37)]
|
||||
[Tooltip("抖动总时长")]
|
||||
public float Duration = 0.2f;
|
||||
|
||||
[Tooltip("最大旋转角度。X/Y 应用于 FollowTarget,Z 应用于 Dutch。")]
|
||||
public Vector3 RotationAmplitude = new Vector3(2f, 2f, 5f);
|
||||
|
||||
[Tooltip("定义抖动随时间变化的曲线")]
|
||||
public AnimationCurve ShakeCurve = new AnimationCurve(new Keyframe(0, 0), new Keyframe(0.2f, 1), new Keyframe(1, 0));
|
||||
|
||||
[MMFInspectorGroup("Distance Attenuation (距离衰减)", true, 38)]
|
||||
[Tooltip("如果勾选,抖动强度将根据摄像机与反馈位置的距离而衰减")]
|
||||
public bool UseAttenuation = false;
|
||||
[Tooltip("在这个范围内,抖动为全强度")]
|
||||
public float AttenuationRange = 50f;
|
||||
[Tooltip("定义强度随距离变化的曲线 (0代表近处/全强度, 1代表远处/无强度)")]
|
||||
public AnimationCurve AttenuationCurve = new AnimationCurve(new Keyframe(0f, 1f), new Keyframe(1f, 0f));
|
||||
|
||||
[Space]
|
||||
[Tooltip("抖动结束后是否重置 Shaker 的值为默认值")]
|
||||
public bool ResetShakerValuesAfterShake = true;
|
||||
[Tooltip("抖动结束后是否将目标的旋转归零")]
|
||||
public bool ResetTargetValuesAfterShake = true;
|
||||
|
||||
[Header("Gizmos")]
|
||||
public bool DrawGizmos = true;
|
||||
|
||||
protected override void CustomPlayFeedback(Vector3 position, float feedbacksIntensity = 1.0f)
|
||||
{
|
||||
if (!Active || !FeedbackTypeAuthorized) return;
|
||||
|
||||
float intensityMultiplier = ComputeIntensity(feedbacksIntensity, position);
|
||||
|
||||
// 处理距离衰减
|
||||
if (UseAttenuation && Camera.main != null)
|
||||
{
|
||||
float distance = Vector3.Distance(position, Camera.main.transform.position);
|
||||
// 简单的线性映射,或者使用曲线
|
||||
float normalizedDistance = Mathf.Clamp01(distance / AttenuationRange);
|
||||
intensityMultiplier *= AttenuationCurve.Evaluate(normalizedDistance);
|
||||
}
|
||||
|
||||
MMCameraRotationShakeEvent.Trigger(
|
||||
ShakeCurve,
|
||||
FeedbackDuration,
|
||||
RotationAmplitude,
|
||||
0f, 1f, false, // RemapMin/Max 和 Relative 暂时不用,保留接口
|
||||
intensityMultiplier,
|
||||
ChannelData,
|
||||
ResetShakerValuesAfterShake,
|
||||
ResetTargetValuesAfterShake,
|
||||
NormalPlayDirection,
|
||||
ComputedTimescaleMode
|
||||
);
|
||||
}
|
||||
|
||||
protected override void CustomStopFeedback(Vector3 position, float feedbacksIntensity = 1)
|
||||
{
|
||||
if (!Active || !FeedbackTypeAuthorized) return;
|
||||
base.CustomStopFeedback(position, feedbacksIntensity);
|
||||
MMCameraRotationShakeEvent.Trigger(ShakeCurve, FeedbackDuration, RotationAmplitude, 0f, 1f, false, stop: true);
|
||||
}
|
||||
|
||||
protected override void CustomRestoreInitialValues()
|
||||
{
|
||||
if (!Active || !FeedbackTypeAuthorized) return;
|
||||
MMCameraRotationShakeEvent.Trigger(ShakeCurve, FeedbackDuration, RotationAmplitude, 0f, 1f, false, restore: true);
|
||||
}
|
||||
|
||||
public override void OnDrawGizmosSelectedHandler()
|
||||
{
|
||||
if (UseAttenuation && DrawGizmos)
|
||||
{
|
||||
Gizmos.color = MMColors.ReunoYellow;
|
||||
Gizmos.DrawWireSphere(Owner.transform.position, AttenuationRange);
|
||||
}
|
||||
}
|
||||
|
||||
public override void AutomaticShakerSetup()
|
||||
{
|
||||
// 自动查找并添加 Shaker
|
||||
#if MM_CINEMACHINE
|
||||
Cinemachine.CinemachineVirtualCamera vcam = Object.FindObjectOfType<Cinemachine.CinemachineVirtualCamera>();
|
||||
if (vcam != null)
|
||||
{
|
||||
if (vcam.GetComponent<MMCinemachineRotationShaker>() == null)
|
||||
{
|
||||
vcam.gameObject.AddComponent<MMCinemachineRotationShaker>();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 67b7d4b750dfaaa4985ea611fcb61506
|
||||
@@ -0,0 +1,52 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!114 &11400000
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: b7f59e54f2bfd184b9dd451a678d089b, type: 3}
|
||||
m_Name: Dutch Roll Z
|
||||
m_EditorClassIdentifier:
|
||||
PositionNoise: []
|
||||
OrientationNoise:
|
||||
- X:
|
||||
Frequency: 5.83
|
||||
Amplitude: 0.09
|
||||
Constant: 1
|
||||
Y:
|
||||
Frequency: 1.8
|
||||
Amplitude: 0.059
|
||||
Constant: 1
|
||||
Z:
|
||||
Frequency: 2.38
|
||||
Amplitude: 0.017
|
||||
Constant: 1
|
||||
- X:
|
||||
Frequency: 9.17
|
||||
Amplitude: 0.14
|
||||
Constant: 1
|
||||
Y:
|
||||
Frequency: 11.35
|
||||
Amplitude: 0.041
|
||||
Constant: 1
|
||||
Z:
|
||||
Frequency: 10.52
|
||||
Amplitude: 0.009
|
||||
Constant: 1
|
||||
- X:
|
||||
Frequency: 57.17
|
||||
Amplitude: 0.15
|
||||
Constant: 1
|
||||
Y:
|
||||
Frequency: 54.17
|
||||
Amplitude: 0.048
|
||||
Constant: 1
|
||||
Z:
|
||||
Frequency: 63.76
|
||||
Amplitude: 0.016
|
||||
Constant: 1
|
||||
@@ -9,7 +9,7 @@ MonoBehaviour:
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: b7f59e54f2bfd184b9dd451a678d089b, type: 3}
|
||||
m_Name: MM_6D_Shake
|
||||
m_Name: MM_6DShake
|
||||
m_EditorClassIdentifier:
|
||||
PositionNoise:
|
||||
- X:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cc3f8183ef05d954a87e3c076cf8aed2
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 11400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,191 @@
|
||||
using UnityEngine;
|
||||
using MoreMountains.Feedbacks;
|
||||
using MoreMountains.Tools;
|
||||
#if MM_CINEMACHINE
|
||||
using Cinemachine;
|
||||
#elif MM_CINEMACHINE3
|
||||
using Unity.Cinemachine;
|
||||
#endif
|
||||
|
||||
namespace MoreMountains.FeedbacksForThirdParty
|
||||
{
|
||||
// 定义事件结构
|
||||
public struct MMCameraRotationShakeEvent
|
||||
{
|
||||
static private event Delegate OnEvent;
|
||||
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)] private static void RuntimeInitialization() { OnEvent = null; }
|
||||
public static void Register(Delegate callback) { OnEvent += callback; }
|
||||
public static void Unregister(Delegate callback) { OnEvent -= callback; }
|
||||
|
||||
public delegate void Delegate(AnimationCurve shakeCurve, float duration, Vector3 rotationAmplitude, float remapMin, float remapMax, bool relative,
|
||||
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);
|
||||
|
||||
public static void Trigger(AnimationCurve shakeCurve, float duration, Vector3 rotationAmplitude, float remapMin, float remapMax, bool relative,
|
||||
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(shakeCurve, duration, rotationAmplitude, remapMin, remapMax, relative, feedbacksIntensity, channelData, resetShakerValuesAfterShake, resetTargetValuesAfterShake, forwardDirection, timescaleMode, stop, restore);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cinemachine Rotation Shaker
|
||||
/// X/Y applied to FollowTarget, Z applied to Camera Dutch.
|
||||
/// </summary>
|
||||
[AddComponentMenu("More Mountains/Feedbacks/Shakers/Cinemachine/MM Cinemachine Rotation Shaker")]
|
||||
#if MM_CINEMACHINE
|
||||
[RequireComponent(typeof(CinemachineVirtualCamera))]
|
||||
#elif MM_CINEMACHINE3
|
||||
[RequireComponent(typeof(CinemachineCamera))]
|
||||
#endif
|
||||
public class MMCinemachineRotationShaker : MMShaker
|
||||
{
|
||||
public Transform FollowTarget => _targetCamera.Follow;
|
||||
|
||||
[MMInspectorGroup("Shake Settings", true, 42)]
|
||||
public Vector3 ShakeRotationAmplitude = new Vector3(2f, 2f, 5f);
|
||||
public AnimationCurve ShakeCurve = new AnimationCurve(new Keyframe(0, 0), new Keyframe(0.5f, 1), new Keyframe(1, 0));
|
||||
|
||||
[MMFInspectorButton("StartShaking")]
|
||||
public bool TestShakeButton;
|
||||
|
||||
#if MM_CINEMACHINE
|
||||
protected CinemachineVirtualCamera _targetCamera;
|
||||
#elif MM_CINEMACHINE3
|
||||
protected CinemachineCamera _targetCamera;
|
||||
#endif
|
||||
|
||||
protected float _initialDutch;
|
||||
protected Quaternion _initialTargetRotation;
|
||||
|
||||
// --- 修复点 1: 显式声明这些备份变量 ---
|
||||
protected float _originalShakeDuration;
|
||||
protected Vector3 _originalShakeRotationAmplitude;
|
||||
protected AnimationCurve _originalShakeCurve;
|
||||
|
||||
protected override void Initialization()
|
||||
{
|
||||
base.Initialization();
|
||||
#if MM_CINEMACHINE
|
||||
_targetCamera = GetComponent<CinemachineVirtualCamera>();
|
||||
#elif MM_CINEMACHINE3
|
||||
_targetCamera = GetComponent<CinemachineCamera>();
|
||||
#endif
|
||||
}
|
||||
|
||||
protected override void GrabInitialValues()
|
||||
{
|
||||
if (_targetCamera != null)
|
||||
{
|
||||
#if MM_CINEMACHINE
|
||||
_initialDutch = _targetCamera.m_Lens.Dutch;
|
||||
#elif MM_CINEMACHINE3
|
||||
_initialDutch = _targetCamera.Lens.Dutch;
|
||||
#endif
|
||||
}
|
||||
|
||||
if (FollowTarget != null)
|
||||
{
|
||||
_initialTargetRotation = FollowTarget.localRotation;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Shake()
|
||||
{
|
||||
// --- 修复点 2: 使用 ShakeFloat 代替手动 ShakeTime 计算 ---
|
||||
// ShakeFloat 会自动处理时间、重映射和曲线计算
|
||||
// 我们请求一个 0 到 1 之间的浮点数作为当前的“强度”
|
||||
float intensity = ShakeFloat(ShakeCurve, 0f, 1f, false, 0f);
|
||||
|
||||
// 根据强度应用 XYZ
|
||||
float xOffset = ShakeRotationAmplitude.x * intensity;
|
||||
float yOffset = ShakeRotationAmplitude.y * intensity;
|
||||
float zOffset = ShakeRotationAmplitude.z * intensity;
|
||||
|
||||
ApplyRotation(xOffset, yOffset, zOffset);
|
||||
}
|
||||
|
||||
protected virtual void ApplyRotation(float x, float y, float z)
|
||||
{
|
||||
// Z -> Dutch
|
||||
if (_targetCamera != null)
|
||||
{
|
||||
#if MM_CINEMACHINE
|
||||
_targetCamera.m_Lens.Dutch = _initialDutch + z;
|
||||
#elif MM_CINEMACHINE3
|
||||
_targetCamera.Lens.Dutch = _initialDutch + z;
|
||||
#endif
|
||||
}
|
||||
|
||||
// X/Y -> FollowTarget
|
||||
if (FollowTarget != null)
|
||||
{
|
||||
FollowTarget.localRotation = _initialTargetRotation * Quaternion.Euler(x, y, 0f);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void ResetTargetValues()
|
||||
{
|
||||
base.ResetTargetValues();
|
||||
ApplyRotation(0f, 0f, 0f);
|
||||
}
|
||||
|
||||
protected override void ResetShakerValues()
|
||||
{
|
||||
base.ResetShakerValues();
|
||||
// 恢复备份的值
|
||||
ShakeDuration = _originalShakeDuration;
|
||||
ShakeRotationAmplitude = _originalShakeRotationAmplitude;
|
||||
ShakeCurve = _originalShakeCurve;
|
||||
}
|
||||
|
||||
public virtual void OnMMCameraRotationShakeEvent(AnimationCurve shakeCurve, float duration, Vector3 rotationAmplitude, float remapMin, float remapMax, bool relative, 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)) return;
|
||||
|
||||
if (stop)
|
||||
{
|
||||
Stop();
|
||||
return;
|
||||
}
|
||||
if (restore)
|
||||
{
|
||||
ResetTargetValues();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Interruptible && Shaking) return;
|
||||
|
||||
_resetShakerValuesAfterShake = resetShakerValuesAfterShake;
|
||||
_resetTargetValuesAfterShake = resetTargetValuesAfterShake;
|
||||
|
||||
if (resetShakerValuesAfterShake)
|
||||
{
|
||||
// 备份当前值
|
||||
_originalShakeDuration = ShakeDuration;
|
||||
_originalShakeRotationAmplitude = ShakeRotationAmplitude;
|
||||
_originalShakeCurve = ShakeCurve;
|
||||
}
|
||||
|
||||
TimescaleMode = timescaleMode;
|
||||
ShakeDuration = duration;
|
||||
// 应用 Intensity
|
||||
ShakeRotationAmplitude = rotationAmplitude * feedbacksIntensity;
|
||||
ShakeCurve = shakeCurve;
|
||||
ForwardDirection = forwardDirection;
|
||||
|
||||
Play();
|
||||
}
|
||||
|
||||
public override void StartListening()
|
||||
{
|
||||
base.StartListening();
|
||||
MMCameraRotationShakeEvent.Register(OnMMCameraRotationShakeEvent);
|
||||
}
|
||||
|
||||
public override void StopListening()
|
||||
{
|
||||
base.StopListening();
|
||||
MMCameraRotationShakeEvent.Unregister(OnMMCameraRotationShakeEvent);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a13116adea7514048aed172a893ed137
|
||||
Reference in New Issue
Block a user