// Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2024 Kybernetik // #pragma warning disable CS0649 // Field is never assigned to, and will always have its default value. using Unity.Collections; using UnityEngine; using UnityEngine.Animations; namespace Animancer.Samples.Jobs { /// An sample of how to use Animation Jobs in Animancer to apply physics based damping to certain bones. /// /// /// This sample is based on Unity's /// Animation Jobs Samples. /// /// This script sets up the job in place of /// /// Damping.cs. /// /// The script is almost identical to the original /// /// DampingJob.cs. /// /// The Animation Rigging package /// has a damping system which is likely better than this sample. /// /// Sample: /// /// Damping /// /// /// https://kybernetik.com.au/animancer/api/Animancer.Samples.Jobs/Damping /// [AddComponentMenu(Strings.SamplesMenuPrefix + "Jobs - Damping")] [AnimancerHelpUrl(typeof(Damping))] public class Damping : MonoBehaviour { /************************************************************************************************************************/ [SerializeField] private AnimancerComponent _Animancer; [SerializeField] private Transform _EndBone; [SerializeField] private int _BoneCount = 3; /************************************************************************************************************************/ protected virtual void Awake() { // Create the job and initialize all its arrays. // They are all Persistent because we want them to last for the full lifetime of the job. // Most of them can use UninitializedMemory which is faster because we will be immediately filling them. // But the velocities will use the default ClearMemory to make sure all the values start at zero. // Since we are about to use these values several times, we can shorten the following lines a bit by using constants: const Allocator Persistent = Allocator.Persistent; const NativeArrayOptions UninitializedMemory = NativeArrayOptions.UninitializedMemory; DampingJob job = new() { jointHandles = new(_BoneCount, Persistent, UninitializedMemory), localPositions = new(_BoneCount, Persistent, UninitializedMemory), localRotations = new(_BoneCount, Persistent, UninitializedMemory), positions = new(_BoneCount, Persistent, UninitializedMemory), velocities = new(_BoneCount, Persistent), }; // Initialize the contents of the arrays for each bone. Animator animator = _Animancer.Animator; Transform bone = _EndBone; for (int i = _BoneCount - 1; i >= 0; i--) { job.jointHandles[i] = animator.BindStreamTransform(bone); job.localPositions[i] = bone.localPosition; job.localRotations[i] = bone.localRotation; job.positions[i] = bone.position; bone = bone.parent; } job.rootHandle = animator.BindStreamTransform(bone); // Add the job to Animancer's output. _Animancer.Graph.InsertOutputJob(job); // Make sure Animancer disposes the Native Arrays when it is destroyed so we don't leak memory. // If we were writing our own job rather than just using the sample, we could have it implement the // IDisposable interface to dispose its arrays so that we would only have to call ...Add(_Job); here. _Animancer.Graph.Disposables.Add(job.jointHandles); _Animancer.Graph.Disposables.Add(job.localPositions); _Animancer.Graph.Disposables.Add(job.localRotations); _Animancer.Graph.Disposables.Add(job.positions); _Animancer.Graph.Disposables.Add(job.velocities); } /************************************************************************************************************************/ /// /// Ensures that the is positive and not larger than the number of bones between the /// and the . /// /// Called in Edit Mode whenever this script is loaded or a value is changed in the Inspector. protected virtual void OnValidate() { if (_BoneCount < 1) { _BoneCount = 1; } else if (_EndBone != null && _Animancer != null && _Animancer.Animator != null) { Transform root = _Animancer.Animator.transform; Transform bone = _EndBone; for (int i = 0; i < _BoneCount; i++) { bone = bone.parent; if (bone == root) { _BoneCount = i + 1; break; } else if (bone == null) { _EndBone = null; Debug.LogWarning("The End Bone must be a child of the Animator."); break; } } } } /************************************************************************************************************************/ } }