Unity创建布娃娃ragdoll源码以及布娃娃的坑

项目要做死亡自然一点就用布娃娃系统,其实可以用其他插件

选用了自带的布娃娃,出现一个问题每个角色的骨骼结构和命名一样,不想每次来个新模型都要打开工具把每个部位拖到布娃娃创建界面里面,创建布娃娃

顺便谈一谈布娃娃的坑

1.播放布娃娃的时候要把animator勾掉,不然不生效

2.部位穿插拉伸,要把部位Character Joint组件上面的Enable Projection勾上

3.激活布娃娃弹得很高,就是刚体的速度,把rigidbody的速度置为0

4.如果布娃娃动作僵硬,肯定用错骨骼来绑定布娃娃导致的

5.布娃娃穿透场景碰撞的问题,其实这个由于速度太快引起的,可以设置rigidbody的碰撞检测模式collisionDetectionMode

6.受力不要全部部位都给力,一般都是给一个部位就够了,不然各种力作用引起怪异表现

7.关于倒地之后抖动问题,这个只能加个检测,如果所有刚体速度小于某个数则关闭刚体功能

8.关于播放布娃娃之前发现部位有部分穿插到其他碰撞器,这个时候给个检测时间,把碰撞器变成触发器,知道没碰撞则变成碰撞器

默认值是Discrete,可以使用Continuous,建议只是触发布娃娃的时候动态修改模式,不然会对性能有影响

源码代码

using UnityEngine;
using UnityEditor;
using System.Collections;
using System;

class CreateRagdoll : ScriptableWizard
{
    public Transform root;

    public Transform leftHips;
    public Transform leftKnee;
    public Transform leftFoot;

    public Transform rightHips;
    public Transform rightKnee;
    public Transform rightFoot;

    public Transform leftArm;
    public Transform leftElbow;

    public Transform rightArm;
    public Transform rightElbow;

    public Transform middleSpine;
    public Transform head;


    public float totalMass = 20;
    public float strength = 0.0F;

    Vector3 right = Vector3.right;
    Vector3 up = Vector3.up;
    Vector3 forward = Vector3.forward;

    Vector3 worldRight = Vector3.right;
    Vector3 worldUp = Vector3.up;
    Vector3 worldForward = Vector3.forward;
    public bool flipForward = false;

    class BoneInfo
    {
        public string name;

        public Transform anchor;
        public CharacterJoint joint;
        public BoneInfo parent;

        public float minLimit;
        public float maxLimit;
        public float swingLimit;

        public Vector3 axis;
        public Vector3 normalAxis;

        public float radiusScale;
        public Type colliderType;

        public ArrayList children = new ArrayList();
        public float density;
        public float summedMass;// The mass of this and all children bodies
    }

    ArrayList bones;
    BoneInfo rootBone;

    string CheckConsistency()
    {
        PrepareBones();
        Hashtable map = new Hashtable();
        foreach (BoneInfo bone in bones)
        {
            if (bone.anchor)
            {
                if (map[bone.anchor] != null)
                {
                    BoneInfo oldBone = (BoneInfo)map[bone.anchor];
                    return String.Format("{0} and {1} may not be assigned to the same bone.", bone.name, oldBone.name);
                }
                map[bone.anchor] = bone;
            }
        }

        foreach (BoneInfo bone in bones)
        {
            if (bone.anchor == null)
                return String.Format("{0} has not been assigned yet.\n", bone.name);
        }

        return "";
    }

    void OnDrawGizmos()
    {
        if (root)
        {
            Gizmos.color = Color.red; Gizmos.DrawRay(root.position, root.TransformDirection(right));
            Gizmos.color = Color.green; Gizmos.DrawRay(root.position, root.TransformDirection(up));
            Gizmos.color = Color.blue; Gizmos.DrawRay(root.position, root.TransformDirection(forward));
        }
    }

    [MenuItem("GameObject/Create Ragdoll")]
    static void CreateWizard()
    {
        ScriptableWizard.DisplayWizard("Create Ragdoll", typeof(CreateRagdoll));
    }

    void DecomposeVector(out Vector3 normalCompo, out Vector3 tangentCompo, Vector3 outwardDir, Vector3 outwardNormal)
    {
        outwardNormal = outwardNormal.normalized;
        normalCompo = outwardNormal * Vector3.Dot(outwardDir, outwardNormal);
        tangentCompo = outwardDir - normalCompo;
    }

    void CalculateAxes()
    {
        if (head != null && root != null)
            up = CalculateDirectionAxis(root.InverseTransformPoint(head.position));
        if (rightElbow != null && root != null)
        {
            Vector3 removed, temp;
            DecomposeVector(out temp, out removed, root.InverseTransformPoint(rightElbow.position), up);
            right = CalculateDirectionAxis(removed);
        }

        forward = Vector3.Cross(right, up);
        if (flipForward)
            forward = -forward;
    }

