阶段性完成

This commit is contained in:
SoulliesOfficial
2025-12-08 05:27:53 -05:00
parent ef7b479712
commit f7af60351b
8770 changed files with 15637030 additions and 208354 deletions

View File

@@ -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 上的 FollowTargetZ 轴旋转将作用于相机的 Dutch。
/// 需要在 Virtual Camera 上挂载 MMCinemachineRotationShaker。
/// </summary>
[AddComponentMenu("")]
[FeedbackPath("Camera/Camera Rotation Shake")]
[FeedbackHelp("控制 Cinemachine 相机的旋转抖动。X/Y 轴作用于 Shaker 指定的 FollowTargetZ 轴作用于相机的 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 应用于 FollowTargetZ 应用于 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
}
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 67b7d4b750dfaaa4985ea611fcb61506

View File

@@ -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

View File

@@ -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:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: cc3f8183ef05d954a87e3c076cf8aed2
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -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);
}
}
}

View File

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