134 lines
6.2 KiB
C#
134 lines
6.2 KiB
C#
// 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
|
|
{
|
|
/// <summary>An sample of how to use Animation Jobs in Animancer to apply physics based damping to certain bones.</summary>
|
|
///
|
|
/// <remarks>
|
|
/// This sample is based on Unity's
|
|
/// <see href="https://github.com/Unity-Technologies/animation-jobs-samples">Animation Jobs Samples</see>.
|
|
/// <para></para>
|
|
/// This script sets up the job in place of
|
|
/// <see href="https://github.com/Unity-Technologies/animation-jobs-samples/blob/master/Assets/animation-jobs-samples/Samples/Scripts/Damping/Damping.cs">
|
|
/// Damping.cs</see>.
|
|
/// <para></para>
|
|
/// The <see cref="DampingJob"/> script is almost identical to the original
|
|
/// <see href="https://github.com/Unity-Technologies/animation-jobs-samples/blob/master/Assets/animation-jobs-samples/Runtime/AnimationJobs/DampingJob.cs">
|
|
/// DampingJob.cs</see>.
|
|
/// <para></para>
|
|
/// The <see href="https://learn.unity.com/tutorial/working-with-animation-rigging">Animation Rigging</see> package
|
|
/// has a damping system which is likely better than this sample.
|
|
/// <para></para>
|
|
/// <strong>Sample:</strong>
|
|
/// <see href="https://kybernetik.com.au/animancer/docs/samples/jobs/damping">
|
|
/// Damping</see>
|
|
/// </remarks>
|
|
///
|
|
/// 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);
|
|
}
|
|
|
|
/************************************************************************************************************************/
|
|
|
|
/// <summary>
|
|
/// Ensures that the <see cref="_BoneCount"/> is positive and not larger than the number of bones between the
|
|
/// <see cref="_EndBone"/> and the <see cref="Animator"/>.
|
|
/// </summary>
|
|
/// <remarks>Called in Edit Mode whenever this script is loaded or a value is changed in the Inspector.</remarks>
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/************************************************************************************************************************/
|
|
}
|
|
}
|