    void OnWizardUpdate()
    {
        errorString = CheckConsistency();
        CalculateAxes();

        if (errorString.Length != 0)
        {
            helpString = "Drag all bones from the hierarchy into their slots.\nMake sure your character is in T-Stand.\n";
        }
        else
        {
            helpString = "Make sure your character is in T-Stand.\nMake sure the blue axis faces in the same direction the chracter is looking.\nUse flipForward to flip the direction";
        }

        isValid = errorString.Length == 0;
    }

    void PrepareBones()
    {
        if (root)
        {
            worldRight = root.TransformDirection(right);
            worldUp = root.TransformDirection(up);
            worldForward = root.TransformDirection(forward);
        }

        bones = new ArrayList();

        rootBone = new BoneInfo();
        rootBone.name = "Root";
        rootBone.anchor = root;
        rootBone.parent = null;
        rootBone.density = 2.5F;
        bones.Add(rootBone);

        AddMirroredJoint("Hips", leftHips, rightHips, "Root", worldRight, worldForward, -20, 70, 30, typeof(CapsuleCollider), 0.3F, 1.5F);
        AddMirroredJoint("Knee", leftKnee, rightKnee, "Hips", worldRight, worldForward, -80, 0, 0, typeof(CapsuleCollider), 0.25F, 1.5F);
        // AddMirroredJoint ("Hips", leftHips, rightHips, "Root", worldRight, worldForward, -0, -70, 30, typeof(CapsuleCollider), 0.3F, 1.5F);
        // AddMirroredJoint ("Knee", leftKnee, rightKnee, "Hips", worldRight, worldForward, -0, -50, 0, typeof(CapsuleCollider), .25F, 1.5F);

        AddJoint("Middle Spine", middleSpine, "Root", worldRight, worldForward, -20, 20, 10, null, 1, 2.5F);

        AddMirroredJoint("Arm", leftArm, rightArm, "Middle Spine", worldUp, worldForward, -70, 10, 50, typeof(CapsuleCollider), 0.25F, 1.0F);
        AddMirroredJoint("Elbow", leftElbow, rightElbow, "Arm", worldForward, worldUp, -90, 0, 0, typeof(CapsuleCollider), 0.20F, 1.0F);

        AddJoint("Head", head, "Middle Spine", worldRight, worldForward, -40, 25, 25, null, 1, 1.0F);
    }

    void OnWizardCreate()
    {

        Cleanup();
        BuildCapsules();
        AddBreastColliders();
        AddHeadCollider();

        BuildBodies();
        BuildJoints();
        CalculateMass();
    }

    BoneInfo FindBone(string name)
    {
        foreach (BoneInfo bone in bones)
        {
            if (bone.name == name)
                return bone;
        }
        return null;
    }

    void AddMirroredJoint(string name, Transform leftAnchor, Transform rightAnchor, string parent, Vector3 worldTwistAxis, Vector3 worldSwingAxis, float minLimit, float maxLimit, float swingLimit, Type colliderType, float radiusScale, float density)
    {
        AddJoint("Left " + name, leftAnchor, parent, worldTwistAxis, worldSwingAxis, minLimit, maxLimit, swingLimit, colliderType, radiusScale, density);
        AddJoint("Right " + name, rightAnchor, parent, worldTwistAxis, worldSwingAxis, minLimit, maxLimit, swingLimit, colliderType, radiusScale, density);
    }


    void AddJoint(string name, Transform anchor, string parent, Vector3 worldTwistAxis, Vector3 worldSwingAxis, float minLimit, float maxLimit, float swingLimit, Type colliderType, float radiusScale, float density)
    {
        BoneInfo bone = new BoneInfo();
        bone.name = name;
        bone.anchor = anchor;
        bone.axis = worldTwistAxis;
        bone.normalAxis = worldSwingAxis;
        bone.minLimit = minLimit;
        bone.maxLimit = maxLimit;
        bone.swingLimit = swingLimit;
        bone.density = density;
        bone.colliderType = colliderType;
        bone.radiusScale = radiusScale;

        if (FindBone(parent) != null)
            bone.parent = FindBone(parent);
        else if (name.StartsWith("Left"))
            bone.parent = FindBone("Left " + parent);
        else if (name.StartsWith("Right"))
            bone.parent = FindBone("Right " + parent);


        bone.parent.children.Add(bone);
        bones.Add(bone);
    }

