461 lines
14 KiB
C#
461 lines
14 KiB
C#
|
using UnityEngine;
|
|||
|
|
|||
|
[AddComponentMenu("Dynamic Bone/Dynamic Bone Collider")]
|
|||
|
public class DynamicBoneCollider : DynamicBoneColliderBase
|
|||
|
{
|
|||
|
#if UNITY_5_3_OR_NEWER
|
|||
|
[Tooltip("The radius of the sphere or capsule.")]
|
|||
|
#endif
|
|||
|
public float m_Radius = 0.5f;
|
|||
|
|
|||
|
#if UNITY_5_3_OR_NEWER
|
|||
|
[Tooltip("The height of the capsule.")]
|
|||
|
#endif
|
|||
|
public float m_Height = 0;
|
|||
|
|
|||
|
#if UNITY_5_3_OR_NEWER
|
|||
|
[Tooltip("The other radius of the capsule.")]
|
|||
|
#endif
|
|||
|
public float m_Radius2 = 0;
|
|||
|
|
|||
|
// prepare data
|
|||
|
float m_ScaledRadius;
|
|||
|
float m_ScaledRadius2;
|
|||
|
Vector3 m_C0;
|
|||
|
Vector3 m_C1;
|
|||
|
float m_C01Distance;
|
|||
|
int m_CollideType;
|
|||
|
|
|||
|
void OnValidate()
|
|||
|
{
|
|||
|
m_Radius = Mathf.Max(m_Radius, 0);
|
|||
|
m_Height = Mathf.Max(m_Height, 0);
|
|||
|
m_Radius2 = Mathf.Max(m_Radius2, 0);
|
|||
|
}
|
|||
|
|
|||
|
public override void Prepare()
|
|||
|
{
|
|||
|
float scale = Mathf.Abs(transform.lossyScale.x);
|
|||
|
float halfHeight = m_Height * 0.5f;
|
|||
|
|
|||
|
if (m_Radius2 <= 0 || Mathf.Abs(m_Radius - m_Radius2) < 0.01f)
|
|||
|
{
|
|||
|
m_ScaledRadius = m_Radius * scale;
|
|||
|
|
|||
|
float h = halfHeight - m_Radius;
|
|||
|
if (h <= 0)
|
|||
|
{
|
|||
|
m_C0 = transform.TransformPoint(m_Center);
|
|||
|
|
|||
|
if (m_Bound == Bound.Outside)
|
|||
|
{
|
|||
|
m_CollideType = 0;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
m_CollideType = 1;
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
Vector3 c0 = m_Center;
|
|||
|
Vector3 c1 = m_Center;
|
|||
|
|
|||
|
switch (m_Direction)
|
|||
|
{
|
|||
|
case Direction.X:
|
|||
|
c0.x += h;
|
|||
|
c1.x -= h;
|
|||
|
break;
|
|||
|
case Direction.Y:
|
|||
|
c0.y += h;
|
|||
|
c1.y -= h;
|
|||
|
break;
|
|||
|
case Direction.Z:
|
|||
|
c0.z += h;
|
|||
|
c1.z -= h;
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
m_C0 = transform.TransformPoint(c0);
|
|||
|
m_C1 = transform.TransformPoint(c1);
|
|||
|
m_C01Distance = (m_C1 - m_C0).magnitude;
|
|||
|
|
|||
|
if (m_Bound == Bound.Outside)
|
|||
|
{
|
|||
|
m_CollideType = 2;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
m_CollideType = 3;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
float r = Mathf.Max(m_Radius, m_Radius2);
|
|||
|
if (halfHeight - r <= 0)
|
|||
|
{
|
|||
|
m_ScaledRadius = r * scale;
|
|||
|
m_C0 = transform.TransformPoint(m_Center);
|
|||
|
|
|||
|
if (m_Bound == Bound.Outside)
|
|||
|
{
|
|||
|
m_CollideType = 0;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
m_CollideType = 1;
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
m_ScaledRadius = m_Radius * scale;
|
|||
|
m_ScaledRadius2 = m_Radius2 * scale;
|
|||
|
|
|||
|
float h0 = halfHeight - m_Radius;
|
|||
|
float h1 = halfHeight - m_Radius2;
|
|||
|
Vector3 c0 = m_Center;
|
|||
|
Vector3 c1 = m_Center;
|
|||
|
|
|||
|
switch (m_Direction)
|
|||
|
{
|
|||
|
case Direction.X:
|
|||
|
c0.x += h0;
|
|||
|
c1.x -= h1;
|
|||
|
break;
|
|||
|
case Direction.Y:
|
|||
|
c0.y += h0;
|
|||
|
c1.y -= h1;
|
|||
|
break;
|
|||
|
case Direction.Z:
|
|||
|
c0.z += h0;
|
|||
|
c1.z -= h1;
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
m_C0 = transform.TransformPoint(c0);
|
|||
|
m_C1 = transform.TransformPoint(c1);
|
|||
|
m_C01Distance = (m_C1 - m_C0).magnitude;
|
|||
|
|
|||
|
if (m_Bound == Bound.Outside)
|
|||
|
{
|
|||
|
m_CollideType = 4;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
m_CollideType = 5;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public override bool Collide(ref Vector3 particlePosition, float particleRadius)
|
|||
|
{
|
|||
|
switch (m_CollideType)
|
|||
|
{
|
|||
|
case 0:
|
|||
|
return OutsideSphere(ref particlePosition, particleRadius, m_C0, m_ScaledRadius);
|
|||
|
case 1:
|
|||
|
return InsideSphere(ref particlePosition, particleRadius, m_C0, m_ScaledRadius);
|
|||
|
case 2:
|
|||
|
return OutsideCapsule(ref particlePosition, particleRadius, m_C0, m_C1, m_ScaledRadius, m_C01Distance);
|
|||
|
case 3:
|
|||
|
return InsideCapsule(ref particlePosition, particleRadius, m_C0, m_C1, m_ScaledRadius, m_C01Distance);
|
|||
|
case 4:
|
|||
|
return OutsideCapsule2(ref particlePosition, particleRadius, m_C0, m_C1, m_ScaledRadius, m_ScaledRadius2, m_C01Distance);
|
|||
|
case 5:
|
|||
|
return InsideCapsule2(ref particlePosition, particleRadius, m_C0, m_C1, m_ScaledRadius, m_ScaledRadius2, m_C01Distance);
|
|||
|
}
|
|||
|
return false;
|
|||
|
}
|
|||
|
|
|||
|
static bool OutsideSphere(ref Vector3 particlePosition, float particleRadius, Vector3 sphereCenter, float sphereRadius)
|
|||
|
{
|
|||
|
float r = sphereRadius + particleRadius;
|
|||
|
float r2 = r * r;
|
|||
|
Vector3 d = particlePosition - sphereCenter;
|
|||
|
float dlen2 = d.sqrMagnitude;
|
|||
|
|
|||
|
// if is inside sphere, project onto sphere surface
|
|||
|
if (dlen2 > 0 && dlen2 < r2)
|
|||
|
{
|
|||
|
float dlen = Mathf.Sqrt(dlen2);
|
|||
|
particlePosition = sphereCenter + d * (r / dlen);
|
|||
|
return true;
|
|||
|
}
|
|||
|
return false;
|
|||
|
}
|
|||
|
|
|||
|
static bool InsideSphere(ref Vector3 particlePosition, float particleRadius, Vector3 sphereCenter, float sphereRadius)
|
|||
|
{
|
|||
|
float r = sphereRadius - particleRadius;
|
|||
|
float r2 = r * r;
|
|||
|
Vector3 d = particlePosition - sphereCenter;
|
|||
|
float dlen2 = d.sqrMagnitude;
|
|||
|
|
|||
|
// if is outside sphere, project onto sphere surface
|
|||
|
if (dlen2 > r2)
|
|||
|
{
|
|||
|
float dlen = Mathf.Sqrt(dlen2);
|
|||
|
particlePosition = sphereCenter + d * (r / dlen);
|
|||
|
return true;
|
|||
|
}
|
|||
|
return false;
|
|||
|
}
|
|||
|
|
|||
|
static bool OutsideCapsule(ref Vector3 particlePosition, float particleRadius, Vector3 capsuleP0, Vector3 capsuleP1, float capsuleRadius, float dirlen)
|
|||
|
{
|
|||
|
float r = capsuleRadius + particleRadius;
|
|||
|
float r2 = r * r;
|
|||
|
Vector3 dir = capsuleP1 - capsuleP0;
|
|||
|
Vector3 d = particlePosition - capsuleP0;
|
|||
|
float t = Vector3.Dot(d, dir);
|
|||
|
|
|||
|
if (t <= 0)
|
|||
|
{
|
|||
|
// check sphere1
|
|||
|
float dlen2 = d.sqrMagnitude;
|
|||
|
if (dlen2 > 0 && dlen2 < r2)
|
|||
|
{
|
|||
|
float dlen = Mathf.Sqrt(dlen2);
|
|||
|
particlePosition = capsuleP0 + d * (r / dlen);
|
|||
|
return true;
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
float dirlen2 = dirlen * dirlen;
|
|||
|
if (t >= dirlen2)
|
|||
|
{
|
|||
|
// check sphere2
|
|||
|
d = particlePosition - capsuleP1;
|
|||
|
float dlen2 = d.sqrMagnitude;
|
|||
|
if (dlen2 > 0 && dlen2 < r2)
|
|||
|
{
|
|||
|
float dlen = Mathf.Sqrt(dlen2);
|
|||
|
particlePosition = capsuleP1 + d * (r / dlen);
|
|||
|
return true;
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
// check cylinder
|
|||
|
Vector3 q = d - dir * (t / dirlen2);
|
|||
|
float qlen2 = q.sqrMagnitude;
|
|||
|
if (qlen2 > 0 && qlen2 < r2)
|
|||
|
{
|
|||
|
float qlen = Mathf.Sqrt(qlen2);
|
|||
|
particlePosition += q * ((r - qlen) / qlen);
|
|||
|
return true;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
return false;
|
|||
|
}
|
|||
|
|
|||
|
static bool InsideCapsule(ref Vector3 particlePosition, float particleRadius, Vector3 capsuleP0, Vector3 capsuleP1, float capsuleRadius, float dirlen)
|
|||
|
{
|
|||
|
float r = capsuleRadius - particleRadius;
|
|||
|
float r2 = r * r;
|
|||
|
Vector3 dir = capsuleP1 - capsuleP0;
|
|||
|
Vector3 d = particlePosition - capsuleP0;
|
|||
|
float t = Vector3.Dot(d, dir);
|
|||
|
|
|||
|
if (t <= 0)
|
|||
|
{
|
|||
|
// check sphere1
|
|||
|
float dlen2 = d.sqrMagnitude;
|
|||
|
if (dlen2 > r2)
|
|||
|
{
|
|||
|
float dlen = Mathf.Sqrt(dlen2);
|
|||
|
particlePosition = capsuleP0 + d * (r / dlen);
|
|||
|
return true;
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
float dirlen2 = dirlen * dirlen;
|
|||
|
if (t >= dirlen2)
|
|||
|
{
|
|||
|
// check sphere2
|
|||
|
d = particlePosition - capsuleP1;
|
|||
|
float dlen2 = d.sqrMagnitude;
|
|||
|
if (dlen2 > r2)
|
|||
|
{
|
|||
|
float dlen = Mathf.Sqrt(dlen2);
|
|||
|
particlePosition = capsuleP1 + d * (r / dlen);
|
|||
|
return true;
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
// check cylinder
|
|||
|
Vector3 q = d - dir * (t / dirlen2);
|
|||
|
float qlen2 = q.sqrMagnitude;
|
|||
|
if (qlen2 > r2)
|
|||
|
{
|
|||
|
float qlen = Mathf.Sqrt(qlen2);
|
|||
|
particlePosition += q * ((r - qlen) / qlen);
|
|||
|
return true;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
return false;
|
|||
|
}
|
|||
|
|
|||
|
static bool OutsideCapsule2(ref Vector3 particlePosition, float particleRadius, Vector3 capsuleP0, Vector3 capsuleP1, float capsuleRadius0, float capsuleRadius1, float dirlen)
|
|||
|
{
|
|||
|
Vector3 dir = capsuleP1 - capsuleP0;
|
|||
|
Vector3 d = particlePosition - capsuleP0;
|
|||
|
float t = Vector3.Dot(d, dir);
|
|||
|
|
|||
|
if (t <= 0)
|
|||
|
{
|
|||
|
// check sphere1
|
|||
|
float r = capsuleRadius0 + particleRadius;
|
|||
|
float r2 = r * r;
|
|||
|
float dlen2 = d.sqrMagnitude;
|
|||
|
if (dlen2 > 0 && dlen2 < r2)
|
|||
|
{
|
|||
|
float dlen = Mathf.Sqrt(dlen2);
|
|||
|
particlePosition = capsuleP0 + d * (r / dlen);
|
|||
|
return true;
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
float dirlen2 = dirlen * dirlen;
|
|||
|
if (t >= dirlen2)
|
|||
|
{
|
|||
|
// check sphere2
|
|||
|
float r = capsuleRadius1 + particleRadius;
|
|||
|
float r2 = r * r;
|
|||
|
d = particlePosition - capsuleP1;
|
|||
|
float dlen2 = d.sqrMagnitude;
|
|||
|
if (dlen2 > 0 && dlen2 < r2)
|
|||
|
{
|
|||
|
float dlen = Mathf.Sqrt(dlen2);
|
|||
|
particlePosition = capsuleP1 + d * (r / dlen);
|
|||
|
return true;
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
// check cylinder
|
|||
|
Vector3 q = d - dir * (t / dirlen2);
|
|||
|
float qlen2 = q.sqrMagnitude;
|
|||
|
|
|||
|
float klen = Vector3.Dot(d, dir / dirlen);
|
|||
|
float r = Mathf.Lerp(capsuleRadius0, capsuleRadius1, klen / dirlen) + particleRadius;
|
|||
|
float r2 = r * r;
|
|||
|
|
|||
|
if (qlen2 > 0 && qlen2 < r2)
|
|||
|
{
|
|||
|
float qlen = Mathf.Sqrt(qlen2);
|
|||
|
particlePosition += q * ((r - qlen) / qlen);
|
|||
|
return true;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
return false;
|
|||
|
}
|
|||
|
|
|||
|
static bool InsideCapsule2(ref Vector3 particlePosition, float particleRadius, Vector3 capsuleP0, Vector3 capsuleP1, float capsuleRadius0, float capsuleRadius1, float dirlen)
|
|||
|
{
|
|||
|
Vector3 dir = capsuleP1 - capsuleP0;
|
|||
|
Vector3 d = particlePosition - capsuleP0;
|
|||
|
float t = Vector3.Dot(d, dir);
|
|||
|
|
|||
|
if (t <= 0)
|
|||
|
{
|
|||
|
// check sphere1
|
|||
|
float r = capsuleRadius0 - particleRadius;
|
|||
|
float r2 = r * r;
|
|||
|
float dlen2 = d.sqrMagnitude;
|
|||
|
if (dlen2 > r2)
|
|||
|
{
|
|||
|
float dlen = Mathf.Sqrt(dlen2);
|
|||
|
particlePosition = capsuleP0 + d * (r / dlen);
|
|||
|
return true;
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
float dirlen2 = dirlen * dirlen;
|
|||
|
if (t >= dirlen2)
|
|||
|
{
|
|||
|
// check sphere2
|
|||
|
float r = capsuleRadius1 - particleRadius;
|
|||
|
float r2 = r * r;
|
|||
|
d = particlePosition - capsuleP1;
|
|||
|
float dlen2 = d.sqrMagnitude;
|
|||
|
if (dlen2 > r2)
|
|||
|
{
|
|||
|
float dlen = Mathf.Sqrt(dlen2);
|
|||
|
particlePosition = capsuleP1 + d * (r / dlen);
|
|||
|
return true;
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
// check cylinder
|
|||
|
Vector3 q = d - dir * (t / dirlen2);
|
|||
|
float qlen2 = q.sqrMagnitude;
|
|||
|
|
|||
|
float klen = Vector3.Dot(d, dir / dirlen);
|
|||
|
float r = Mathf.Lerp(capsuleRadius0, capsuleRadius1, klen / dirlen) - particleRadius;
|
|||
|
float r2 = r * r;
|
|||
|
|
|||
|
if (qlen2 > r2)
|
|||
|
{
|
|||
|
float qlen = Mathf.Sqrt(qlen2);
|
|||
|
particlePosition += q * ((r - qlen) / qlen);
|
|||
|
return true;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
return false;
|
|||
|
}
|
|||
|
|
|||
|
void OnDrawGizmosSelected()
|
|||
|
{
|
|||
|
if (!enabled)
|
|||
|
return;
|
|||
|
|
|||
|
Prepare();
|
|||
|
|
|||
|
if (m_Bound == Bound.Outside)
|
|||
|
{
|
|||
|
Gizmos.color = Color.yellow;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
Gizmos.color = Color.magenta;
|
|||
|
}
|
|||
|
|
|||
|
switch (m_CollideType)
|
|||
|
{
|
|||
|
case 0:
|
|||
|
case 1:
|
|||
|
Gizmos.DrawWireSphere(m_C0, m_ScaledRadius);
|
|||
|
break;
|
|||
|
case 2:
|
|||
|
case 3:
|
|||
|
DrawCapsule(m_C0, m_C1, m_ScaledRadius, m_ScaledRadius);
|
|||
|
break;
|
|||
|
case 4:
|
|||
|
case 5:
|
|||
|
DrawCapsule(m_C0, m_C1, m_ScaledRadius, m_ScaledRadius2);
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
static void DrawCapsule(Vector3 c0, Vector3 c1, float radius0, float radius1)
|
|||
|
{
|
|||
|
Gizmos.DrawLine(c0, c1);
|
|||
|
Gizmos.DrawWireSphere(c0, radius0);
|
|||
|
Gizmos.DrawWireSphere(c1, radius1);
|
|||
|
}
|
|||
|
}
|