Search Unity

  1. Unity 2020.1 has been released.
    Dismiss Notice
  2. Good news ✨ We have more Unite Now videos available for you to watch on-demand! Come check them out and ask our experts any questions!
    Dismiss Notice

[TUTORIAL] How to make clothes animate along with character

Discussion in 'Animation' started by TwoTen, Jun 8, 2017.

  1. TwoTen

    TwoTen

    Joined:
    May 25, 2016
    Posts:
    1,157
    Hello!
    Many want to have different clothes for their game that you can swap with the character. There are a few approchaes to this. One is to have different body parts with different clothes attached to it and swap the whole parts. This is kindoff a dirt method in my oppinion but works quite well.

    The other way to do it is to rig the clothing in question and make it's bones follow the bones of the character. That's the method I will show how to do.
    Firstly, let me show you some results from my own game.
    Here is my temporary customization window.
    https://i.gyazo.com/81c191197374c0667f93bbc65dd31bd5.mp4
    And here is the ingame result:
    https://i.gyazo.com/13b391865b3b658658b3dacaac690875.mp4


    Alright, so now for how to do it. Firstly what we have done is to use the same rig for the player and for all the clothing that needs rigging. Thing's like glasses can just be a child of the head. Then I use a script that sync the bones up. I am not the creator of the script, and I sadly can't remember where I found it. I think it was somehwere on the Unity Answers. If anyone know where. Please let me know. But all you have to do is replace the clothing's animator component (Which get's added automatically) with the following script.

    Edit: Credits to Pixels for originally posting the script:
    http://answers.unity3d.com/questions/44355/shared-skeleton-and-animation-state.html

    Code (CSharp):
    1. using System.Collections.Generic;
    2. using UnityEngine;
    3.  
    4. public class Equipmentizer : MonoBehaviour
    5. {
    6.     public SkinnedMeshRenderer TargetMeshRenderer;
    7.  
    8.     void Start()
    9.     {
    10.         Dictionary<string, Transform> boneMap = new Dictionary<string, Transform>();
    11.         foreach (Transform bone in TargetMeshRenderer.bones)
    12.             boneMap[bone.gameObject.name] = bone;
    13.  
    14.  
    15.         SkinnedMeshRenderer myRenderer = gameObject.GetComponent<SkinnedMeshRenderer>();
    16.  
    17.         Transform[] newBones = new Transform[myRenderer.bones.Length];
    18.         for (int i = 0; i < myRenderer.bones.Length; ++i)
    19.         {
    20.             GameObject bone = myRenderer.bones[i].gameObject;
    21.             if (!boneMap.TryGetValue(bone.name, out newBones[i]))
    22.             {
    23.                 Debug.Log("Unable to map bone \"" + bone.name + "\" to target skeleton.");
    24.                 break;
    25.             }
    26.         }
    27.         myRenderer.bones = newBones;
    28.  
    29.     }
    30. }
    Then you drag the characters skinned mesh renderer into the "TargetMeshRenderer" field. And it should all be working. Hope this helped
     
    Last edited: Jun 9, 2017
    sparkwd, Junacik99, RendCycle and 7 others like this.
  2. TonyLi

    TonyLi

    Joined:
    Apr 10, 2012
    Posts:
    10,584
    Thanks for sharing!

    Do you get much poke-through of the original model through the clothing mesh? Do you ever turn off any of the base model's mesh to prevent poke-through, such as turning off the bare leg mesh when the character is wearing pants that completely cover the legs?
     
  3. TwoTen

    TwoTen

    Joined:
    May 25, 2016
    Posts:
    1,157
    No, the character is one big mesh. And the poking is not noticable at all from my experiance.
     
  4. TonyLi

    TonyLi

    Joined:
    Apr 10, 2012
    Posts:
    10,584
    Thanks!
     
  5. mattis89

    mattis89

    Joined:
    Jan 10, 2017
    Posts:
    1,099
    How to do that step with "same rigging with character and clothes?
     
    Ziplock9000 likes this.
  6. TwoTen

    TwoTen

    Joined:
    May 25, 2016
    Posts:
    1,157
    Just use the exact same skeleton when you rig. If you notice clipping you need to weight paint to fix it.
     
  7. mattis89

    mattis89

    Joined:
    Jan 10, 2017
    Posts:
    1,099
    Okay.. but when I made my character in character creator v2 (iclone) .. the rigging was like automatic.. didnt even notice it... but then I have one more character with exact same rig but with different shoes, can I use them? I dont really understand this...
     
  8. TwoTen

    TwoTen

    Joined:
    May 25, 2016
    Posts:
    1,157
    That is really for your software you create the rigs in. I can't help you with that. All I am showing in this article is how to exchange bones. Basically make your clothes bones use the character bones so they match up.
     
    mattis89 likes this.
  9. ksam2

    ksam2

    Joined:
    Apr 28, 2012
    Posts:
    1,015
    Thank you. Just how to disable it when we need to take cloth off? It works good but I need to revert cloth back to its position again.
     
    Last edited: Jan 29, 2018
  10. TwoTen

    TwoTen

    Joined:
    May 25, 2016
    Posts:
    1,157
    Remap the bones back to the original gameobjects?
     
    ksam2 likes this.
  11. ksam2

    ksam2

    Joined:
    Apr 28, 2012
    Posts:
    1,015
    Do you know what's the difference between the script on first post and below script?

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public class ClothEquipmentizer : MonoBehaviour { public GameObject target;
    6.  
    7.     // Use this for initialization
    8.     void Start () {
    9.         SkinnedMeshRenderer targetRenderer = target.GetComponent<SkinnedMeshRenderer>();
    10.         Dictionary<string, Transform> boneMap = new Dictionary<string, Transform>();
    11.         foreach( Transform bone in targetRenderer.bones )
    12.         {
    13.             boneMap[bone.name] = bone;
    14.         }
    15.  
    16.         SkinnedMeshRenderer thisRenderer = GetComponent<SkinnedMeshRenderer>();
    17.         Transform[] boneArray = thisRenderer.bones;
    18.         for(int idx = 0; idx < boneArray.Length; ++idx )
    19.         {
    20.             string boneName = boneArray[idx].name;
    21.             if( false == boneMap.TryGetValue(boneName, out boneArray[idx]) )
    22.             {
    23.                 Debug.LogError("failed to get bone: " + boneName);
    24.                 Debug.Break();
    25.             }
    26.         }
    27.         thisRenderer.bones = boneArray; //take effect
    28.      
    29.     }
    30.  
    31.     // Update is called once per frame
    32.     void Update () {
    33.      
    34.     }
    35. }
    36.  
    Which is better to use?
     
  12. joshcamas

    joshcamas

    Joined:
    Jun 16, 2017
    Posts:
    1,044
    If I understand correctly, this leads to several skinned meshes per character. This of course slows the game down. I know there are ways to merge them, however I have yet to succeed. Have you tried this, and if so, any success?
     
  13. TwoTen

    TwoTen

    Joined:
    May 25, 2016
    Posts:
    1,157
    Not too informed on meshes. But I think a skinned mesh just contains a mesh, you should be able to simply generate the mesh. Not too sure.
     
  14. JemoYT

    JemoYT

    Joined:
    Feb 13, 2017
    Posts:
    7
    Can you give me an example on how to implement this? Where do I attach it? Do I need exactly every bone or just the bones near the mesh and stuff? Can you give me a picture of the hiearchy?
     
  15. TwoTen

    TwoTen

    Joined:
    May 25, 2016
    Posts:
    1,157
    You could probably get away with skipping bones. If you manually map x bones to y bones. But that might cause issues with weighting and might not map very well. All we are doing is to replace the clothing bones with the bones of the rig. As you can see in the example, very simple to do.

    As for example how to implement? I gave one in the original post that is written by Pixels
     
  16. JemoYT

    JemoYT

    Joined:
    Feb 13, 2017
    Posts:
    7
    maybe can you give me a screenshot in the inspector and in the hiearchy?

    I haven't tried it yet, Not even copy pasted the code.
    Before I do, Do you put the script on each bone you want to emulate? or Just put it somewhere and assign the bones to emulate.
     
    Last edited: Jun 29, 2018
  17. TwoTen

    TwoTen

    Joined:
    May 25, 2016
    Posts:
    1,157
    I can't give you a hierarchy screenshot right now. It's just the bones anyways. No biggie.

    And check line 15
     
  18. Liamkirt

    Liamkirt

    Joined:
    Jun 6, 2017
    Posts:
    1
    Not working for me. Do you apply the script to the parent or the Mesh?
     
  19. mattis89

    mattis89

    Joined:
    Jan 10, 2017
    Posts:
    1,099
    the one with the renderer
     
  20. s2celerity

    s2celerity

    Joined:
    Nov 20, 2018
    Posts:
    2
    idk what happened, ive had this working before but now whenever i set this up it seems to warp my mesh inside the character model and take position where the bones are

    https://imgur.com/a/AZ2Kz6h

    heres image

    the highlighted part is what is supposed to be the players pants, but ye it goes inside the body and then turns into the bones
     
  21. s2celerity

    s2celerity

    Joined:
    Nov 20, 2018
    Posts:
    2

    after several hours (more like all day) of figuring things out,
    the problem was that the mesh scale wasnt set properly and after turning off the import scale i adjusted it and now it works great
     
  22. PJRM

    PJRM

    Joined:
    Mar 4, 2013
    Posts:
    290
    @TwoTen
    I like the idea. I was struggling to figure it out how i would do it. however i'll try to know other methods too. May have another way of doing it automatically.
     
  23. genaray

    genaray

    Joined:
    Feb 8, 2017
    Posts:
    159
    @TwoTen

    I could need some help aswell... are you online right now ?
     
  24. TwoTen

    TwoTen

    Joined:
    May 25, 2016
    Posts:
    1,157
    Huh?
     
    GilbertoBitt likes this.
  25. XplorationGames

    XplorationGames

    Joined:
    Oct 31, 2018
    Posts:
    10
    1. Thanks @TwoTen.
    2. I am offering a slightly modified script of yours below, addressing one issue I have in my own workflow.

    Scenario: I import several items from the same rig in bulk, each having the necessary bone structure with them. Unity will automatically rename these duplicate bones as part of the bulk import (hip, hip 1, hip 2, etc). Initially I had problems with the script working, but quickly realized it was because the name of the bones weren't matching the original bones of the character's skinned mesh renderer. I manually renamed the bones to change them back to base names (hip instead of hip 1, hip 2, etc). The script worked like magic.

    So I modified the script so that in the for loop which does the bone mapping, if there is a failure to map, I drop the last 2 digits from the bone name and reattempt the bone map. Below is the code, with comments where I modified (it was rather simple).


    Code (CSharp):
    1. u
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. public class ClothingBoneMapper : MonoBehaviour
    5. {
    6.     public SkinnedMeshRenderer TargetMeshRenderer;
    7.     void Start()
    8.     {
    9.         Dictionary<string, Transform> boneMap = new Dictionary<string, Transform>();
    10.         foreach (Transform bone in TargetMeshRenderer.bones)
    11.             boneMap[bone.gameObject.name] = bone;
    12.         SkinnedMeshRenderer myRenderer = gameObject.GetComponent<SkinnedMeshRenderer>();
    13.         int bonenamelength;         //Added in case bonename needs to be modified in forloop below
    14.         string bonename;            //Added in case bonename needs to be modified in forloop below
    15.         string newname;             //Added in case bonename needs to be modified in forloop below
    16.         Transform[] newBones = new Transform[myRenderer.bones.Length];
    17.         for (int i = 0; i < myRenderer.bones.Length; ++i)
    18.         {
    19.             GameObject bone = myRenderer.bones[i].gameObject;
    20.             bonename = bone.name;                   //Added in case bonename needs to be modified in forloop below
    21.             bonenamelength = bonename.Length;       //Added in case bonename needs to be modified in forloop below
    22.             Debug.Log(bone.name);      
    23.             if (!boneMap.TryGetValue(bone.name, out newBones[i]))
    24.             {
    25.                 newname = bonename.Substring(0, bonenamelength - 2);        //Added to modify the name of the bone to drop the last 2 digits.
    26.                 Debug.Log("Unable to map bone \"" + bone.name + "\" to target skeleton. Checking for name " + newname );    //Modified message to indicate bonename was modified
    27.                 if (!boneMap.TryGetValue(newname, out newBones[i]))                                                         //Added in case bonename needs to be modified in forloop below.  Repeats the mapping with last 2 characters removed from name
    28.                 {
    29.                     Debug.Log("Unable to map bone \"" + bone.name + "\" or \"" + newname + "\" to target skeleton.");       //Added to repeat the bonemap with newname.  Will provide new debug and break if this also fails.
    30.                     break;
    31.                 }
    32.             }
    33.         }
    34.         myRenderer.bones = newBones;
    35.     }
    36. }
     
    Last edited: Oct 3, 2019
    TwoTen likes this.
  26. joshcamas

    joshcamas

    Joined:
    Jun 16, 2017
    Posts:
    1,044
    Here's a skinned mesh combiner that combines multiple skinned meshes. However, it requires the clothes and other meshes to have bones that are already mapped. Also instead of combining skinned mesh renderers, it simply combines meshes, alongside a single hierarchy. This makes this faster, but as I mentioned it also needs the exact same bone array initially, so it's not for everyone.

    https://github.com/joshcamas/skinned-mesh-combiner
     
    XplorationGames and TwoTen like this.
  27. XplorationGames

    XplorationGames

    Joined:
    Oct 31, 2018
    Posts:
    10
    @joshcamas - Thanks for this.

    I plan to grab this and see if I can make it work when there are only partial bone structure matches on the various meshes. That will be the case in my workflow.
     
    joshcamas likes this.
  28. joshcamas

    joshcamas

    Joined:
    Jun 16, 2017
    Posts:
    1,044
    Hopefully it works out! If it does, I could add it to the repo as an alternative path, if you'd like
     
  29. Brokencutlass

    Brokencutlass

    Joined:
    Mar 18, 2019
    Posts:
    84
    Was hopeful this would work but it doesn't seem to. The mask just float in front of my character's face, not really moving with him.
     
  30. TwoTen

    TwoTen

    Joined:
    May 25, 2016
    Posts:
    1,157
    Is your bone structure the same?
     
  31. Brokencutlass

    Brokencutlass

    Joined:
    Mar 18, 2019
    Posts:
    84
    Yea.
     
  32. TwoTen

    TwoTen

    Joined:
    May 25, 2016
    Posts:
    1,157
    Well, then start debugging whats wrong. The code is very simple.

    Just start printing out the bone positions in both meshes and start your debug procedure from there.
     
  33. unitylepi

    unitylepi

    Joined:
    May 21, 2018
    Posts:
    25
    Don't forget to remap the skinnedMeshRenderer's rootBone, or animating will fail:

    Code (CSharp):
    1.    
    2. public static int ReplaceBones(this SkinnedMeshRenderer smr, Dictionary<string, Transform> boneMap)
    3.     {
    4.         int nrUnmappedBones = 0;
    5.         int nrBonesToReplace = smr.bones.Length;
    6.  
    7.         string rootBoneName = smr.rootBone.transform.name;
    8.         Transform rootBoneRef = null;
    9.  
    10.         Transform[] newBones = new Transform[smr.bones.Length];
    11.         for (int i = 0; i < nrBonesToReplace; ++i )
    12.         {
    13.             GameObject bone = smr.bones[i].gameObject;
    14.             if (!boneMap.TryGetValue(bone.name, out newBones[i]))
    15.             {
    16.                 nrUnmappedBones++;
    17.                 Debug.Log("Unable to map bone \"" + bone.name + "\" to target armature.");
    18.    //             break;
    19.             }
    20.  
    21.             if (bone.name == rootBoneName) rootBoneRef = newBones[i];
    22.         }
    23.  
    24.         smr.bones = newBones;
    25.         smr.rootBone = rootBoneRef;
    26.  
    27.         return nrUnmappedBones;
    28.     }
    29.  
     
unityunity