    void BuildCapsules()
    {
        foreach (BoneInfo bone in bones)
        {
            if (bone.colliderType != typeof(CapsuleCollider))
                continue;

            int direction;
            float distance;
            if (bone.children.Count == 1)
            {
                BoneInfo childBone = (BoneInfo)bone.children[0];
                Vector3 endPoint = childBone.anchor.position;
                CalculateDirection(bone.anchor.InverseTransformPoint(endPoint), out direction, out distance);
            }
            else
            {
                Vector3 endPoint = (bone.anchor.position - bone.parent.anchor.position) + bone.anchor.position;
                CalculateDirection(bone.anchor.InverseTransformPoint(endPoint), out direction, out distance);

                if (bone.anchor.GetComponentsInChildren(typeof(Transform)).Length > 1)
                {
                    Bounds bounds = new Bounds();
                    foreach (Transform child in bone.anchor.GetComponentsInChildren(typeof(Transform)))
                    {
                        bounds.Encapsulate(bone.anchor.InverseTransformPoint(child.position));
                    }

                    if (distance > 0)
                        distance = bounds.max[direction];
                    else
                        distance = bounds.min[direction];
                }
            }

            CapsuleCollider collider = bone.anchor.gameObject.AddComponent();
            collider.direction = direction;

            Vector3 center = Vector3.zero;
            center[direction] = distance * 0.5F;
            collider.center = center;
            collider.height = Mathf.Abs(distance);
            collider.radius = Mathf.Abs(distance * bone.radiusScale);
        }
    }

    void Cleanup()
    {
        foreach (BoneInfo bone in bones)
        {
            if (!bone.anchor)
                continue;

            Component[] joints = bone.anchor.GetComponentsInChildren(typeof(Joint));
            foreach (Joint joint in joints)
                DestroyImmediate(joint);

            Component[] bodies = bone.anchor.GetComponentsInChildren(typeof(Rigidbody));
            foreach (Rigidbody body in bodies)
                DestroyImmediate(body);

            Component[] colliders = bone.anchor.GetComponentsInChildren(typeof(Collider));
            foreach (Collider collider in colliders)
                DestroyImmediate(collider);
        }
    }

    void BuildBodies()
    {
        foreach (BoneInfo bone in bones)
        {
            Rigidbody ri = bone.anchor.gameObject.AddComponent();
            ri.mass = bone.density;
        }
    }

    void BuildJoints()
    {
        foreach (BoneInfo bone in bones)
        {
            if (bone.parent == null)
                continue;

            CharacterJoint joint = (CharacterJoint)bone.anchor.gameObject.AddComponent();
            bone.joint = joint;

            // Setup connection and axis
            joint.axis = CalculateDirectionAxis(bone.anchor.InverseTransformDirection(bone.axis));
            joint.swingAxis = CalculateDirectionAxis(bone.anchor.InverseTransformDirection(bone.normalAxis));
            joint.anchor = Vector3.zero;
            joint.connectedBody = bone.parent.anchor.GetComponent();

            // Setup limits
            SoftJointLimit limit = new SoftJointLimit();

            limit.limit = bone.minLimit;
            joint.lowTwistLimit = limit;

            limit.limit = bone.maxLimit;
            joint.highTwistLimit = limit;

            limit.limit = bone.swingLimit;
            joint.swing1Limit = limit;

            limit.limit = 0;
            joint.swing2Limit = limit;
        }
    }

    void CalculateMassRecurse(BoneInfo bone)
    {
        float mass = bone.anchor.GetComponent().mass;
        foreach (BoneInfo child in bone.children)
        {
            CalculateMassRecurse(child);
            mass += child.summedMass;
        }
        bone.summedMass = mass;
    }

    void CalculateMass()
    {
        // Calculate allChildMass by summing all bodies
        CalculateMassRecurse(rootBone);

        // Rescale the mass so that the whole character weights totalMass
        float massScale = totalMass / rootBone.summedMass;
        foreach (BoneInfo bone in bones)
            bone.anchor.GetComponent().mass *= massScale;

        // Recalculate allChildMass by summing all bodies
        CalculateMassRecurse(rootBone);
    }

    ///@todo: This should take into account the inertia tensor.
    JointDrive CalculateSpringDamper(float frequency, float damping, float mass)
    {
        JointDrive drive = new JointDrive();
        drive.positionSpring = 9 * frequency * frequency * mass;
        drive.positionDamper = 4.5F * frequency * damping * mass;
        return drive;
    }

    static void CalculateDirection(Vector3 point, out int direction, out float distance)
    {
        // Calculate longest axis
        direction = 0;
        if (Mathf.Abs(point[1]) > Mathf.Abs(point[0]))
            direction = 1;
        if (Mathf.Abs(point[2]) > Mathf.Abs(point[direction]))
            direction = 2;

        distance = point[direction];
    }

    static Vector3 CalculateDirectionAxis(Vector3 point)
    {
        int direction = 0;
        float distance;
        CalculateDirection(point, out direction, out distance);
        Vector3 axis = Vector3.zero;
        if (distance > 0)
            axis[direction] = 1.0F;
        else
            axis[direction] = -1.0F;
        return axis;
    }

