614 lines
15 KiB
C#
614 lines
15 KiB
C#
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
|
|
[AddComponentMenu("Dynamic Bone/Dynamic Bone")]
|
|
public class DynamicBone : MonoBehaviour
|
|
{
|
|
public enum UpdateMode
|
|
{
|
|
Normal,
|
|
AnimatePhysics,
|
|
UnscaledTime
|
|
}
|
|
|
|
public enum FreezeAxis
|
|
{
|
|
None,
|
|
X,
|
|
Y,
|
|
Z
|
|
}
|
|
|
|
private class Particle
|
|
{
|
|
public Transform m_Transform;
|
|
|
|
public int m_ParentIndex = -1;
|
|
|
|
public float m_Damping;
|
|
|
|
public float m_Elasticity;
|
|
|
|
public float m_Stiffness;
|
|
|
|
public float m_Inert;
|
|
|
|
public float m_Radius;
|
|
|
|
public float m_BoneLength;
|
|
|
|
public Vector3 m_Position = Vector3.zero;
|
|
|
|
public Vector3 m_PrevPosition = Vector3.zero;
|
|
|
|
public Vector3 m_EndOffset = Vector3.zero;
|
|
|
|
public Vector3 m_InitLocalPosition = Vector3.zero;
|
|
|
|
public Quaternion m_InitLocalRotation = Quaternion.identity;
|
|
}
|
|
|
|
public Transform m_Root;
|
|
|
|
public float m_UpdateRate = 60f;
|
|
|
|
public UpdateMode m_UpdateMode;
|
|
|
|
[Range(0f, 1f)]
|
|
public float m_Damping = 0.1f;
|
|
|
|
public AnimationCurve m_DampingDistrib;
|
|
|
|
[Range(0f, 1f)]
|
|
public float m_Elasticity = 0.1f;
|
|
|
|
public AnimationCurve m_ElasticityDistrib;
|
|
|
|
[Range(0f, 1f)]
|
|
public float m_Stiffness = 0.1f;
|
|
|
|
public AnimationCurve m_StiffnessDistrib;
|
|
|
|
[Range(0f, 1f)]
|
|
public float m_Inert;
|
|
|
|
public AnimationCurve m_InertDistrib;
|
|
|
|
public float m_Radius;
|
|
|
|
public AnimationCurve m_RadiusDistrib;
|
|
|
|
public float m_EndLength;
|
|
|
|
public Vector3 m_EndOffset = Vector3.zero;
|
|
|
|
public Vector3 m_Gravity = Vector3.zero;
|
|
|
|
public Vector3 m_Force = Vector3.zero;
|
|
|
|
public List<DynamicBoneCollider> m_Colliders;
|
|
|
|
public List<Transform> m_Exclusions;
|
|
|
|
public FreezeAxis m_FreezeAxis;
|
|
|
|
public bool m_DistantDisable;
|
|
|
|
public Transform m_ReferenceObject;
|
|
|
|
public float m_DistanceToObject = 20f;
|
|
|
|
[Tooltip("Check this if you want the bone to be dynamic even on low performance HW")]
|
|
public bool neverOptimize;
|
|
|
|
private Vector3 m_LocalGravity = Vector3.zero;
|
|
|
|
private Vector3 m_ObjectMove = Vector3.zero;
|
|
|
|
private Vector3 m_ObjectPrevPosition = Vector3.zero;
|
|
|
|
private float m_BoneTotalLength;
|
|
|
|
private float m_ObjectScale = 1f;
|
|
|
|
private float m_Time;
|
|
|
|
private float m_Weight = 1f;
|
|
|
|
private bool m_DistantDisabled;
|
|
|
|
private List<Particle> m_Particles = new List<Particle>();
|
|
|
|
private void Start()
|
|
{
|
|
SetupParticles();
|
|
}
|
|
|
|
private void FixedUpdate()
|
|
{
|
|
if (m_UpdateMode == UpdateMode.AnimatePhysics)
|
|
{
|
|
PreUpdate();
|
|
}
|
|
}
|
|
|
|
private void Update()
|
|
{
|
|
if (m_UpdateMode != UpdateMode.AnimatePhysics)
|
|
{
|
|
PreUpdate();
|
|
}
|
|
}
|
|
|
|
private void LateUpdate()
|
|
{
|
|
if (m_DistantDisable)
|
|
{
|
|
CheckDistance();
|
|
}
|
|
if (m_Weight > 0f && (!m_DistantDisable || !m_DistantDisabled))
|
|
{
|
|
float deltaTime = Time.deltaTime;
|
|
UpdateDynamicBones(deltaTime);
|
|
}
|
|
}
|
|
|
|
private void PreUpdate()
|
|
{
|
|
if (m_Weight > 0f && (!m_DistantDisable || !m_DistantDisabled))
|
|
{
|
|
InitTransforms();
|
|
}
|
|
}
|
|
|
|
private void CheckDistance()
|
|
{
|
|
Transform referenceObject = m_ReferenceObject;
|
|
if (referenceObject == null && (bool)Camera.main)
|
|
{
|
|
referenceObject = Camera.main.transform;
|
|
}
|
|
if (!referenceObject)
|
|
{
|
|
return;
|
|
}
|
|
bool flag = (referenceObject.position - base.transform.position).sqrMagnitude > m_DistanceToObject * m_DistanceToObject;
|
|
if (flag != m_DistantDisabled)
|
|
{
|
|
if (!flag)
|
|
{
|
|
ResetParticlesPosition();
|
|
}
|
|
m_DistantDisabled = flag;
|
|
}
|
|
}
|
|
|
|
private void OnEnable()
|
|
{
|
|
ResetParticlesPosition();
|
|
}
|
|
|
|
private void OnDisable()
|
|
{
|
|
InitTransforms();
|
|
}
|
|
|
|
private void OnValidate()
|
|
{
|
|
m_UpdateRate = Mathf.Max(m_UpdateRate, 0f);
|
|
m_Damping = Mathf.Clamp01(m_Damping);
|
|
m_Elasticity = Mathf.Clamp01(m_Elasticity);
|
|
m_Stiffness = Mathf.Clamp01(m_Stiffness);
|
|
m_Inert = Mathf.Clamp01(m_Inert);
|
|
m_Radius = Mathf.Max(m_Radius, 0f);
|
|
if (Application.isEditor && Application.isPlaying)
|
|
{
|
|
InitTransforms();
|
|
SetupParticles();
|
|
}
|
|
}
|
|
|
|
private void OnDrawGizmosSelected()
|
|
{
|
|
if (!base.enabled || m_Root == null)
|
|
{
|
|
return;
|
|
}
|
|
if (Application.isEditor && !Application.isPlaying && base.transform.hasChanged)
|
|
{
|
|
InitTransforms();
|
|
SetupParticles();
|
|
}
|
|
Gizmos.color = Color.white;
|
|
for (int i = 0; i < m_Particles.Count; i++)
|
|
{
|
|
Particle particle = m_Particles[i];
|
|
if (particle.m_ParentIndex >= 0)
|
|
{
|
|
Particle particle2 = m_Particles[particle.m_ParentIndex];
|
|
Gizmos.DrawLine(particle.m_Position, particle2.m_Position);
|
|
}
|
|
if (particle.m_Radius > 0f)
|
|
{
|
|
Gizmos.DrawWireSphere(particle.m_Position, particle.m_Radius * m_ObjectScale);
|
|
}
|
|
}
|
|
}
|
|
|
|
public void SetWeight(float w)
|
|
{
|
|
if (m_Weight != w)
|
|
{
|
|
if (w == 0f)
|
|
{
|
|
InitTransforms();
|
|
}
|
|
else if (m_Weight == 0f)
|
|
{
|
|
ResetParticlesPosition();
|
|
}
|
|
m_Weight = w;
|
|
}
|
|
}
|
|
|
|
public float GetWeight()
|
|
{
|
|
return m_Weight;
|
|
}
|
|
|
|
private void UpdateDynamicBones(float t)
|
|
{
|
|
if (m_Root == null)
|
|
{
|
|
return;
|
|
}
|
|
m_ObjectScale = Mathf.Abs(base.transform.lossyScale.x);
|
|
m_ObjectMove = base.transform.position - m_ObjectPrevPosition;
|
|
m_ObjectPrevPosition = base.transform.position;
|
|
int num = 1;
|
|
if (m_UpdateRate > 0f)
|
|
{
|
|
float num2 = 1f / m_UpdateRate;
|
|
m_Time += t;
|
|
num = 0;
|
|
while (m_Time >= num2)
|
|
{
|
|
m_Time -= num2;
|
|
if (++num >= 3)
|
|
{
|
|
m_Time = 0f;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (num > 0)
|
|
{
|
|
for (int i = 0; i < num; i++)
|
|
{
|
|
UpdateParticles1();
|
|
UpdateParticles2();
|
|
m_ObjectMove = Vector3.zero;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
SkipUpdateParticles();
|
|
}
|
|
ApplyParticlesToTransforms();
|
|
}
|
|
|
|
private void SetupParticles()
|
|
{
|
|
m_Particles.Clear();
|
|
if (m_Root == null)
|
|
{
|
|
return;
|
|
}
|
|
m_LocalGravity = m_Root.InverseTransformDirection(m_Gravity);
|
|
m_ObjectScale = Mathf.Abs(base.transform.lossyScale.x);
|
|
m_ObjectPrevPosition = base.transform.position;
|
|
m_ObjectMove = Vector3.zero;
|
|
m_BoneTotalLength = 0f;
|
|
AppendParticles(m_Root, -1, 0f);
|
|
for (int i = 0; i < m_Particles.Count; i++)
|
|
{
|
|
Particle particle = m_Particles[i];
|
|
particle.m_Damping = m_Damping;
|
|
particle.m_Elasticity = m_Elasticity;
|
|
particle.m_Stiffness = m_Stiffness;
|
|
particle.m_Inert = m_Inert;
|
|
particle.m_Radius = m_Radius;
|
|
if (m_BoneTotalLength > 0f)
|
|
{
|
|
float time = particle.m_BoneLength / m_BoneTotalLength;
|
|
if (m_DampingDistrib != null && m_DampingDistrib.keys.Length != 0)
|
|
{
|
|
particle.m_Damping *= m_DampingDistrib.Evaluate(time);
|
|
}
|
|
if (m_ElasticityDistrib != null && m_ElasticityDistrib.keys.Length != 0)
|
|
{
|
|
particle.m_Elasticity *= m_ElasticityDistrib.Evaluate(time);
|
|
}
|
|
if (m_StiffnessDistrib != null && m_StiffnessDistrib.keys.Length != 0)
|
|
{
|
|
particle.m_Stiffness *= m_StiffnessDistrib.Evaluate(time);
|
|
}
|
|
if (m_InertDistrib != null && m_InertDistrib.keys.Length != 0)
|
|
{
|
|
particle.m_Inert *= m_InertDistrib.Evaluate(time);
|
|
}
|
|
if (m_RadiusDistrib != null && m_RadiusDistrib.keys.Length != 0)
|
|
{
|
|
particle.m_Radius *= m_RadiusDistrib.Evaluate(time);
|
|
}
|
|
}
|
|
particle.m_Damping = Mathf.Clamp01(particle.m_Damping);
|
|
particle.m_Elasticity = Mathf.Clamp01(particle.m_Elasticity);
|
|
particle.m_Stiffness = Mathf.Clamp01(particle.m_Stiffness);
|
|
particle.m_Inert = Mathf.Clamp01(particle.m_Inert);
|
|
particle.m_Radius = Mathf.Max(particle.m_Radius, 0f);
|
|
}
|
|
}
|
|
|
|
private void AppendParticles(Transform b, int parentIndex, float boneLength)
|
|
{
|
|
Particle particle = new Particle();
|
|
particle.m_Transform = b;
|
|
particle.m_ParentIndex = parentIndex;
|
|
if ((bool)b)
|
|
{
|
|
particle.m_Position = (particle.m_PrevPosition = b.position);
|
|
particle.m_InitLocalPosition = b.localPosition;
|
|
particle.m_InitLocalRotation = b.localRotation;
|
|
}
|
|
else
|
|
{
|
|
Transform transform = m_Particles[parentIndex].m_Transform;
|
|
if (m_EndLength > 0f)
|
|
{
|
|
Transform parent = transform.parent;
|
|
if ((bool)parent)
|
|
{
|
|
particle.m_EndOffset = transform.InverseTransformPoint(transform.position * 2f - parent.position) * m_EndLength;
|
|
}
|
|
else
|
|
{
|
|
particle.m_EndOffset = new Vector3(m_EndLength, 0f, 0f);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
particle.m_EndOffset = transform.InverseTransformPoint(base.transform.TransformDirection(m_EndOffset) + transform.position);
|
|
}
|
|
particle.m_Position = (particle.m_PrevPosition = transform.TransformPoint(particle.m_EndOffset));
|
|
}
|
|
if (parentIndex >= 0)
|
|
{
|
|
boneLength += (m_Particles[parentIndex].m_Transform.position - particle.m_Position).magnitude;
|
|
particle.m_BoneLength = boneLength;
|
|
m_BoneTotalLength = Mathf.Max(m_BoneTotalLength, boneLength);
|
|
}
|
|
int count = m_Particles.Count;
|
|
m_Particles.Add(particle);
|
|
if (!b)
|
|
{
|
|
return;
|
|
}
|
|
for (int i = 0; i < b.childCount; i++)
|
|
{
|
|
bool flag = false;
|
|
if (m_Exclusions != null)
|
|
{
|
|
for (int j = 0; j < m_Exclusions.Count; j++)
|
|
{
|
|
if (m_Exclusions[j] == b.GetChild(i))
|
|
{
|
|
flag = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (!flag)
|
|
{
|
|
AppendParticles(b.GetChild(i), count, boneLength);
|
|
}
|
|
}
|
|
if (b.childCount == 0 && (m_EndLength > 0f || m_EndOffset != Vector3.zero))
|
|
{
|
|
AppendParticles(null, count, boneLength);
|
|
}
|
|
}
|
|
|
|
private void InitTransforms()
|
|
{
|
|
for (int i = 0; i < m_Particles.Count; i++)
|
|
{
|
|
Particle particle = m_Particles[i];
|
|
if ((bool)particle.m_Transform)
|
|
{
|
|
particle.m_Transform.localPosition = particle.m_InitLocalPosition;
|
|
particle.m_Transform.localRotation = particle.m_InitLocalRotation;
|
|
}
|
|
}
|
|
}
|
|
|
|
private void ResetParticlesPosition()
|
|
{
|
|
for (int i = 0; i < m_Particles.Count; i++)
|
|
{
|
|
Particle particle = m_Particles[i];
|
|
if ((bool)particle.m_Transform)
|
|
{
|
|
particle.m_Position = (particle.m_PrevPosition = particle.m_Transform.position);
|
|
continue;
|
|
}
|
|
Transform transform = m_Particles[particle.m_ParentIndex].m_Transform;
|
|
particle.m_Position = (particle.m_PrevPosition = transform.TransformPoint(particle.m_EndOffset));
|
|
}
|
|
m_ObjectPrevPosition = base.transform.position;
|
|
}
|
|
|
|
private void UpdateParticles1()
|
|
{
|
|
Vector3 gravity = m_Gravity;
|
|
Vector3 normalized = m_Gravity.normalized;
|
|
Vector3 lhs = m_Root.TransformDirection(m_LocalGravity);
|
|
Vector3 vector = normalized * Mathf.Max(Vector3.Dot(lhs, normalized), 0f);
|
|
gravity -= vector;
|
|
gravity = (gravity + m_Force) * m_ObjectScale;
|
|
for (int i = 0; i < m_Particles.Count; i++)
|
|
{
|
|
Particle particle = m_Particles[i];
|
|
if (particle.m_ParentIndex >= 0)
|
|
{
|
|
Vector3 vector2 = particle.m_Position - particle.m_PrevPosition;
|
|
Vector3 vector3 = m_ObjectMove * particle.m_Inert;
|
|
particle.m_PrevPosition = particle.m_Position + vector3;
|
|
particle.m_Position += vector2 * (1f - particle.m_Damping) + gravity + vector3;
|
|
}
|
|
else
|
|
{
|
|
particle.m_PrevPosition = particle.m_Position;
|
|
particle.m_Position = particle.m_Transform.position;
|
|
}
|
|
}
|
|
}
|
|
|
|
private void UpdateParticles2()
|
|
{
|
|
Plane plane = default(Plane);
|
|
for (int i = 1; i < m_Particles.Count; i++)
|
|
{
|
|
Particle particle = m_Particles[i];
|
|
Particle particle2 = m_Particles[particle.m_ParentIndex];
|
|
if (!particle2.m_Transform)
|
|
{
|
|
continue;
|
|
}
|
|
float num = ((!particle.m_Transform) ? particle2.m_Transform.localToWorldMatrix.MultiplyVector(particle.m_EndOffset).magnitude : (particle2.m_Transform.position - particle.m_Transform.position).magnitude);
|
|
float num2 = Mathf.Lerp(1f, particle.m_Stiffness, m_Weight);
|
|
if (num2 > 0f || particle.m_Elasticity > 0f)
|
|
{
|
|
Matrix4x4 localToWorldMatrix = particle2.m_Transform.localToWorldMatrix;
|
|
localToWorldMatrix.SetColumn(3, particle2.m_Position);
|
|
Vector3 vector = ((!particle.m_Transform) ? localToWorldMatrix.MultiplyPoint3x4(particle.m_EndOffset) : localToWorldMatrix.MultiplyPoint3x4(particle.m_Transform.localPosition));
|
|
Vector3 vector2 = vector - particle.m_Position;
|
|
particle.m_Position += vector2 * particle.m_Elasticity;
|
|
if (num2 > 0f)
|
|
{
|
|
vector2 = vector - particle.m_Position;
|
|
float magnitude = vector2.magnitude;
|
|
float num3 = num * (1f - num2) * 2f;
|
|
if (magnitude > num3)
|
|
{
|
|
particle.m_Position += vector2 * ((magnitude - num3) / magnitude);
|
|
}
|
|
}
|
|
}
|
|
if (m_Colliders != null)
|
|
{
|
|
float particleRadius = particle.m_Radius * m_ObjectScale;
|
|
for (int j = 0; j < m_Colliders.Count; j++)
|
|
{
|
|
DynamicBoneCollider dynamicBoneCollider = m_Colliders[j];
|
|
if ((bool)dynamicBoneCollider && dynamicBoneCollider.enabled)
|
|
{
|
|
dynamicBoneCollider.Collide(ref particle.m_Position, particleRadius);
|
|
}
|
|
}
|
|
}
|
|
if (m_FreezeAxis != 0)
|
|
{
|
|
switch (m_FreezeAxis)
|
|
{
|
|
case FreezeAxis.X:
|
|
plane.SetNormalAndPosition(particle2.m_Transform.right, particle2.m_Position);
|
|
break;
|
|
case FreezeAxis.Y:
|
|
plane.SetNormalAndPosition(particle2.m_Transform.up, particle2.m_Position);
|
|
break;
|
|
case FreezeAxis.Z:
|
|
plane.SetNormalAndPosition(particle2.m_Transform.forward, particle2.m_Position);
|
|
break;
|
|
}
|
|
particle.m_Position -= plane.normal * plane.GetDistanceToPoint(particle.m_Position);
|
|
}
|
|
Vector3 vector3 = particle2.m_Position - particle.m_Position;
|
|
float magnitude2 = vector3.magnitude;
|
|
if (magnitude2 > 0f)
|
|
{
|
|
particle.m_Position += vector3 * ((magnitude2 - num) / magnitude2);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void SkipUpdateParticles()
|
|
{
|
|
for (int i = 0; i < m_Particles.Count; i++)
|
|
{
|
|
Particle particle = m_Particles[i];
|
|
if (particle.m_ParentIndex >= 0)
|
|
{
|
|
particle.m_PrevPosition += m_ObjectMove;
|
|
particle.m_Position += m_ObjectMove;
|
|
Particle particle2 = m_Particles[particle.m_ParentIndex];
|
|
if (!particle2.m_Transform)
|
|
{
|
|
continue;
|
|
}
|
|
float num = ((!particle.m_Transform) ? particle2.m_Transform.localToWorldMatrix.MultiplyVector(particle.m_EndOffset).magnitude : (particle2.m_Transform.position - particle.m_Transform.position).magnitude);
|
|
float num2 = Mathf.Lerp(1f, particle.m_Stiffness, m_Weight);
|
|
if (num2 > 0f)
|
|
{
|
|
Matrix4x4 localToWorldMatrix = particle2.m_Transform.localToWorldMatrix;
|
|
localToWorldMatrix.SetColumn(3, particle2.m_Position);
|
|
Vector3 vector = ((!particle.m_Transform) ? localToWorldMatrix.MultiplyPoint3x4(particle.m_EndOffset) : localToWorldMatrix.MultiplyPoint3x4(particle.m_Transform.localPosition));
|
|
Vector3 vector2 = vector - particle.m_Position;
|
|
float magnitude = vector2.magnitude;
|
|
float num3 = num * (1f - num2) * 2f;
|
|
if (magnitude > num3)
|
|
{
|
|
particle.m_Position += vector2 * ((magnitude - num3) / magnitude);
|
|
}
|
|
}
|
|
Vector3 vector3 = particle2.m_Position - particle.m_Position;
|
|
float magnitude2 = vector3.magnitude;
|
|
if (magnitude2 > 0f)
|
|
{
|
|
particle.m_Position += vector3 * ((magnitude2 - num) / magnitude2);
|
|
}
|
|
}
|
|
else if ((bool)particle.m_Transform)
|
|
{
|
|
particle.m_PrevPosition = particle.m_Position;
|
|
particle.m_Position = particle.m_Transform.position;
|
|
}
|
|
}
|
|
}
|
|
|
|
private static Vector3 MirrorVector(Vector3 v, Vector3 axis)
|
|
{
|
|
return v - axis * (Vector3.Dot(v, axis) * 2f);
|
|
}
|
|
|
|
private void ApplyParticlesToTransforms()
|
|
{
|
|
for (int i = 1; i < m_Particles.Count; i++)
|
|
{
|
|
Particle particle = m_Particles[i];
|
|
Particle particle2 = m_Particles[particle.m_ParentIndex];
|
|
if (particle2.m_Transform.childCount <= 1)
|
|
{
|
|
Vector3 direction = ((!particle.m_Transform) ? particle.m_EndOffset : particle.m_Transform.localPosition);
|
|
Vector3 toDirection = particle.m_Position - particle2.m_Position;
|
|
Quaternion quaternion = Quaternion.FromToRotation(particle2.m_Transform.TransformDirection(direction), toDirection);
|
|
particle2.m_Transform.rotation = quaternion * particle2.m_Transform.rotation;
|
|
}
|
|
if ((bool)particle.m_Transform)
|
|
{
|
|
particle.m_Transform.position = particle.m_Position;
|
|
}
|
|
}
|
|
}
|
|
}
|