using UnityEngine; using System.Collections; using System; namespace RootMotion.Dynamics { // Code for setting up Puppets. public partial class PuppetMaster: MonoBehaviour { /// /// Sets up a Puppet from the specified ragdoll and target characters. /// public static PuppetMaster SetUp(Transform target, Transform ragdoll, int characterControllerLayer, int ragdollLayer) { if (ragdoll != target) { PuppetMaster puppetMaster = ragdoll.gameObject.AddComponent(); puppetMaster.SetUpTo(target, characterControllerLayer, ragdollLayer); return puppetMaster; } else { return SetUp(ragdoll, characterControllerLayer, ragdollLayer); } } /// /// Sets up a Puppet using a single ragdoll character. This will duplicate the ragdoll character, remove the ragdoll components from the original and use it as the animated target. /// public static PuppetMaster SetUp(Transform target, int characterControllerLayer, int ragdollLayer) { Transform ragdoll = ((GameObject)GameObject.Instantiate(target.gameObject, target.position, target.rotation)).transform; PuppetMaster puppetMaster = ragdoll.gameObject.AddComponent(); puppetMaster.SetUpTo(target, characterControllerLayer, ragdollLayer); // Clean up the target from all the ragdoll components RemoveRagdollComponents(target, characterControllerLayer); return puppetMaster; } /// /// Sets the ragdoll up as a Puppet. Assigns the specified layers to the animated target and the ragdoll. If setUpTo is the same Transform as the PuppetMaster's, the character will be duplicated and the duplicate will be used as the animated target. /// public void SetUpTo(Transform setUpTo, int characterControllerLayer, int ragdollLayer) { if (setUpTo == null) { Debug.LogWarning("SetUpTo is null. Can not set the PuppetMaster up to a null Transform.",transform); return; } // Setting up the ragdoll to itself if (setUpTo == transform) { setUpTo = ((GameObject)GameObject.Instantiate(setUpTo.gameObject, setUpTo.position, setUpTo.rotation)).transform; setUpTo.name = name; // Clean up the target from all the ragdoll components RemoveRagdollComponents(setUpTo, characterControllerLayer); } RemoveUnnecessaryBones(); /* Animator[] animators = GetComponentsInChildren(); for (int i = 0; i < animators.Length; i++) { DestroyImmediate(animators[i]); } Animation[] animations = GetComponentsInChildren(); for (int i = 0; i < animations.Length; i++) { DestroyImmediate(animations[i]); } */ Component[] components = GetComponentsInChildren(); for (int i = 0; i < components.Length; i++) { if (components[i] is PuppetMaster || components[i] is Transform || components[i] is Rigidbody || components[i] is BoxCollider || components[i] is CapsuleCollider || components[i] is SphereCollider || components[i] is MeshCollider || components[i] is Joint || components[i] is Animator) { } else DestroyImmediate(components[i]); } // Destroy Animators last, in case some components require it (ThirdPersonCharacter) Animator[] animators = GetComponentsInChildren(); for (int i = 0; i < animators.Length; i++) { DestroyImmediate(animators[i]); } // Remove everything except PuppetMaster and Transform from this component components = transform.GetComponents(); for (int i = 0; i < components.Length; i++) { if (components[i] is PuppetMaster || components[i] is Transform) { } else DestroyImmediate(components[i]); } //JointConverter.ToConfigurable(gameObject); // Add ConfigurableJoints to Rigidbodies that don't have a Joint (Pelvis and other free muscles) Rigidbody[] rigidbodies = transform.GetComponentsInChildren(); foreach (Rigidbody r in rigidbodies) { if (r.transform != transform && r.GetComponent() == null) { r.gameObject.AddComponent(); } } targetRoot = setUpTo; // Auto-detect targets SetUpMuscles(setUpTo); name = "PuppetMaster"; bool newParent = setUpTo.parent == null || setUpTo.parent != transform.parent || setUpTo.parent.name != setUpTo.name + " Root"; Transform root = newParent? new GameObject(setUpTo.name + " Root").transform: setUpTo.parent; root.parent = transform.parent; Transform behaviourRoot = new GameObject("Behaviours").transform; Comments comments = behaviourRoot.gameObject.GetComponent(); if (comments == null) comments = behaviourRoot.gameObject.AddComponent(); comments.text = "All Puppet Behaviours should be parented to this GameObject, the PuppetMaster will automatically find them from here. All Puppet Behaviours have been designed so that they could be simply copied from one character to another without changing any references. It is important because they contain a lot of parameters and would be otherwise tedious to set up and tweak."; root.position = setUpTo.position; root.rotation = setUpTo.rotation; behaviourRoot.position = setUpTo.position; behaviourRoot.rotation = setUpTo.rotation; transform.position = setUpTo.position; transform.rotation = setUpTo.rotation; behaviourRoot.parent = root; transform.parent = root; setUpTo.parent = root; // Layers targetRoot.gameObject.layer = characterControllerLayer; var children = GetComponentsInChildren(); foreach(Transform child in children) { child.gameObject.layer = ragdollLayer; } Physics.IgnoreLayerCollision(characterControllerLayer, ragdollLayer); } /// /// Removes all the ragdoll components (except Cloth colliders), including compound colliders that are not used by Cloth. Components on the 'target' GameObject will not be touched for they are probably not ragdoll components, but required by a character controller. /// /// Target. public static void RemoveRagdollComponents(Transform target, int characterControllerLayer) { if (target == null) return; // Get rid of all the ragdoll components on the target Rigidbody[] rigidbodies = target.GetComponentsInChildren(); Cloth[] cloths = target.GetComponentsInChildren(); for (int i = 0; i < rigidbodies.Length; i++) { if (rigidbodies[i].gameObject != target.gameObject) { var joint = rigidbodies[i].GetComponent(); var collider = rigidbodies[i].GetComponent(); if (joint != null) DestroyImmediate(joint); if (collider != null) { if (!IsClothCollider(collider, cloths)) DestroyImmediate(collider); else collider.gameObject.layer = characterControllerLayer; } DestroyImmediate(rigidbodies[i]); } } // Get rid of (compound) colliders that are not used by Cloth Collider[] colliders = target.GetComponentsInChildren(); for (int i = 0; i < colliders.Length; i++) { if (colliders[i].transform != target && !IsClothCollider(colliders[i], cloths)) { DestroyImmediate(colliders[i]); } } // Get rid of the PuppetMaster var puppetMaster = target.GetComponent(); if (puppetMaster != null) DestroyImmediate(puppetMaster); } // Builds muscles private void SetUpMuscles(Transform setUpTo) { // Auto-detect targets ConfigurableJoint[] joints = transform.GetComponentsInChildren(); if (joints.Length == 0) { Debug.LogWarning("No ConfigurableJoints found, can not build PuppetMaster. Please create ConfigurableJoints to connect the ragdoll bones together.", transform); return; } var animator = targetRoot.GetComponentInChildren(); Transform[] children = setUpTo.GetComponentsInChildren(); muscles = new Muscle[joints.Length]; int hipIndex = -1; for (int i = 0; i < joints.Length; i++) { muscles[i] = new Muscle(); muscles[i].joint = joints[i]; muscles[i].name = joints[i].name; muscles[i].props = new Muscle.Props(1f, 1f, 1f, 1f); if (muscles[i].joint.connectedBody == null && hipIndex == -1) hipIndex = i; foreach (Transform c in children) { if (c.name == joints[i].name) { muscles[i].target = c; if (animator != null) { muscles[i].props.group = FindGroup(animator, muscles[i].target); } break; } } } // The hip muscle is not first in hierarchy if (hipIndex != 0) { var firstMuscle = muscles[0]; var hipMuscle = muscles[hipIndex]; muscles[hipIndex] = firstMuscle; muscles[0] = hipMuscle; } bool allSameGroup = true; foreach (Muscle m in muscles) { if (m.target == null) { Debug.LogWarning("No target Transform found for PuppetMaster muscle " + m.joint.name + ". Please assign manually.", transform); } if (m.props.group != muscles[0].props.group) allSameGroup = false; } if (allSameGroup) { Debug.LogWarning("Muscle groups need to be assigned in the PuppetMaster!", transform); } } // Returns the Muscle.Group of the specified bone Transform (only if using the Humanoid rig) private static Muscle.Group FindGroup(Animator animator, Transform t) { if (!animator.isHuman) return Muscle.Group.Hips; if (t == animator.GetBoneTransform(HumanBodyBones.Chest)) return Muscle.Group.Spine; if (t == animator.GetBoneTransform(HumanBodyBones.Head)) return Muscle.Group.Head; if (t == animator.GetBoneTransform(HumanBodyBones.Hips)) return Muscle.Group.Hips; if (t == animator.GetBoneTransform(HumanBodyBones.LeftFoot)) return Muscle.Group.Foot; if (t == animator.GetBoneTransform(HumanBodyBones.LeftHand)) return Muscle.Group.Hand; if (t == animator.GetBoneTransform(HumanBodyBones.LeftLowerArm)) return Muscle.Group.Arm; if (t == animator.GetBoneTransform(HumanBodyBones.LeftLowerLeg)) return Muscle.Group.Leg; if (t == animator.GetBoneTransform(HumanBodyBones.LeftUpperArm)) return Muscle.Group.Arm; if (t == animator.GetBoneTransform(HumanBodyBones.LeftUpperLeg)) return Muscle.Group.Leg; if (t == animator.GetBoneTransform(HumanBodyBones.RightFoot)) return Muscle.Group.Foot; if (t == animator.GetBoneTransform(HumanBodyBones.RightHand)) return Muscle.Group.Hand; if (t == animator.GetBoneTransform(HumanBodyBones.RightLowerArm)) return Muscle.Group.Arm; if (t == animator.GetBoneTransform(HumanBodyBones.RightLowerLeg)) return Muscle.Group.Leg; if (t == animator.GetBoneTransform(HumanBodyBones.RightUpperArm)) return Muscle.Group.Arm; if (t == animator.GetBoneTransform(HumanBodyBones.RightUpperLeg)) return Muscle.Group.Leg; return Muscle.Group.Spine; } // Removes all bones that don't have a Rigidbody or a Collider attached because they are not part of the simulation private void RemoveUnnecessaryBones() { Transform[] children = GetComponentsInChildren(); for (int i = 1; i < children.Length; i++) { bool keep = false; if (children[i].GetComponent() != null || children[i].GetComponent() != null) keep = true; // Ragdoll bone if (children[i].GetComponent() != null && children[i].GetComponent() == null) keep = true; // Compound collider if (children[i].GetComponent() != null) keep = false; // Character controller if (!keep) { Transform[] save = new Transform[children[i].childCount]; for (int c = 0; c < save.Length; c++) { save[c] = children[i].GetChild(c); } for (int c = 0; c < save.Length; c++) { save[c].parent = children[i].parent; } DestroyImmediate(children[i].gameObject); } } } // Returns true if the collider is used by the Cloth. private static bool IsClothCollider(Collider collider, Cloth[] cloths) { if (cloths == null) return false; foreach (Cloth cloth in cloths) { if (cloth == null) return false; foreach (CapsuleCollider c in cloth.capsuleColliders) { if (c != null && c.gameObject == collider.gameObject) return true; } foreach (ClothSphereColliderPair s in cloth.sphereColliders) { if (s.first != null && s.first.gameObject == collider.gameObject) return true; if (s.second != null && s.second.gameObject == collider.gameObject) return true; } } return false; } } }