CinemachineImpulseDefinition.cs 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  1. using Cinemachine.Utility;
  2. using System;
  3. using UnityEngine;
  4. namespace Cinemachine
  5. {
  6. /// <summary>
  7. /// Property applied to CinemachineImpulseManager Channels.
  8. /// Used for custom drawing in the inspector.
  9. /// </summary>
  10. public sealed class CinemachineImpulseDefinitionPropertyAttribute : PropertyAttribute {}
  11. /// <summary>
  12. /// Definition of an impulse signal that gets propagated to listeners.
  13. ///
  14. /// Here you provide a Raw Signal source, and define an envelope for time-scaling
  15. /// it to craft the complete Impulse signal shape. Also, you provide here parameters
  16. /// that define how the signal dissipates with spatial distance from the source location.
  17. /// Finally, you specify the Impulse Channel on which the signal will be sent.
  18. ///
  19. /// An API method is provided here to take these parameters, create an Impulse Event,
  20. /// and broadcast it on the channel.
  21. ///
  22. /// When creating a custom Impulse Source class, you will have an instance of this class
  23. /// as a field in your custom class. Be sure also to include the
  24. /// [CinemachineImpulseDefinition] attribute on the field, to get the right
  25. /// property drawer for it.
  26. /// </summary>
  27. [DocumentationSorting(DocumentationSortingAttribute.Level.API)]
  28. [Serializable]
  29. public class CinemachineImpulseDefinition
  30. {
  31. /// <summary>
  32. /// Impulse events generated here will appear on the channels included in the mask.
  33. /// </summary>
  34. [CinemachineImpulseChannelProperty]
  35. [Tooltip("Impulse events generated here will appear on the channels included in the mask.")]
  36. public int m_ImpulseChannel = 1;
  37. /// <summary>
  38. /// Defines the signal that will be generated.
  39. /// </summary>
  40. [Header("Signal Shape")]
  41. [Tooltip("Defines the signal that will be generated.")]
  42. [CinemachineEmbeddedAssetProperty(true)]
  43. public SignalSourceAsset m_RawSignal = null;
  44. /// <summary>
  45. /// Gain to apply to the amplitudes defined in the signal source asset.
  46. /// </summary>
  47. [Tooltip("Gain to apply to the amplitudes defined in the signal source. 1 is normal. Setting this to 0 completely mutes the signal.")]
  48. public float m_AmplitudeGain = 1f;
  49. /// <summary>
  50. /// Scale factor to apply to the time axis.
  51. /// </summary>
  52. [Tooltip("Scale factor to apply to the time axis. 1 is normal. Larger magnitudes will make the signal progress more rapidly.")]
  53. public float m_FrequencyGain = 1f;
  54. /// <summary>How to fit the signal into the envelope time</summary>
  55. public enum RepeatMode
  56. {
  57. /// <summary>Time-stretch the signal to fit the envelope</summary>
  58. Stretch,
  59. /// <summary>Loop the signal in time to fill the envelope</summary>
  60. Loop
  61. }
  62. /// <summary>How to fit the signal into the envelope time</summary>
  63. [Tooltip("How to fit the signal into the envelope time")]
  64. public RepeatMode m_RepeatMode = RepeatMode.Stretch;
  65. /// <summary>Randomize the signal start time</summary>
  66. [Tooltip("Randomize the signal start time")]
  67. public bool m_Randomize = true;
  68. /// <summary>
  69. /// This defines the time-envelope of the signal.
  70. /// The raw signal will be time-scaled to fit in the envelope.
  71. /// </summary>
  72. [Tooltip("This defines the time-envelope of the signal. The raw signal will be time-scaled to fit in the envelope.")]
  73. [CinemachineImpulseEnvelopeProperty]
  74. public CinemachineImpulseManager.EnvelopeDefinition m_TimeEnvelope
  75. = CinemachineImpulseManager.EnvelopeDefinition.Default();
  76. /// <summary>
  77. /// The signal will have full amplitude in this radius surrounding the impact point.
  78. /// Beyond that it will dissipate with distance.
  79. /// </summary>
  80. [Header("Spatial Range")]
  81. [Tooltip("The signal will have full amplitude in this radius surrounding the impact point. Beyond that it will dissipate with distance.")]
  82. public float m_ImpactRadius = 100;
  83. /// <summary>How the signal direction behaves as the listener moves away from the origin.</summary>
  84. [Tooltip("How the signal direction behaves as the listener moves away from the origin.")]
  85. public CinemachineImpulseManager.ImpulseEvent.DirectionMode m_DirectionMode
  86. = CinemachineImpulseManager.ImpulseEvent.DirectionMode.Fixed;
  87. /// <summary>
  88. /// This defines how the signal will dissipate with distance beyond the impact radius.
  89. /// </summary>
  90. [Tooltip("This defines how the signal will dissipate with distance beyond the impact radius.")]
  91. public CinemachineImpulseManager.ImpulseEvent.DissipationMode m_DissipationMode
  92. = CinemachineImpulseManager.ImpulseEvent.DissipationMode.ExponentialDecay;
  93. /// <summary>
  94. /// At this distance beyond the impact radius, the signal will have dissipated to zero.
  95. /// </summary>
  96. [Tooltip("At this distance beyond the impact radius, the signal will have dissipated to zero.")]
  97. public float m_DissipationDistance = 1000;
  98. /// <summary>
  99. /// The speed (m/s) at which the impulse propagates through space. High speeds
  100. /// allow listeners to react instantaneously, while slower speeds allow listeners in the
  101. /// scene to react as if to a wave spreading from the source.
  102. /// </summary>
  103. [Tooltip("The speed (m/s) at which the impulse propagates through space. High speeds "
  104. + "allow listeners to react instantaneously, while slower speeds allow listeners in the "
  105. + "scene to react as if to a wave spreading from the source.")]
  106. public float m_PropagationSpeed = 343; // speed of sound
  107. /// <summary>Call this from your behaviour's OnValidate to validate the fields here</summary>
  108. public void OnValidate()
  109. {
  110. m_ImpactRadius = Mathf.Max(0, m_ImpactRadius);
  111. m_DissipationDistance = Mathf.Max(0, m_DissipationDistance);
  112. m_TimeEnvelope.Validate();
  113. m_PropagationSpeed = Mathf.Max(1, m_PropagationSpeed);
  114. }
  115. /// <summary>Generate an impulse event at a location in space,
  116. /// and broadcast it on the appropriate impulse channel</summary>
  117. public void CreateEvent(
  118. Vector3 position, Vector3 velocity)
  119. {
  120. CreateAndReturnEvent(position, velocity);
  121. }
  122. /// <summary>Generate an impulse event at a location in space,
  123. /// and broadcast it on the appropriate impulse channel</summary>
  124. public CinemachineImpulseManager.ImpulseEvent CreateAndReturnEvent(
  125. Vector3 position, Vector3 velocity)
  126. {
  127. if (m_RawSignal == null || Mathf.Abs(m_TimeEnvelope.Duration) < UnityVectorExtensions.Epsilon)
  128. return null;
  129. CinemachineImpulseManager.ImpulseEvent e
  130. = CinemachineImpulseManager.Instance.NewImpulseEvent();
  131. e.m_Envelope = m_TimeEnvelope;
  132. // Scale the time-envelope decay as the root of the amplitude scale
  133. e.m_Envelope = m_TimeEnvelope;
  134. if (m_TimeEnvelope.m_ScaleWithImpact)
  135. e.m_Envelope.m_DecayTime *= Mathf.Sqrt(velocity.magnitude);
  136. e.m_SignalSource = new SignalSource(this, velocity);
  137. e.m_Position = position;
  138. e.m_Radius = m_ImpactRadius;
  139. e.m_Channel = m_ImpulseChannel;
  140. e.m_DirectionMode = m_DirectionMode;
  141. e.m_DissipationMode = m_DissipationMode;
  142. e.m_DissipationDistance = m_DissipationDistance;
  143. e.m_PropagationSpeed = m_PropagationSpeed;
  144. CinemachineImpulseManager.Instance.AddImpulseEvent(e);
  145. return e;
  146. }
  147. // Wrap the raw signal to handle gain, RepeatMode, randomization, and velocity
  148. class SignalSource : ISignalSource6D
  149. {
  150. CinemachineImpulseDefinition m_Def;
  151. Vector3 m_Velocity;
  152. float m_StartTimeOffset = 0;
  153. public SignalSource(CinemachineImpulseDefinition def, Vector3 velocity)
  154. {
  155. m_Def = def;
  156. m_Velocity = velocity;
  157. if (m_Def.m_Randomize && m_Def.m_RawSignal.SignalDuration <= 0)
  158. m_StartTimeOffset = UnityEngine.Random.Range(-1000f, 1000f);
  159. }
  160. public float SignalDuration { get { return m_Def.m_RawSignal.SignalDuration; } }
  161. public void GetSignal(float timeSinceSignalStart, out Vector3 pos, out Quaternion rot)
  162. {
  163. float time = m_StartTimeOffset + timeSinceSignalStart * m_Def.m_FrequencyGain;
  164. // Do we have to fit the signal into the envelope?
  165. float signalDuration = SignalDuration;
  166. if (signalDuration > 0)
  167. {
  168. if (m_Def.m_RepeatMode == RepeatMode.Loop)
  169. time %= signalDuration;
  170. else if (m_Def.m_TimeEnvelope.Duration > UnityVectorExtensions.Epsilon)
  171. time *= m_Def.m_TimeEnvelope.Duration / signalDuration; // stretch
  172. }
  173. m_Def.m_RawSignal.GetSignal(time, out pos, out rot);
  174. float gain = m_Velocity.magnitude;
  175. Vector3 dir = m_Velocity.normalized;
  176. gain *= m_Def.m_AmplitudeGain;
  177. pos *= gain;
  178. pos = Quaternion.FromToRotation(Vector3.down, m_Velocity) * pos;
  179. rot = Quaternion.SlerpUnclamped(Quaternion.identity, rot, gain);
  180. }
  181. }
  182. }
  183. }