    static int SmallestComponent(Vector3 point)
    {
        int direction = 0;
        if (Mathf.Abs(point[1]) < Mathf.Abs(point[0]))
            direction = 1;
        if (Mathf.Abs(point[2]) < Mathf.Abs(point[direction]))
            direction = 2;
        return direction;
    }

    static int LargestComponent(Vector3 point)
    {
        int direction = 0;
        if (Mathf.Abs(point[1]) > Mathf.Abs(point[0]))
            direction = 1;
        if (Mathf.Abs(point[2]) > Mathf.Abs(point[direction]))
            direction = 2;
        return direction;
    }

    static int SecondLargestComponent(Vector3 point)
    {
        int smallest = SmallestComponent(point);
        int largest = LargestComponent(point);
        if (smallest < largest)
        {
            int temp = largest;
            largest = smallest;
            smallest = temp;
        }

        if (smallest == 0 && largest == 1)
            return 2;
        else if (smallest == 0 && largest == 2)
            return 1;
        else
            return 0;
    }

    Bounds Clip(Bounds bounds, Transform relativeTo, Transform clipTransform, bool below)
    {
        int axis = LargestComponent(bounds.size);

        if (Vector3.Dot(worldUp, relativeTo.TransformPoint(bounds.max)) > Vector3.Dot(worldUp, relativeTo.TransformPoint(bounds.min)) == below)
        {
            Vector3 min = bounds.min;
            min[axis] = relativeTo.InverseTransformPoint(clipTransform.position)[axis];
            bounds.min = min;
        }
        else
        {
            Vector3 max = bounds.max;
            max[axis] = relativeTo.InverseTransformPoint(clipTransform.position)[axis];
            bounds.max = max;
        }
        return bounds;
    }

    Bounds GetBreastBounds(Transform relativeTo)
    {
        // Root bounds
        Bounds bounds = new Bounds();
        bounds.Encapsulate(relativeTo.InverseTransformPoint(leftHips.position));
        bounds.Encapsulate(relativeTo.InverseTransformPoint(rightHips.position));
        bounds.Encapsulate(relativeTo.InverseTransformPoint(leftArm.position));
        bounds.Encapsulate(relativeTo.InverseTransformPoint(rightArm.position));
        Vector3 size = bounds.size;
        size[SmallestComponent(bounds.size)] = size[LargestComponent(bounds.size)] / 2.0F;
        bounds.size = size;
        return bounds;
    }

    void AddBreastColliders()
    {
        // Middle spine and root
        if (middleSpine != null && root != null)
        {
            Bounds bounds;
            BoxCollider box;

            // Middle spine bounds
            bounds = Clip(GetBreastBounds(root), root, middleSpine, false);
            box = (BoxCollider)root.gameObject.AddComponent();
            box.center = bounds.center;
            box.size = bounds.size;

            bounds = Clip(GetBreastBounds(middleSpine), middleSpine, middleSpine, true);
            box = (BoxCollider)middleSpine.gameObject.AddComponent();
            box.center = bounds.center;
            box.size = bounds.size;
        }
        // Only root
        else
        {
            Bounds bounds = new Bounds();
            bounds.Encapsulate(root.InverseTransformPoint(leftHips.position));
            bounds.Encapsulate(root.InverseTransformPoint(rightHips.position));
            bounds.Encapsulate(root.InverseTransformPoint(leftArm.position));
            bounds.Encapsulate(root.InverseTransformPoint(rightArm.position));

            Vector3 size = bounds.size;
            size[SmallestComponent(bounds.size)] = size[LargestComponent(bounds.size)] / 2.0F;

            BoxCollider box = (BoxCollider)root.gameObject.AddComponent();
            box.center = bounds.center;
            box.size = size;
        }
    }

    void AddHeadCollider()
    {
        if (head.GetComponent())
            Destroy(head.GetComponent());

        float radius = Vector3.Distance(leftArm.transform.position, rightArm.transform.position);
        radius /= 4;

        SphereCollider sphere = (SphereCollider)head.gameObject.AddComponent();
        sphere.radius = radius;
        Vector3 center = Vector3.zero;

        int direction;
        float distance;
        CalculateDirection(head.InverseTransformPoint(root.position), out direction, out distance);
        if (distance > 0)
            center[direction] = -radius;
        else
            center[direction] = radius;
        sphere.center = center;
    }


}

一般项目中都要搞这些自动化工具,不过前提是美术命名规范,骨骼的名字统一

我按照一个角色为例,把对应的骨骼名字定义好

然后调用下面代码CreateHumanRagdoll

using UnityEngine;
using UnityEditor;
using System.Collections;
using System;

class MyCreateRagdoll
{

    public static string rootName = "Character1_Hips";

