Shared skeleton and animation state

Suppose I have an animated human mesh and, as one example, a large pair of gloves that I would like to have rendering (around the actual hands) at certain times. The gloves have already been skinned to the same skeletal structure that the human model uses.

Ultimately my goal is to have the gloves mesh animate along with the human model by virtue of the fact that its mesh renderer is using the human model's bones (which are animated by the human object's animation component).

My first stab so far has been essentially the following code:

public class Equipmentizer : MonoBehaviour {
public GameObject target;

void Start () {
    SkinnedMeshRenderer targetRenderer = target.GetComponent<SkinnedMeshRenderer>();
    Dictionary<string,Transform> boneMap = new Dictionary<string,Transform>();
    foreach( Transform bone in targetRenderer.bones )
        boneMap[bone.gameObject.name] = bone;

    SkinnedMeshRenderer myRenderer = gameObject.GetComponent<SkinnedMeshRenderer>();
    for( int i = 0; i < myRenderer.bones.Length; ++i )
    {
        GameObject bone = myRenderer.bones*.gameObject;*
 _if( !boneMap.TryGetValue(bone.name, out myRenderer.bones*) )*_
 _*{*_
 _*Debug.Log("Unable to map bone \"" + bone.name + "\" to target skeleton.");*_
 _*break;*_
 _*}*_
 _*}*_
_*}*_
_*```*_
_*<p>Alas the gloves still animate separately, clearly still using their own bones.  After some additional testing, it seems that any assignments to elements of the mesh renderer's bones array do not actually happen.  No errors though.</p>*_
_*<p>Is there a way to make this work, or perhaps a better approach to solving this problem?  I do want to avoid saving all interchangeable skinned components out into one big asset and then hiding or showing individual meshes - this has been the only alternative suggestion I've encountered so far.</p>*_

I've resolved this issue, so in the event that someone might find it useful in the future, I'll provide an answer here. The code I posted in the question is mostly correct; however, it seems that assigning to individual elements of the SkinnedMeshRenderer's bones array will always silently fail (more accurately, it probably succeeds at modifying the wrong data).

The solution of course was to create a new array of bones and assign it to the whole property at once. The final code looks like this:

public class Equipmentizer : MonoBehaviour {
public GameObject target;

// Use this for initialization
void Start () {
    SkinnedMeshRenderer targetRenderer = target.GetComponent<SkinnedMeshRenderer>();
    Dictionary<string,Transform> boneMap = new Dictionary<string,Transform>();
    foreach( Transform bone in targetRenderer.bones )
        boneMap[bone.gameObject.name] = bone;

    SkinnedMeshRenderer myRenderer = gameObject.GetComponent<SkinnedMeshRenderer>();
    Transform[] newBones = new Transform[myRenderer.bones.Length];
    for( int i = 0; i < myRenderer.bones.Length; ++i )
    {
        GameObject bone = myRenderer.bones*.gameObject;*
 _if( !boneMap.TryGetValue(bone.name, out newBones*) )*_
 _*{*_
 _*Debug.Log("Unable to map bone \"" + bone.name + "\" to target skeleton.");*_
 _*break;*_
 _*}*_
 _*}*_
 _*myRenderer.bones = newBones;*_
_*}}*_
_*```*_
_*<p>And I now have multiple meshes smoothly animating with one shared skeleton!</p>*_
_*<p>Also note that because the attached object doesn't use its own skeleton now, there's no sense in keeping its Animation component around.</p>*_

Great example and thanks for the help!

I made one adjustment - in my situation, the 2nd GameObject needed bones from throughout the hierarchy so I added a method that farms all bones.

using System.Collections.Generic;
using UnityEngine;

public class Equipmentizer : MonoBehaviour
{
    public GameObject target;
    
    void Start ()
    {
        Dictionary<string,Transform> boneMap = new Dictionary<string,Transform>();
        GetAllSkinnedMeshRenderers(ref boneMap);

        SkinnedMeshRenderer myRenderer = GetComponent<SkinnedMeshRenderer>();
        Transform[] newBones = new Transform[myRenderer.bones.Length];

        for(int i = 0; i < myRenderer.bones.Length; ++i )
        {
            GameObject bone = myRenderer.bones*.gameObject;*

if( !boneMap.TryGetValue(bone.name, out newBones*) )*
{
Debug.Log(“Unable to map bone "” + bone.name + “" to target skeleton.”);
}
}

myRenderer.bones = newBones;
}

void GetAllSkinnedMeshRenderers(ref Dictionary<string, Transform> map)
{
SkinnedMeshRenderer[] renderers = target.GetComponentsInChildren();
Dictionary<string, Transform> boneMap = new Dictionary<string, Transform>();

foreach (SkinnedMeshRenderer smr in renderers)
{
foreach (Transform bone in smr.bones)
{
if(!boneMap.ContainsKey(bone.gameObject.name)) boneMap[bone.gameObject.name] = bone;
}

}

map = boneMap;
}
}

works great, thanks again

Indeed, Transform[] xxx = SkinnedMeshRenderer.bones creates a clone of the Transform, so without SkinnedMeshRenderer.bones = yyy nothing will take effect;

void Start () {	
	SkinnedMeshRenderer targetRenderer = m_attached.GetComponent<SkinnedMeshRenderer>();
	Dictionary<string, Transform> boneMap = new Dictionary<string, Transform>();
	foreach( Transform bone in targetRenderer.bones )
	{
		boneMap[bone.name] = bone;
	}
	
	SkinnedMeshRenderer thisRenderer = GetComponent<SkinnedMeshRenderer>();
	Transform[] boneArray = thisRenderer.bones;
	for(int idx = 0; idx < boneArray.Length; ++idx )
	{
		string boneName = boneArray[idx].name;
		if( false == boneMap.TryGetValue(boneName, out boneArray[idx]) )
		{
			Debug.LogError("failed to get bone: " + boneName);
			Debug.Break();
		}
	}
	thisRenderer.bones = boneArray; //take effect
}

Dudes, I can honestly just say thank you, I have a dude, with lots of stuff, armor, weapons, clothes, all seperate fbx’s, but skinned against the same skeleton, and tons of animations working with Mecanim, idle, normal and combat animations, thank you, I was almost ready to friggin give up, couldn’t find a good solution for this for days, your script works perfectly, and I just didn’t know how to do this, I know he needed to be reparented to a different skeleton, but when you do this in the editor, it doesn’t work, because the minute the animator starts doing other animations, those meshes, even though they have been set to different root bones, still do their own thing. Thank you!

So I would really like to do the same thing you are talking about here but I have been messing with your script and my models for hours now and I don’t get it…what should the “target” variable be?
Should it be the item I am instantiating (skinned armor piece in this case)? OR should it be the main character body (and it has the full skeleton, they are both skinned to the same rig in maya exported as fbx)…any info would help at this point…or if u would be so kind as to make a small downloadable unity project that we can open and see how it is arranged Im sure many many people would find this useful…probably others have but I am somewhat new to coding so I guess that is why this isn’t making sense.

Thank you very much for the code though …cause Im sure it works, just me not getting it

Thank you too much for the example. Very clever.