You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
304 lines
12 KiB
C#
304 lines
12 KiB
C#
using UnityEngine;
|
|
using System.Collections;
|
|
using System;
|
|
|
|
namespace RootMotion.Dynamics {
|
|
|
|
// Code for setting up Puppets.
|
|
public partial class PuppetMaster: MonoBehaviour {
|
|
|
|
/// <summary>
|
|
/// Sets up a Puppet from the specified ragdoll and target characters.
|
|
/// </summary>
|
|
public static PuppetMaster SetUp(Transform target, Transform ragdoll, int characterControllerLayer, int ragdollLayer) {
|
|
if (ragdoll != target) {
|
|
PuppetMaster puppetMaster = ragdoll.gameObject.AddComponent<PuppetMaster>();
|
|
puppetMaster.SetUpTo(target, characterControllerLayer, ragdollLayer);
|
|
return puppetMaster;
|
|
} else {
|
|
return SetUp(ragdoll, characterControllerLayer, ragdollLayer);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 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.
|
|
/// </summary>
|
|
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>();
|
|
puppetMaster.SetUpTo(target, characterControllerLayer, ragdollLayer);
|
|
|
|
// Clean up the target from all the ragdoll components
|
|
RemoveRagdollComponents(target, characterControllerLayer);
|
|
|
|
return puppetMaster;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 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.
|
|
/// </summary>
|
|
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<Animator>();
|
|
for (int i = 0; i < animators.Length; i++) {
|
|
DestroyImmediate(animators[i]);
|
|
}
|
|
|
|
Animation[] animations = GetComponentsInChildren<Animation>();
|
|
for (int i = 0; i < animations.Length; i++) {
|
|
DestroyImmediate(animations[i]);
|
|
}
|
|
*/
|
|
|
|
Component[] components = GetComponentsInChildren<Component>();
|
|
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<Animator>();
|
|
for (int i = 0; i < animators.Length; i++) {
|
|
DestroyImmediate(animators[i]);
|
|
}
|
|
|
|
// Remove everything except PuppetMaster and Transform from this component
|
|
components = transform.GetComponents<Component>();
|
|
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<Rigidbody>();
|
|
foreach (Rigidbody r in rigidbodies) {
|
|
if (r.transform != transform && r.GetComponent<ConfigurableJoint>() == null) {
|
|
r.gameObject.AddComponent<ConfigurableJoint>();
|
|
}
|
|
}
|
|
|
|
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<Comments>();
|
|
if (comments == null) comments = behaviourRoot.gameObject.AddComponent<Comments>();
|
|
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<Transform>();
|
|
foreach(Transform child in children) { child.gameObject.layer = ragdollLayer; }
|
|
|
|
Physics.IgnoreLayerCollision(characterControllerLayer, ragdollLayer);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 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.
|
|
/// </summary>
|
|
/// <param name="target">Target.</param>
|
|
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<Rigidbody>();
|
|
Cloth[] cloths = target.GetComponentsInChildren<Cloth>();
|
|
|
|
for (int i = 0; i < rigidbodies.Length; i++) {
|
|
if (rigidbodies[i].gameObject != target.gameObject) {
|
|
var joint = rigidbodies[i].GetComponent<Joint>();
|
|
var collider = rigidbodies[i].GetComponent<Collider>();
|
|
|
|
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<Collider>();
|
|
|
|
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<PuppetMaster>();
|
|
if (puppetMaster != null) DestroyImmediate(puppetMaster);
|
|
}
|
|
|
|
// Builds muscles
|
|
private void SetUpMuscles(Transform setUpTo) {
|
|
// Auto-detect targets
|
|
ConfigurableJoint[] joints = transform.GetComponentsInChildren<ConfigurableJoint>();
|
|
|
|
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<Animator>();
|
|
Transform[] children = setUpTo.GetComponentsInChildren<Transform>();
|
|
|
|
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<Transform>();
|
|
for (int i = 1; i < children.Length; i++) {
|
|
bool keep = false;
|
|
|
|
if (children[i].GetComponent<Rigidbody>() != null || children[i].GetComponent<ConfigurableJoint>() != null) keep = true; // Ragdoll bone
|
|
if (children[i].GetComponent<Collider>() != null && children[i].GetComponent<Rigidbody>() == null) keep = true; // Compound collider
|
|
if (children[i].GetComponent<CharacterController>() != 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;
|
|
}
|
|
|
|
}
|
|
} |