    public static string leftHipsName = "Character1_LeftUpLeg";
    public static string leftKneeName = "Character1_LeftLeg";
    public static string leftFootName = "Character1_LeftFoot";

    public static string rightHipsName = "Character1_RightUpLeg";
    public static string rightKneeName = "Character1_RightLeg";
    public static string rightFootName = "Character1_RightFoot";

    public static string leftArmName = "Character1_LeftArm";
    public static string leftElbowName = "Character1_LeftForeArm";

    public static string rightArmName = "Character1_RightArm";
    public static string rightElbowName = "Character1_RightForeArm";

    public static string middleSpineName = "Character1_Spine2";
    public static string headName = "Character1_Head";


    [MenuItem("GameObject/Create MyRagdoll", false, 1)]
    static void CreateRagdoll()
    {
        GameObject go = Selection.activeGameObject;
        if (null != go)
        {
            CreateHumanRagdoll(go);
        }
    }

    public static void CreateHumanRagdoll(GameObject go)
    {
        MyCreateRagdoll rb = new MyCreateRagdoll();
        Transform[] tt = go.GetComponentsInChildren();
        foreach (Transform t in tt)
        {
            if (t.name == rootName)
            {
                rb.root = t;
            }
            else if (t.name == leftHipsName)
            {
                rb.leftHips = t;
            }
            else if (t.name == leftKneeName)
            {
                rb.leftKnee = t;
            }
            else if (t.name == leftFootName)
            {
                rb.leftFoot = t;
            }
            else if (t.name == rightHipsName)
            {
                rb.rightHips = t;
            }
            else if (t.name == rightKneeName)
            {
                rb.rightKnee = t;
            }
            else if (t.name == rightFootName)
            {
                rb.rightFoot = t;
            }
            else if (t.name == leftArmName)
            {
                rb.leftArm = t;
            }
            else if (t.name == leftElbowName)
            {
                rb.leftElbow = t;
            }
            else if (t.name == rightArmName)
            {
                rb.rightArm = t;
            }
            else if (t.name == rightElbowName)
            {
                rb.rightElbow = t;
            }
            else if (t.name == middleSpineName)
            {
                rb.middleSpine = t;
            }
            else if (t.name == headName)
            {
                rb.head = t;
            }
        }
        rb.PrepareBones();
        rb.OnWizardCreate();
    }



    public Transform root;

    public Transform leftHips;
    public Transform leftKnee;
    public Transform leftFoot;

    public Transform rightHips;
    public Transform rightKnee;
    public Transform rightFoot;

    public Transform leftArm;
    public Transform leftElbow;

    public Transform rightArm;
    public Transform rightElbow;

    public Transform middleSpine;
    public Transform head;


    public float totalMass = 20;
    public float strength = 0.0F;

    Vector3 right = Vector3.right;
    Vector3 up = Vector3.up;
    Vector3 forward = Vector3.forward;

    Vector3 worldRight = Vector3.right;
    Vector3 worldUp = Vector3.up;
    Vector3 worldForward = Vector3.forward;
    public bool flipForward = false;

    class BoneInfo
    {
        public string name;

        public Transform anchor;
        public CharacterJoint joint;
        public BoneInfo parent;

        public float minLimit;
        public float maxLimit;
        public float swingLimit;

        public Vector3 axis;
        public Vector3 normalAxis;

        public float radiusScale;
        public Type colliderType;

        public ArrayList children = new ArrayList();
        public float density;
        public float summedMass;// The mass of this and all children bodies
    }

    ArrayList bones;
    BoneInfo rootBone;

    string CheckConsistency()
    {
        PrepareBones();
        Hashtable map = new Hashtable();
        foreach (BoneInfo bone in bones)
        {
            if (bone.anchor)
            {
                if (map[bone.anchor] != null)
                {
                    BoneInfo oldBone = (BoneInfo)map[bone.anchor];
                    return String.Format("{0} and {1} may not be assigned to the same bone.", bone.name, oldBone.name);
                }
                map[bone.anchor] = bone;
            }
        }

        foreach (BoneInfo bone in bones)
        {
            if (bone.anchor == null)
                return String.Format("{0} has not been assigned yet.\n", bone.name);
        }

        return "";
    }

    void OnDrawGizmos()
    {
        if (root)
        {
            Gizmos.color = Color.red; Gizmos.DrawRay(root.position, root.TransformDirection(right));
            Gizmos.color = Color.green; Gizmos.DrawRay(root.position, root.TransformDirection(up));
            Gizmos.color = Color.blue; Gizmos.DrawRay(root.position, root.TransformDirection(forward));
        }
    }

    [MenuItem("GameObject/Create Ragdoll")]
    static void CreateWizard()
    {
        ScriptableWizard.DisplayWizard("Create Ragdoll", typeof(CreateRagdoll));
    }

