SimpleLean.cs 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  1. // Animancer // https://kybernetik.com.au/animancer // Copyright 2022 Kybernetik //
  2. #pragma warning disable CS0649 // Field is never assigned to, and will always have its default value.
  3. using System;
  4. using Unity.Collections;
  5. using UnityEngine;
  6. using UnityEngine.Animations;
  7. namespace Animancer.Examples.Jobs
  8. {
  9. /// <summary>
  10. /// A wrapper that manages an Animation Job (the <see cref="Job"/> struct nested inside this class) which rotates a
  11. /// set of bones to allow the character to dynamically lean over independantly of their animations.
  12. /// </summary>
  13. ///
  14. /// <remarks>
  15. /// The axis around which the bones are rotated can be set to achieve several different effects:
  16. /// <list type="number">
  17. /// <item>The right axis allows bending forwards and backwards.</item>
  18. /// <item>The up axis allows turning to either side.</item>
  19. /// <item>The forward axis allows leaning to either side.</item>
  20. /// </list>
  21. /// <see cref="https://github.com/KybernetikGames/animancer/issues/48#issuecomment-632336377">
  22. /// This script is based on an implementation by ted-hou on GitHub.</see>
  23. /// </remarks>
  24. ///
  25. /// <example><see href="https://kybernetik.com.au/animancer/docs/examples/jobs/lean">Lean</see></example>
  26. ///
  27. /// https://kybernetik.com.au/animancer/api/Animancer.Examples.Jobs/SimpleLean
  28. ///
  29. public sealed class SimpleLean : AnimancerJob<SimpleLean.Job>, IDisposable
  30. {
  31. /************************************************************************************************************************/
  32. #region Initialisation
  33. /************************************************************************************************************************/
  34. public SimpleLean(AnimancerPlayable animancer, Vector3 axis, NativeArray<TransformStreamHandle> leanBones)
  35. {
  36. var animator = animancer.Component.Animator;
  37. _Job = new Job
  38. {
  39. root = animator.BindStreamTransform(animator.transform),
  40. bones = leanBones,
  41. axis = axis,
  42. angle = AnimancerUtilities.CreateNativeReference<float>(),
  43. };
  44. CreatePlayable(animancer);
  45. animancer.Disposables.Add(this);
  46. }
  47. /************************************************************************************************************************/
  48. public SimpleLean(AnimancerPlayable animancer)
  49. : this(animancer, Vector3.forward, GetDefaultHumanoidLeanBones(animancer.Component.Animator))
  50. { }
  51. /************************************************************************************************************************/
  52. public static NativeArray<TransformStreamHandle> GetDefaultHumanoidLeanBones(Animator animator)
  53. {
  54. var leanBones = new NativeArray<TransformStreamHandle>(2, Allocator.Persistent, NativeArrayOptions.UninitializedMemory);
  55. leanBones[0] = animator.BindStreamTransform(animator.GetBoneTransform(HumanBodyBones.Spine));
  56. leanBones[1] = animator.BindStreamTransform(animator.GetBoneTransform(HumanBodyBones.Chest));
  57. return leanBones;
  58. }
  59. /************************************************************************************************************************/
  60. #endregion
  61. /************************************************************************************************************************/
  62. #region Control
  63. /************************************************************************************************************************/
  64. // The Axis probably won't change often so the setter can just get the job data and change it.
  65. /************************************************************************************************************************/
  66. public Vector3 Axis
  67. {
  68. get => _Job.axis;
  69. set
  70. {
  71. if (_Job.axis == value)
  72. return;
  73. _Job.axis = value;
  74. _Playable.SetJobData(_Job);
  75. }
  76. }
  77. /************************************************************************************************************************/
  78. // But since the Angle could change all the time, we can exploit the fact that arrays are actualy references to avoid
  79. // copying the entire struct out of the job playable then back in every time.
  80. /************************************************************************************************************************/
  81. public float Angle
  82. {
  83. get => _Job.angle[0];
  84. set => _Job.angle[0] = value;
  85. }
  86. /************************************************************************************************************************/
  87. #endregion
  88. /************************************************************************************************************************/
  89. #region Clean Up
  90. /************************************************************************************************************************/
  91. void IDisposable.Dispose() => Dispose();
  92. /// <summary>Cleans up the <see cref="NativeArray{T}"/>s.</summary>
  93. /// <remarks>Called by <see cref="AnimancerPlayable.OnPlayableDestroy"/>.</remarks>
  94. private void Dispose()
  95. {
  96. if (_Job.angle.IsCreated)
  97. _Job.angle.Dispose();
  98. if (_Job.bones.IsCreated)
  99. _Job.bones.Dispose();
  100. }
  101. /// <summary>Destroys the <see cref="_Playable"/> and restores the graph connection it was intercepting.</summary>
  102. public override void Destroy()
  103. {
  104. Dispose();
  105. base.Destroy();
  106. }
  107. /************************************************************************************************************************/
  108. #endregion
  109. /************************************************************************************************************************/
  110. #region Job
  111. /************************************************************************************************************************/
  112. /// <summary>An <see cref="IAnimationJob"/> that applies a lean effect to an <see cref="AnimationStream"/>.</summary>
  113. /// <example><see href="https://kybernetik.com.au/animancer/docs/examples/jobs/lean">Lean</see></example>
  114. public struct Job : IAnimationJob
  115. {
  116. /************************************************************************************************************************/
  117. public TransformStreamHandle root;
  118. public NativeArray<TransformStreamHandle> bones;
  119. public Vector3 axis;
  120. public NativeArray<float> angle;
  121. /************************************************************************************************************************/
  122. public void ProcessRootMotion(AnimationStream stream) { }
  123. /************************************************************************************************************************/
  124. public void ProcessAnimation(AnimationStream stream)
  125. {
  126. var angle = this.angle[0] / bones.Length;
  127. var worldAxis = root.GetRotation(stream) * axis;
  128. var offset = Quaternion.AngleAxis(angle, worldAxis);
  129. for (int i = bones.Length - 1; i >= 0; i--)
  130. {
  131. var bone = bones[i];
  132. bone.SetRotation(stream, offset * bone.GetRotation(stream));
  133. }
  134. }
  135. /************************************************************************************************************************/
  136. }
  137. /************************************************************************************************************************/
  138. #endregion
  139. /************************************************************************************************************************/
  140. }
  141. }