using UnityEngine; using System.Collections; namespace RootMotion.Dynamics { /// /// Contains common functionality and helpers for creating any type of ragdolls. /// public abstract class RagdollCreator : MonoBehaviour { [System.Serializable] public enum ColliderType { Box, Capsule } [System.Serializable] public enum JointType { Configurable, Character } [System.Serializable] public enum Direction { X = 0, Y = 1, Z = 2 } public struct CreateJointParams { public Rigidbody rigidbody; public Rigidbody connectedBody; public Transform child; public Vector3 worldSwingAxis; public Limits limits; public JointType type; public struct Limits { public float minSwing; public float maxSwing; public float swing2; public float twist; public Limits(float minSwing, float maxSwing, float swing2, float twist) { this.minSwing = minSwing; this.maxSwing = maxSwing; this.swing2 = swing2; this.twist = twist; } } public CreateJointParams(Rigidbody rigidbody, Rigidbody connectedBody, Transform child, Vector3 worldSwingAxis, Limits limits, JointType type) { this.rigidbody = rigidbody; this.connectedBody = connectedBody; this.child = child; this.worldSwingAxis = worldSwingAxis; this.limits = limits; this.type = type; } } public static void ClearAll(Transform root) { if (root == null) return; // If there is a Humanoid Animator, use it to find the first bone to clear Transform r = root; Animator animator = root.GetComponentInChildren(); if (animator != null && animator.isHuman) { // Check if Optimize GameObjects is used Transform hips = animator.GetBoneTransform(HumanBodyBones.Hips); if (hips != null) { Transform[] hipChildren = hips.GetComponentsInChildren(); if (hipChildren.Length > 2) { r = hips; } } } Transform[] transforms = r.GetComponentsInChildren(); if (transforms.Length < 2) return; for (int i = animator != null && animator.isHuman ? 0 : 1; i < transforms.Length; i++) { ClearTransform(transforms[i]); } } protected static void ClearTransform(Transform transform) { if (transform == null) return; if (transform.name == "Foot Collider") { DestroyImmediate(transform.gameObject); return; } Collider[] colliders = transform.GetComponents(); foreach (Collider collider in colliders) { if (collider != null && !collider.isTrigger) GameObject.DestroyImmediate(collider); } var joint = transform.GetComponent(); if (joint != null) GameObject.DestroyImmediate(joint); var rigidbody = transform.GetComponent(); if (rigidbody != null) GameObject.DestroyImmediate(rigidbody); } protected static Collider CreateCollider(Transform t, Vector3 startPoint, Vector3 endPoint, ColliderType colliderType, float lengthOverlap, float width, Transform rigidbodyT = null) { if (rigidbodyT == null) rigidbodyT = t; Vector3 direction = endPoint - startPoint; float height = direction.magnitude * (1f + lengthOverlap); Vector3 heightAxis = AxisTools.GetAxisVectorToDirection(t, direction); rigidbodyT.gameObject.AddComponent(); float scaleF = GetScaleF(t); switch (colliderType) { case ColliderType.Capsule: CapsuleCollider capsule = t.gameObject.AddComponent(); capsule.height = Mathf.Abs(height / scaleF); capsule.radius = Mathf.Abs((width * 0.75f) / scaleF); capsule.direction = DirectionVector3ToInt(heightAxis); capsule.center = t.InverseTransformPoint(Vector3.Lerp(startPoint, endPoint, 0.5f)); return capsule as Collider; case ColliderType.Box: Vector3 size = Vector3.Scale(heightAxis, new Vector3(height, height, height)); if (size.x == 0f) size.x = width; if (size.y == 0f) size.y = width; if (size.z == 0f) size.z = width; BoxCollider box = t.gameObject.AddComponent(); box.size = size / scaleF; box.size = new Vector3(Mathf.Abs(box.size.x), Mathf.Abs(box.size.y), Mathf.Abs(box.size.z)); box.center = t.InverseTransformPoint(Vector3.Lerp(startPoint, endPoint, 0.5f)); return box as Collider; } return null; } protected static void CreateCollider(Transform t, Vector3 startPoint, Vector3 endPoint, ColliderType colliderType, float lengthOverlap, float width, float proportionAspect, Vector3 widthDirection) { if (colliderType == ColliderType.Capsule) { CreateCollider(t, startPoint, endPoint, colliderType, lengthOverlap, width * proportionAspect); return; } Vector3 direction = endPoint - startPoint; float height = direction.magnitude * (1f + lengthOverlap); Vector3 heightAxis = AxisTools.GetAxisVectorToDirection(t, direction); Vector3 widthAxis = AxisTools.GetAxisVectorToDirection(t, widthDirection); if (widthAxis == heightAxis) { Debug.LogWarning("Width axis = height axis on " + t.name, t); widthAxis = new Vector3(heightAxis.y, heightAxis.z, heightAxis.x); } t.gameObject.AddComponent(); Vector3 heightAdd = Vector3.Scale(heightAxis, new Vector3(height, height, height)); Vector3 widthAdd = Vector3.Scale(widthAxis, new Vector3(width, width, width)); Vector3 size = heightAdd + widthAdd; if (size.x == 0f) size.x = width * proportionAspect; if (size.y == 0f) size.y = width * proportionAspect; if (size.z == 0f) size.z = width * proportionAspect; BoxCollider box = t.gameObject.AddComponent(); box.size = size / GetScaleF(t); box.center = t.InverseTransformPoint(Vector3.Lerp(startPoint, endPoint, 0.5f)); } protected static float GetScaleF(Transform t) { Vector3 scale = t.lossyScale; return (scale.x + scale.y + scale.z) / 3f; } protected static Vector3 Abs(Vector3 v) { Vector3Abs(ref v); return v; } protected static void Vector3Abs(ref Vector3 v) { v.x = Mathf.Abs(v.x); v.y = Mathf.Abs(v.y); v.z = Mathf.Abs(v.z); } protected static Vector3 DirectionIntToVector3(int dir) { if (dir == 0) return Vector3.right; if (dir == 1) return Vector3.up; return Vector3.forward; } protected static Vector3 DirectionToVector3(Direction dir) { if (dir == Direction.X) return Vector3.right; if (dir == Direction.Y) return Vector3.up; return Vector3.forward; } protected static int DirectionVector3ToInt(Vector3 dir) { float dotX = Vector3.Dot(dir, Vector3.right); float dotY = Vector3.Dot(dir, Vector3.up); float dotZ = Vector3.Dot(dir, Vector3.forward); float absDotX = Mathf.Abs(dotX); float absDotY = Mathf.Abs(dotY); float absDotZ = Mathf.Abs(dotZ); int rotatedDirection = 0; if (absDotY > absDotX && absDotY > absDotZ) rotatedDirection = 1; if (absDotZ > absDotX && absDotZ > absDotY) rotatedDirection = 2; return rotatedDirection; } protected static Vector3 GetLocalOrthoDirection(Transform transform, Vector3 worldDir) { worldDir = worldDir.normalized; float dotX = Vector3.Dot(worldDir, transform.right); float dotY = Vector3.Dot(worldDir, transform.up); float dotZ = Vector3.Dot(worldDir, transform.forward); float absDotX = Mathf.Abs(dotX); float absDotY = Mathf.Abs(dotY); float absDotZ = Mathf.Abs(dotZ); Vector3 orthoDirection = Vector3.right; if (absDotY > absDotX && absDotY > absDotZ) orthoDirection = Vector3.up; if (absDotZ > absDotX && absDotZ > absDotY) orthoDirection = Vector3.forward; if (Vector3.Dot(worldDir, transform.rotation * orthoDirection) < 0f) orthoDirection = -orthoDirection; return orthoDirection; } protected static Rigidbody GetConnectedBody(Transform bone, ref Transform[] bones) { if (bone.parent == null) return null; foreach (Transform bone2 in bones) { if (bone.parent == bone2 && bone2.GetComponent() != null) return bone2.GetComponent(); } return GetConnectedBody(bone.parent, ref bones); } protected static void CreateJoint(CreateJointParams p) { Vector3 axis = GetLocalOrthoDirection(p.rigidbody.transform, p.worldSwingAxis); Vector3 twistAxis = Vector3.forward; if (p.child != null) { twistAxis = GetLocalOrthoDirection(p.rigidbody.transform, p.child.position - p.rigidbody.transform.position); } else if (p.connectedBody != null) { twistAxis = GetLocalOrthoDirection(p.rigidbody.transform, p.rigidbody.transform.position - p.connectedBody.transform.position); } Vector3 secondaryAxis = Vector3.Cross(axis, twistAxis); if (p.type == JointType.Configurable) { ConfigurableJoint j = p.rigidbody.gameObject.AddComponent(); j.connectedBody = p.connectedBody; ConfigurableJointMotion linearMotion = p.connectedBody != null ? ConfigurableJointMotion.Locked : ConfigurableJointMotion.Free; ConfigurableJointMotion angularMotion = p.connectedBody != null ? ConfigurableJointMotion.Limited : ConfigurableJointMotion.Free; j.xMotion = linearMotion; j.yMotion = linearMotion; j.zMotion = linearMotion; j.angularXMotion = angularMotion; j.angularYMotion = angularMotion; j.angularZMotion = angularMotion; if (p.connectedBody != null) { j.axis = axis; j.secondaryAxis = secondaryAxis; j.lowAngularXLimit = ToSoftJointLimit(p.limits.minSwing); j.highAngularXLimit = ToSoftJointLimit(p.limits.maxSwing); j.angularYLimit = ToSoftJointLimit(p.limits.swing2); j.angularZLimit = ToSoftJointLimit(p.limits.twist); } j.anchor = Vector3.zero; } else { if (p.connectedBody == null) return; CharacterJoint j = p.rigidbody.gameObject.AddComponent(); j.connectedBody = p.connectedBody; j.axis = axis; j.swingAxis = secondaryAxis; j.lowTwistLimit = ToSoftJointLimit(p.limits.minSwing); j.highTwistLimit = ToSoftJointLimit(p.limits.maxSwing); j.swing1Limit = ToSoftJointLimit(p.limits.swing2); j.swing2Limit = ToSoftJointLimit(p.limits.twist); j.anchor = Vector3.zero; } } private static SoftJointLimit ToSoftJointLimit(float limit) { SoftJointLimit s = new SoftJointLimit(); s.limit = limit; return s; } } }