    void DecomposeVector(out Vector3 normalCompo, out Vector3 tangentCompo, Vector3 outwardDir, Vector3 outwardNormal)
    {
        outwardNormal = outwardNormal.normalized;
        normalCompo = outwardNormal * Vector3.Dot(outwardDir, outwardNormal);
        tangentCompo = outwardDir - normalCompo;
    }

    void CalculateAxes()
    {
        if (head != null && root != null)
            up = CalculateDirectionAxis(root.InverseTransformPoint(head.position));
        if (rightElbow != null && root != null)
        {
            Vector3 removed, temp;
            DecomposeVector(out temp, out removed, root.InverseTransformPoint(rightElbow.position), up);
            right = CalculateDirectionAxis(removed);
        }

        forward = Vector3.Cross(right, up);
        if (flipForward)
            forward = -forward;
    }

    void PrepareBones()
    {
        if (root)
        {
            worldRight = root.TransformDirection(right);
            worldUp = root.TransformDirection(up);
            worldForward = root.TransformDirection(forward);
        }

        bones = new ArrayList();

        rootBone = new BoneInfo();
        rootBone.name = "Root";
        rootBone.anchor = root;
        rootBone.parent = null;
        rootBone.density = 2.5F;
        bones.Add(rootBone);

        AddMirroredJoint("Hips", leftHips, rightHips, "Root", worldRight, worldForward, -20, 70, 30, typeof(CapsuleCollider), 0.3F, 1.5F);
        AddMirroredJoint("Knee", leftKnee, rightKnee, "Hips", worldRight, worldForward, -80, 0, 0, typeof(CapsuleCollider), 0.25F, 1.5F);
        // AddMirroredJoint ("Hips", leftHips, rightHips, "Root", worldRight, worldForward, -0, -70, 30, typeof(CapsuleCollider), 0.3F, 1.5F);
        // AddMirroredJoint ("Knee", leftKnee, rightKnee, "Hips", worldRight, worldForward, -0, -50, 0, typeof(CapsuleCollider), .25F, 1.5F);

        AddJoint("Middle Spine", middleSpine, "Root", worldRight, worldForward, -20, 20, 10, null, 1, 2.5F);

        AddMirroredJoint("Arm", leftArm, rightArm, "Middle Spine", worldUp, worldForward, -70, 10, 50, typeof(CapsuleCollider), 0.25F, 1.0F);
        AddMirroredJoint("Elbow", leftElbow, rightElbow, "Arm", worldForward, worldUp, -90, 0, 0, typeof(CapsuleCollider), 0.20F, 1.0F);

        AddJoint("Head", head, "Middle Spine", worldRight, worldForward, -40, 25, 25, null, 1, 1.0F);
    }

    void OnWizardCreate()
    {

        Cleanup();
        BuildCapsules();
        AddBreastColliders();
        AddHeadCollider();

        BuildBodies();
        BuildJoints();
        CalculateMass();
    }

    BoneInfo FindBone(string name)
    {
        foreach (BoneInfo bone in bones)
        {
            if (bone.name == name)
                return bone;
        }
        return null;
    }

    void AddMirroredJoint(string name, Transform leftAnchor, Transform rightAnchor, string parent, Vector3 worldTwistAxis, Vector3 worldSwingAxis, float minLimit, float maxLimit, float swingLimit, Type colliderType, float radiusScale, float density)
    {
        AddJoint("Left " + name, leftAnchor, parent, worldTwistAxis, worldSwingAxis, minLimit, maxLimit, swingLimit, colliderType, radiusScale, density);
        AddJoint("Right " + name, rightAnchor, parent, worldTwistAxis, worldSwingAxis, minLimit, maxLimit, swingLimit, colliderType, radiusScale, density);
    }


    void AddJoint(string name, Transform anchor, string parent, Vector3 worldTwistAxis, Vector3 worldSwingAxis, float minLimit, float maxLimit, float swingLimit, Type colliderType, float radiusScale, float density)
    {
        BoneInfo bone = new BoneInfo();
        bone.name = name;
        bone.anchor = anchor;
        bone.axis = worldTwistAxis;
        bone.normalAxis = worldSwingAxis;
        bone.minLimit = minLimit;
        bone.maxLimit = maxLimit;
        bone.swingLimit = swingLimit;
        bone.density = density;
        bone.colliderType = colliderType;
        bone.radiusScale = radiusScale;

        if (FindBone(parent) != null)
            bone.parent = FindBone(parent);
        else if (name.StartsWith("Left"))
            bone.parent = FindBone("Left " + parent);
        else if (name.StartsWith("Right"))
            bone.parent = FindBone("Right " + parent);


        bone.parent.children.Add(bone);
        bones.Add(bone);
    }

    void BuildCapsules()
    {
        foreach (BoneInfo bone in bones)
        {
            if (bone.colliderType != typeof(CapsuleCollider))
                continue;

            int direction;
            float distance;
            if (bone.children.Count == 1)
            {
                BoneInfo childBone = (BoneInfo)bone.children[0];
                Vector3 endPoint = childBone.anchor.position;
                CalculateDirection(bone.anchor.InverseTransformPoint(endPoint), out direction, out distance);
            }
            else
            {
                Vector3 endPoint = (bone.anchor.position - bone.parent.anchor.position) + bone.anchor.position;
                CalculateDirection(bone.anchor.InverseTransformPoint(endPoint), out direction, out distance);

                if (bone.anchor.GetComponentsInChildren(typeof(Transform)).Length > 1)
                {
                    Bounds bounds = new Bounds();
                    foreach (Transform child in bone.anchor.GetComponentsInChildren(typeof(Transform)))
                    {
                        bounds.Encapsulate(bone.anchor.InverseTransformPoint(child.position));
                    }

                    if (distance > 0)
                        distance = bounds.max[direction];
                    else
                        distance = bounds.min[direction];
                }
            }

            CapsuleCollider collider = bone.anchor.gameObject.AddComponent();
            collider.direction = direction;

            Vector3 center = Vector3.zero;
            center[direction] = distance * 0.5F;
            collider.center = center;
            collider.height = Mathf.Abs(distance);
            collider.radius = Mathf.Abs(distance * bone.radiusScale);
        }
    }

    void Cleanup()
    {
        foreach (BoneInfo bone in bones)
        {
            if (!bone.anchor)
                continue;

            Component[] joints = bone.anchor.GetComponentsInChildren(typeof(Joint));
            foreach (Joint joint in joints)
                GameObject.DestroyImmediate(joint);

            Component[] bodies = bone.anchor.GetComponentsInChildren(typeof(Rigidbody));
            foreach (Rigidbody body in bodies)
                GameObject.DestroyImmediate(body);

            Component[] colliders = bone.anchor.GetComponentsInChildren(typeof(Collider));
            foreach (Collider collider in colliders)
                GameObject.DestroyImmediate(collider);
        }
    }

    void BuildBodies()
    {
        foreach (BoneInfo bone in bones)
        {
            Rigidbody ri = bone.anchor.gameObject.AddComponent();
            ri.mass = bone.density;
        }
    }

    void BuildJoints()
    {
        foreach (BoneInfo bone in bones)
        {
            if (bone.parent == null)
                continue;

            CharacterJoint joint = (CharacterJoint)bone.anchor.gameObject.AddComponent();
            bone.joint = joint;

            // Setup connection and axis
            joint.axis = CalculateDirectionAxis(bone.anchor.InverseTransformDirection(bone.axis));
            joint.swingAxis = CalculateDirectionAxis(bone.anchor.InverseTransformDirection(bone.normalAxis));
            joint.anchor = Vector3.zero;
            joint.connectedBody = bone.parent.anchor.GetComponent();

            // Setup limits
            SoftJointLimit limit = new SoftJointLimit();

            limit.limit = bone.minLimit;
            joint.lowTwistLimit = limit;

            limit.limit = bone.maxLimit;
            joint.highTwistLimit = limit;

            limit.limit = bone.swingLimit;
            joint.swing1Limit = limit;

            limit.limit = 0;
            joint.swing2Limit = limit;
        }
    }

    void CalculateMassRecurse(BoneInfo bone)
    {
        float mass = bone.anchor.GetComponent().mass;
        foreach (BoneInfo child in bone.children)
        {
            CalculateMassRecurse(child);
            mass += child.summedMass;
        }
        bone.summedMass = mass;
    }

    void CalculateMass()
    {
        // Calculate allChildMass by summing all bodies
        CalculateMassRecurse(rootBone);

        // Rescale the mass so that the whole character weights totalMass
        float massScale = totalMass / rootBone.summedMass;
        foreach (BoneInfo bone in bones)
            bone.anchor.GetComponent().mass *= massScale;

        // Recalculate allChildMass by summing all bodies
        CalculateMassRecurse(rootBone);
    }

    ///@todo: This should take into account the inertia tensor.
    JointDrive CalculateSpringDamper(float frequency, float damping, float mass)
    {
        JointDrive drive = new JointDrive();
        drive.positionSpring = 9 * frequency * frequency * mass;
        drive.positionDamper = 4.5F * frequency * damping * mass;
        return drive;
    }

    static void CalculateDirection(Vector3 point, out int direction, out float distance)
    {
        // Calculate longest axis
        direction = 0;
        if (Mathf.Abs(point[1]) > Mathf.Abs(point[0]))
            direction = 1;
        if (Mathf.Abs(point[2]) > Mathf.Abs(point[direction]))
            direction = 2;

        distance = point[direction];
    }

    static Vector3 CalculateDirectionAxis(Vector3 point)
    {
        int direction = 0;
        float distance;
        CalculateDirection(point, out direction, out distance);
        Vector3 axis = Vector3.zero;
        if (distance > 0)
            axis[direction] = 1.0F;
        else
            axis[direction] = -1.0F;
        return axis;
    }

    static int SmallestComponent(Vector3 point)
    {
        int direction = 0;
        if (Mathf.Abs(point[1]) < Mathf.Abs(point[0]))
            direction = 1;
        if (Mathf.Abs(point[2]) < Mathf.Abs(point[direction]))
            direction = 2;
        return direction;
    }

    static int LargestComponent(Vector3 point)
    {
        int direction = 0;
        if (Mathf.Abs(point[1]) > Mathf.Abs(point[0]))
            direction = 1;
        if (Mathf.Abs(point[2]) > Mathf.Abs(point[direction]))
            direction = 2;
        return direction;
    }

    static int SecondLargestComponent(Vector3 point)
    {
        int smallest = SmallestComponent(point);
        int largest = LargestComponent(point);
        if (smallest < largest)
        {
            int temp = largest;
            largest = smallest;
            smallest = temp;
        }

        if (smallest == 0 && largest == 1)
            return 2;
        else if (smallest == 0 && largest == 2)
            return 1;
        else
            return 0;
    }

    Bounds Clip(Bounds bounds, Transform relativeTo, Transform clipTransform, bool below)
    {
        int axis = LargestComponent(bounds.size);

        if (Vector3.Dot(worldUp, relativeTo.TransformPoint(bounds.max)) > Vector3.Dot(worldUp, relativeTo.TransformPoint(bounds.min)) == below)
        {
            Vector3 min = bounds.min;
            min[axis] = relativeTo.InverseTransformPoint(clipTransform.position)[axis];
            bounds.min = min;
        }
        else
        {
            Vector3 max = bounds.max;
            max[axis] = relativeTo.InverseTransformPoint(clipTransform.position)[axis];
            bounds.max = max;
        }
        return bounds;
    }

    Bounds GetBreastBounds(Transform relativeTo)
    {
        // Root bounds
        Bounds bounds = new Bounds();
        bounds.Encapsulate(relativeTo.InverseTransformPoint(leftHips.position));
        bounds.Encapsulate(relativeTo.InverseTransformPoint(rightHips.position));
        bounds.Encapsulate(relativeTo.InverseTransformPoint(leftArm.position));
        bounds.Encapsulate(relativeTo.InverseTransformPoint(rightArm.position));
        Vector3 size = bounds.size;
        size[SmallestComponent(bounds.size)] = size[LargestComponent(bounds.size)] / 2.0F;
        bounds.size = size;
        return bounds;
    }

    void AddBreastColliders()
    {
        // Middle spine and root
        if (middleSpine != null && root != null)
        {
            Bounds bounds;
            BoxCollider box;

            // Middle spine bounds
            bounds = Clip(GetBreastBounds(root), root, middleSpine, false);
            box = (BoxCollider)root.gameObject.AddComponent();
            box.center = bounds.center;
            box.size = bounds.size;

            bounds = Clip(GetBreastBounds(middleSpine), middleSpine, middleSpine, true);
            box = (BoxCollider)middleSpine.gameObject.AddComponent();
            box.center = bounds.center;
            box.size = bounds.size;
        }
        // Only root
        else
        {
            Bounds bounds = new Bounds();
            bounds.Encapsulate(root.InverseTransformPoint(leftHips.position));
            bounds.Encapsulate(root.InverseTransformPoint(rightHips.position));
            bounds.Encapsulate(root.InverseTransformPoint(leftArm.position));
            bounds.Encapsulate(root.InverseTransformPoint(rightArm.position));

            Vector3 size = bounds.size;
            size[SmallestComponent(bounds.size)] = size[LargestComponent(bounds.size)] / 2.0F;

            BoxCollider box = (BoxCollider)root.gameObject.AddComponent();
            box.center = bounds.center;
            box.size = size;
        }
    }

    void AddHeadCollider()
    {
        if (head.GetComponent())
            GameObject.Destroy(head.GetComponent());

        float radius = Vector3.Distance(leftArm.transform.position, rightArm.transform.position);
        radius /= 4;

        SphereCollider sphere = (SphereCollider)head.gameObject.AddComponent();
        sphere.radius = radius;
        Vector3 center = Vector3.zero;

        int direction;
        float distance;
        CalculateDirection(head.InverseTransformPoint(root.position), out direction, out distance);
        if (distance > 0)
            center[direction] = -radius;
        else
            center[direction] = radius;
        sphere.center = center;
    }


}

工程

链接:https://pan.baidu.com/s/1IYU9ayDaUFTMSBjh6lQ1HQ 密码:k7lg

你可能感兴趣的:(编辑器扩展)