Search Unity

UMA - Unity Multipurpose Avatar on the Asset Store!

Discussion in 'Assets and Asset Store' started by FernandoRibeiro, Dec 24, 2013.

  1. Jaimi

    Jaimi

    Joined:
    Jan 10, 2009
    Posts:
    6,208
    I posted this on the slack, and reposting here. I'll add a better version to UMA later.
    Basically, if you want your UMA to clean up any possible "junk bones" that get left over, without deleting the entire skeleton, add this component to the DCA.
    Then in the "killbones" array on the component, add the name of any bone that you want deleted and removed. You don't have to add all of the bones, just the topmost bone - all bones below it will be deleted and removed from the bone dictionaries.
    For example, if you used the "boney tail", then add "PonyTail1" to the Killbones list, and it will remove that (and it's children) during the character build. Of course, if you re-add the ponytail, it will come back like it's supposed to.
     

    Attached Files:

    Alic, Graham-B and hopeful like this.
  2. Ne0mega

    Ne0mega

    Joined:
    Feb 18, 2018
    Posts:
    755
    what is the texture set up or overlay set up for UMA's cutout materials?
    Ive tried just about every combo of options in the alpha section of texture import, and the best I get is dark UMAs, but have yet to see any transparency or cut out.
    I also selected cutout in the UMA overlay drop down
     
    Last edited: May 25, 2019
  3. pegassy

    pegassy

    Joined:
    Sep 28, 2017
    Posts:
    49
    Thank you for the link. I did use that wiki page in the first place to create the “UMA/Load And Save/Save DynamicCharacterAvatar(s) Text (optimized)” text file, and then I used the Load/Save part in the UMA component to load the character. There is no problem there. I can get a character to start with the saved properties when I play in Unity.

    As I mentioned in the previous post, I am trying to call that text file in runtime using a Unity event. Below is where I explained what I did in my previous post. (you may check the pictures I posted in the previous post):

    So I want to call such a txt file using an event, say when I am picking up a new costume. In order to do that I did call DynamicCharacterAvatar.LoadFromAssetFile(string). However, if I am entering only "RecipeName" this time it gives me a warning:

    Asset 'RecipeName' Not found in Global Index
    UnityEngine.Debug:LogWarning(Object)
    UMA.CharacterSystem.DynamicCharacterAvatar:LoadFromAssetFile(String) (at Assets/_MISC/UMA/Core/Extensions/DynamicCharacterSystem/Scripts/DynamicCharacterAvatar.cs:2392)
    UnityEngine.Events.UnityEvent`1:Invoke(GameObject)
    PixelCrushers.TriggerEvent:OnTriggerEnter(Collider) (at Assets/_TOOLS/Plugins/Pixel Crushers/Common/Scripts/UnityEvents/TriggerEvent.cs:36)

    I tried to use the long file path name, and Fenvir.txt but these did not work and I kept getting the warning. What is the right way to write that string in there?
     
  4. jessejarvis

    jessejarvis

    Joined:
    Aug 9, 2013
    Posts:
    303
    So I am having an issue with UMA and 2019.1.4f1.

    It's saying I should add Unity.Timeline as a reference but it already is.

    https://onedrive.live.com/?authkey=!ADqQbC-0y7GV27o&cid=882EF7B44808FEEB&id=882EF7B44808FEEB!8004&parId=882EF7B44808FEEB!3684&o=OneUp

    Edit: Okay so if that Unity.Timeline didn't fix it for anybody else, make sure you are using .NET 4.5 because apparently 3.5 does not work with UMA 2.8 and Unity 2019.1.X, it seems like it's looking for the wrong Unity.Timeline or something.
     
    Last edited: May 25, 2019
    hopeful likes this.
  5. pegassy

    pegassy

    Joined:
    Sep 28, 2017
    Posts:
    49
    Nvm, I got it to work using an event trigger and DynamicCharacterAvatar.LoadFromText file. I am so happy! :)

    Thank you for all the help.
     
  6. pegassy

    pegassy

    Joined:
    Sep 28, 2017
    Posts:
    49
    Ok, I am trying to change between three different text recipes in runtime by using DynamicCharacterAvatar.LoadFromTextFile. Let's say my text files are called Cloth1, Cloth2, and Cloth3. I can successfully change these clothes in runtime, however, instead of switching entirely to a recipe, it tries to wear them on top of each other, even items in the same slot.

    So I tried to avoid that by making sure to call ClearSlots() first before each recipe call. However, now I can call each recipe only once, and then it will not work the second time. For example, if I called Cloth1, Cloth2, and Cloth3, all these recipes will be worn by the character the first time, but if i call them again during the same play time, then it will not have any effects, and will not give any console errors. Please advise.

    Thank you.
     
  7. Jaimi

    Jaimi

    Joined:
    Jan 10, 2009
    Posts:
    6,208
    Assign the UMA_Cutout_Diffuse_Normal_Metallic umamaterial to the overlay and slot.
    Then, alpha cutout is set to 0.5 in the template material, so anything that is more than 50% transparent in the ALBEDO texture will be cutout, and anything that is more than 50% opaque will be fully opaque.
     
  8. Jaimi

    Jaimi

    Joined:
    Jan 10, 2009
    Posts:
    6,208
    Are you doing this inside an UMA event? Because that won't work and cause all kinds of problems.
     
  9. pegassy

    pegassy

    Joined:
    Sep 28, 2017
    Posts:
    49
    I am not using the "Character Events" under Dynamic Character Avatar component, but using an external event trigger to call these functions when it is activated:
    upload_2019-5-25_14-11-43.png

    I appreciate any advise on this.

    It is working pretty good except you cannot choose the same cloth twice:

    https://twitter.com/chasetailgames/status/1132389683197911040
     

    Attached Files:

  10. Jaimi

    Jaimi

    Joined:
    Jan 10, 2009
    Posts:
    6,208

    I'm not aware of any reason that loading a character and then reloading it will cause an error like that. I'll try to duplicate it in a test app, see if I get the same results.

    I'm assuming you are using UMA 2.8.1. If not, I recommend to update.

    Also, you are loading characters as costumes. Have you looked at WardrobeCollections? That's what they are designed for - they're a group of wardrobe recipes that get added at one time.
     
  11. pegassy

    pegassy

    Joined:
    Sep 28, 2017
    Posts:
    49
    Yes, I am using UMA 2.8.1. I will look into WardrobeCollections in the documentation and tutorials. I was really dreading to get into UMA, and I am really happy I could get this far. Thank you so much for your help so far.
     
  12. Jaimi

    Jaimi

    Joined:
    Jan 10, 2009
    Posts:
    6,208
    They're not well documented, but they are actually pretty simple to use - right click, create/ UMA/DCS/WardrobeCollection. Then edit it, add the race(s), set the slots for the race, and then you can add it to your character like a wardrobe item.

    You can, for example, add both Male and Female races to the same wardrobe collection, and it will equip the right wardrobe items.
     
  13. pegassy

    pegassy

    Joined:
    Sep 28, 2017
    Posts:
    49
    Yes, it worked really good. I understand that the .txt recipes are for creating characters with multiple properties (DNA, race, etc) rather than just wardrobe. So now I am creating my initial character with a loaded text recipe to determine height, eyes, etc., and then initiate a wardrobe, then when equipping new items use a wardrobe collection.

    I realized that the reason I could not reload a wardrobe collection that I had uploaded before was not because of UMA, but because of the event system that I was using. I switched to a different event system (Dialogue System trigger event) and that successfully initiated the wardrobe collection change every time.

    A quick question: I noticed your ghost asset on asset store. I am really interested in using it in my project for certain NPCs. Does that work with UMA?
     
  14. Jaimi

    Jaimi

    Joined:
    Jan 10, 2009
    Posts:
    6,208
    Yes - that will work with UMA, but you will need to switch the material at runtime, or create new overlays (switching the material is probably easier). I should probably make a UMA specific version of the shaders. I'll do that over my vacation.
     
  15. Tarball

    Tarball

    Joined:
    Oct 16, 2016
    Posts:
    166
    It certainly has been causing havoc for the past week. Is the only purpose of the slashes to escape the double quotes? Because, I just realized that when you get of rid all of the slashes via:

    Code (CSharp):
    1. Debug.Log(avatar.GetCurrentRecipe().Replace(@"\", ""));
    you no longer have valid JSON... If the only purpose of the slashes is to escape the double quotes, then something is seriously wrong with the current version of UMA. I hope I'm wrong and some of the slashes serve some other purpose. For instance, put this in your validator:

    Code (CSharp):
    1. {"packedRecipeType":"DynamicCharacterAvatar","name":"Character","race":"HumanFemale","dna":[{"dnaType":"DynamicUMADna","dnaTypeHash":1932961400,"packedDna":"{"bDnaAsset":{"instanceID":121298},"bDnaAssetName":"HumanFemaleDNA 1","bDnaSettings":[{"name":"height","value":53},{"name":"headSize","value":128},{"name":"headWidth","value":128},{"name":"neckThickness","value":128},{"name":"armLength","value":128},{"name":"forearmLength","value":128},{"name":"armWidth","value":128},{"name":"forearmWidth","value":128},{"name":"handsSize","value":128},{"name":"feetSize","value":128},{"name":"legSeparation","value":128},{"name":"upperMuscle","value":61},{"name":"lowerMuscle","value":128},{"name":"upperWeight","value":128},{"name":"lowerWeight","value":128},{"name":"legsSize","value":128},{"name":"belly","value":128},{"name":"waist","value":73},{"name":"gluteusSize","value":128},{"name":"earsSize","value":128},{"name":"earsPosition","value":128},{"name":"earsRotation","value":128},{"name":"noseSize","value":128},{"name":"noseCurve","value":128},{"name":"noseWidth","value":128},{"name":"noseInclination","value":128},{"name":"nosePosition","value":128},{"name":"nosePronounced","value":128},{"name":"noseFlatten","value":128},{"name":"chinSize","value":128},{"name":"chinPronounced","value":128},{"name":"chinPosition","value":128},{"name":"mandibleSize","value":128},{"name":"jawsSize","value":128},{"name":"jawsPosition","value":128},{"name":"cheekSize","value":128},{"name":"cheekPosition","value":128},{"name":"lowCheekPronounced","value":128},{"name":"lowCheekPosition","value":128},{"name":"foreheadSize","value":128},{"name":"foreheadPosition","value":128},{"name":"lipsSize","value":128},{"name":"mouthSize","value":128},{"name":"eyeRotation","value":128},{"name":"eyeSize","value":128},{"name":"breastSize","value":229},{"name":"breastCleavage","value":128},{"name":"eyeSpacing","value":128},{"name":"green","value":128},{"name":"red","value":128},{"name":"blue","value":128}]}"}],"characterColors":[{"name":"Skin","colors":[247,221,192,255,0,0,0,0,255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0]},{"name":"Hair","colors":[165,107,70,255,0,0,0,0,255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0]},{"name":"Eyes","colors":[120,163,222,255,0,0,0,0,255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0]},{"name":"Undies","colors":[255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0,255,255,255,255,0,0,0,0]}],"wardrobeSet":[{"slot":"Face","recipe":"FFace1"},{"slot":"Eyes","recipe":"MaleEye5"},{"slot":"Underwear","recipe":"FemaleUndies2"},{"slot":"Hair","recipe":"FemaleHair1"},{"slot":"Eyebrows","recipe":"FEyebrow3"},{"slot":"Eyelashes","recipe":"Fem_Eyelash"}],"raceAnimatorController":"UltimateCharacterControllerDemo"}
     
  16. Jaimi

    Jaimi

    Joined:
    Jan 10, 2009
    Posts:
    6,208
    Your sql was parameterized, so you do not need to modify the SQL or escape the quotes again.

     
  17. Tarball

    Tarball

    Joined:
    Oct 16, 2016
    Posts:
    166
    It has been parameterized all along. If my column were formatted to take a string, it wouldn't matter, but I format as json, because I want the full functionality of what I'm working with. This was one of the main reasons I chose postgreSQL, as a database. The json is only valid on those online checkers when leaving your slashes in there. For practical use, the slashes serve no purpose is what I'm getting at. The json is not valid.

    I would like to continue to use both UMA and postgreSQL, but it seems I'll have to hack the UMA recipes to do so. It would be better to just fix the reading and writing of the UMA recipe to valid json is what I'm suggesting. I'm willing to help however I can.
     
  18. Jaimi

    Jaimi

    Joined:
    Jan 10, 2009
    Posts:
    6,208
    I understand. I'm not an expert on JSON, but this was directly produced by Unity's JSON formatter - nothing was hand coded:

    Code (csharp):
    1.  
    2.                 SaveOptions thisSaveOptions = SaveOptions.saveColors | SaveOptions.saveAnimator | SaveOptions.saveDNA | SaveOptions.saveWardrobe;
    3.                 currentRecipeString = JsonUtility.ToJson(new UMATextRecipe.DCSPackRecipe(this, this.name, "DynamicCharacterAvatar", thisSaveOptions));
    4.  
    There's not a lot for us to change there. You may want to write your own serializer that takes the DCSPackRecipe. Of course, if you find something we've done wrong, we'd be happy to fix it (in a way that doesn't cause others to break - perhaps adding a FromJSON and ToJSON function). You can generate a DCSPackRecipe like this:

    Code (csharp):
    1.  
    2.                 SaveOptions thisSaveOptions = SaveOptions.saveColors | SaveOptions.saveAnimator | SaveOptions.saveDNA | SaveOptions.saveWardrobe;
    3.                 UMATextRecipe.DCSPackRecipe recipe = new UMATextRecipe.DCSPackRecipe(Avatar, Avatar.name, "DynamicCharacterAvatar", thisSaveOptions));
    4.  
     
    Tarball likes this.
  19. Tarball

    Tarball

    Joined:
    Oct 16, 2016
    Posts:
    166
    @Jaimi Thank you very much for the info and timely response. I'll have to dig around in the code a bit first, but what I suspect is occurring is that there is a ToJson in the DCSPackRecipe method already, meaning the recipe gets json'd twice. That would make sense why the inner recipe has the backslashes. It's just a guess at this point though. I suspect that if I'm right, if it were changed, it would break it for others, but maybe it's something to consider for 3.0 someday. If I can find a problem, I'll let you know. Thanks again.
     
  20. Tarball

    Tarball

    Joined:
    Oct 16, 2016
    Posts:
    166
    As I suspected, UMATextRecipe.DCSPackRecipe.dna.packedDna is already in valid json format. So, when you combine with "DynamicCharacterAvatar.SaveOptions" and serialize it yet again, then you have a json nested within a json which leads to the backslashes and odd behavior if you try to do anything but retrieve it to load.

    @Jaimi , My suggestion would be to combine "DynamicCharacterAvatar.SaveOptions" with the raw dna, rather than with the serialized dna. Then, serialize into one json. This would make for a json which would play nice with other storage options. Of course, it's just a suggestion.

    As it turns out, in my game, I'm better off storing the UMATextRecipe.DCSPackRecipe.dna.packedDna and only updating wardrobe slots one by one, rather than dealing with the whole json every time. I would imagine most people would want the same -- I would think it would be rare to want to update DNA frequently after initial character creation. If you want, I'll share the code, when it is finished. It will be a plugin for saving/loading/updating "Dynamic Character Avatar" that you can hopefully just stick in the UMA folder somewhere if you want. I'll try to keep it as generic as possible.
     
  21. Zervos

    Zervos

    Joined:
    Nov 27, 2016
    Posts:
    1
    I want to point out a gotcha that I came across just in case someone else bumps into it. I'm using Unity 2018.3.0f2. Despite the fact that Unity removed clamping in versions 2018.3 and later, I noticed that my UMA character's blendshape settings would not go past 100. After doing some grep'ing in the UMA source, I noticed the following code snippet in the file UMAData.cs:

    Code (CSharp):
    1. #if !UNITY_2018_3_OR_NEWER
    2.             if (weight < 0.0f || weight > 1.0f)
    3.             {
    4.                 if (Debug.isDebugBuild)
    5.                     Debug.LogWarning("SetBlendShape: Weight is out of range, clamping...");
    6.             }
    7.             weight = Mathf.Clamp01(weight);
    8. #endif
    So the code looks like it's doing the right thing, however, at least my version of Unity did not define UNITY_2018_3_OR_NEWER so UMA blendshapes stayed clamped between 0 and 100. That's the gotcha! So I had to go to File | Build Settings... and click the Player Settings... button, in the dialogue, and enter UNITY_2018_3_OR_NEWER in the Scripting Define Symbols input field.

    I don't know if Unity is supposed to do this for you. But I ended up having to do this myself.
     
    hopeful likes this.
  22. kenamis

    kenamis

    Joined:
    Feb 5, 2015
    Posts:
    387
    It should be automatic; https://docs.unity3d.com/Manual/PlatformDependentCompilation.html
    "Starting from Unity 5.3.4, you can compile code selectively based on the earliest version of Unity required to compile or execute a given portion of code. Given the same version format as above (X.Y.Z), Unity exposes one global #define in the format UNITY_X_Y_OR_NEWER, that can be used for this purpose."
     
  23. AmandaAlavi

    AmandaAlavi

    Joined:
    Sep 14, 2018
    Posts:
    4
    How do you change or add the keywords that auto-categorizes things? Like if it ends up under Body or face DNA, or if it uses a color list.

    And is it possible to add subcategories in the wardrobe slots?
     
  24. Graham-B

    Graham-B

    Joined:
    Feb 27, 2013
    Posts:
    331
    Thank you for sharing on here Jaimi!
     
  25. Jaimi

    Jaimi

    Joined:
    Jan 10, 2009
    Posts:
    6,208
    If you're asking about in the sample apps, then they're really just hard coded - look in the samples, and you can see that we just stuck them where we thought they would go based on keyword.

    In a real project though, you would separate out the different DNA into multiple "DynamicDNAConverterControllers". Each controller has a "Display Value" that you can use to categorize the DNA.
    For example, you could split out the face DNA into a separate controller, and make the Display Value = "Face", and then delete the face DNA from the default controller, and set it's value as "Body".
    You can of course put anything you want in there - including separators "Face/Nose", "Face/Eyes", and then split those out into sub categories.
     
  26. Jaimi

    Jaimi

    Joined:
    Jan 10, 2009
    Posts:
    6,208
    Sure - All (working) contributions are welcome!
     
    hopeful likes this.
  27. Tarball

    Tarball

    Joined:
    Oct 16, 2016
    Posts:
    166
    If anyone has had similar trouble as me, I wrote a static class (in its own namespace) which will save/load data for you in generic types easily, assuming you do something with the data for persistence anyway. Just put in your asset folder anywhere and put the save / load methods in whatever monobehaviour you want.

    You guys are welcome to include this in the project, if you want, or build on it however. I think you can get rid of about 2000 lines of code or so, at least, if you follow what I have done. Besides getting rid of all the confusing classes and methods that essentially do the same things, you could get rid of a bunch of pointless lists and make many of the bizarre types simple generics. I'm not saying this script is bug-free, but maybe it can help. It helps me in many ways.

    Code (CSharp):
    1. // Brought to you by Tarball 5/28/2019
    2. // Drop in Asset folder to save your UMA data to generic types
    3. // Call public methods from any script
    4.  
    5. using UMA.CharacterSystem;
    6. using System.Collections.Generic;
    7. using System.Linq;
    8.  
    9. namespace UMA
    10. {
    11.     namespace EasyMode
    12.     {
    13.         public static class EasyUMA
    14.         {
    15.             public static List<UMAPackedRecipeBase.UMAPackedDna> dnaPackedRecipe = new List<UMAPackedRecipeBase.UMAPackedDna>();
    16.             public static UMAPackedRecipeBase.UMAPackedDna packedDna = new UMAPackedRecipeBase.UMAPackedDna();
    17.             public static Dictionary<int, Dictionary<string, string>> DNAInfo = new Dictionary<int, Dictionary<string, string>>();
    18.             public static Dictionary<string, short[]> colorRecipe = new Dictionary<string, short[]>();
    19.             public static Dictionary<string, string> wardrobeRecipe = new Dictionary<string, string>();
    20.             public static UMATextRecipe.DCSPackRecipe totalRecipe = new UMATextRecipe.DCSPackRecipe();
    21.             public static bool saveAsMale;
    22.  
    23.             private static DynamicCharacterAvatar.SaveOptions saveOptions = DynamicCharacterAvatar.SaveOptions.saveDNA
    24.                     | DynamicCharacterAvatar.SaveOptions.saveColors | DynamicCharacterAvatar.SaveOptions.saveWardrobe;
    25.  
    26.             private static Dictionary<string, string> wardrobe_slots = new Dictionary<string, string>();
    27.             private static Dictionary<string, short[]> UMA_colors = new Dictionary<string, short[]>();
    28.             private static List<UMAPackedRecipeBase.UMAPackedDna> dnaList = new List<UMAPackedRecipeBase.UMAPackedDna>();
    29.  
    30.             // Returns the current DCSPackRecipe from a DynamicCharacterAvatar
    31.             private static UMATextRecipe.DCSPackRecipe packedRecipe(DynamicCharacterAvatar currentAvatar)
    32.             {
    33.                 if (currentAvatar != null)
    34.                 {
    35.                     return new UMATextRecipe.DCSPackRecipe(currentAvatar, currentAvatar.name, "DynamicCharacterAvatar", saveOptions);
    36.                 }
    37.                 else return null;
    38.             }
    39.  
    40.             /// <summary>
    41.             /// Returns a Dictionary with raw DNA information for saving in c# generic types
    42.             /// </summary>
    43.             public static Dictionary<int, Dictionary<string, string>> PrintDNADictionary(DynamicCharacterAvatar dynamicCharacterAvatar)
    44.             {
    45.                 if (dynamicCharacterAvatar != null)
    46.                 {
    47.                     dnaList = packedRecipe(dynamicCharacterAvatar).dna;
    48.                     if (dnaList != null && dnaList.Count != 0)
    49.                     {
    50.                         Dictionary<string, string> tempStrings = new Dictionary<string, string>();
    51.                         Dictionary<int, Dictionary<string, string>> tempDict = new Dictionary<int, Dictionary<string, string>>();
    52.                         tempStrings.Add(dnaList.First().dnaType, dnaList.First().packedDna);
    53.                         tempDict.Add(dnaList.First().dnaTypeHash, tempStrings);
    54.                         return tempDict;
    55.                     }
    56.                     else return null;
    57.                 }
    58.                 else return null;
    59.             }
    60.  
    61.             /// <summary>
    62.             /// Returns a Dictionary with wardrobe information in the format: (string wardrobe_slot_name, string wardrobe_item_name), for example ("Chest", "BreastPlate001")
    63.             /// </summary>
    64.             public static Dictionary<string, string> PrintWardrobe(DynamicCharacterAvatar dynamicCharacterAvatar)
    65.             {
    66.                 if (dynamicCharacterAvatar != null)
    67.                 {
    68.                     var recipe = packedRecipe(dynamicCharacterAvatar);
    69.                     foreach (WardrobeSettings wardrobe_item in recipe.wardrobeSet)
    70.                     {
    71.                         wardrobe_slots.Add(wardrobe_item.slot, wardrobe_item.recipe);
    72.                     }
    73.                     return wardrobe_slots;
    74.                 }
    75.                 else return null;
    76.             }
    77.  
    78.             /// <summary>
    79.             /// Returns a Dictionary with shared color information (string color_name, short[] RGBA_numbers)
    80.             /// </summary>
    81.             public static Dictionary<string, short[]> PrintColors(DynamicCharacterAvatar dynamicCharacterAvatar)
    82.             {
    83.                 if (dynamicCharacterAvatar != null)
    84.                 {
    85.                     var recipe = packedRecipe(dynamicCharacterAvatar);
    86.                     foreach (UMAPackedRecipeBase.PackedOverlayColorDataV3 charColors in recipe.characterColors)
    87.                     {
    88.                         UMA_colors.Add(charColors.name, charColors.colors);
    89.                     }
    90.                     return UMA_colors;
    91.                 }
    92.                 else return null;
    93.             }
    94.  
    95.             /// <summary>
    96.             /// Returns the full recipe as a DCSPackRecipe
    97.             /// </summary>
    98.             public static UMATextRecipe.DCSPackRecipe gatherFullRecipe(DynamicCharacterAvatar currentAvatar, bool isMale,
    99.                 Dictionary<int, Dictionary<string, string>> DNAInfo, Dictionary<string, short[]> colorsData, Dictionary<string, string> wardrobeData)
    100.             {
    101.                 UMATextRecipe.DCSPackRecipe recipe = new UMATextRecipe.DCSPackRecipe();
    102.                 UMAPackedRecipeBase.UMAPackedDna item = new UMAPackedRecipeBase.UMAPackedDna();
    103.                 item.dnaTypeHash = DNAInfo.First().Key;
    104.                 item.dnaType = DNAInfo.First().Value.First().Key;
    105.                 item.packedDna = DNAInfo.First().Value.First().Value;
    106.                 List<UMAPackedRecipeBase.UMAPackedDna> senselessList = new List<UMAPackedRecipeBase.UMAPackedDna>();
    107.                 senselessList.Add(item);
    108.                 recipe.dna = senselessList;
    109.  
    110.                 if (isMale)
    111.                     recipe.race = "HumanMale";
    112.                 else recipe.race = "HumanFemale";
    113.  
    114.                 if (colorsData != null && colorsData.Count != 0)
    115.                 {
    116.                     UMAPackedRecipeBase.PackedOverlayColorDataV3[] overlays = new UMAPackedRecipeBase.PackedOverlayColorDataV3[colorsData.Count];
    117.                     recipe.characterColors = new List<UMAPackedRecipeBase.PackedOverlayColorDataV3>();
    118.                     int i = 0;
    119.                     foreach (var uniqueColor in colorsData)
    120.                     {
    121.                         overlays[i] = new UMAPackedRecipeBase.PackedOverlayColorDataV3();
    122.                         overlays[i].name = uniqueColor.Key;
    123.                         overlays[i].colors = uniqueColor.Value;
    124.                         recipe.characterColors.Add(overlays[i]);
    125.                         i++;
    126.                     }
    127.                 }
    128.  
    129.                 if (wardrobeData != null && wardrobeData.Count != 0)
    130.                 {
    131.                     WardrobeSettings[] wardrobeInfo = new WardrobeSettings[wardrobeData.Count];
    132.                     recipe.wardrobeSet = new List<WardrobeSettings>();
    133.                     int i = 0;
    134.                     foreach (var wardrobePiece in wardrobeData)
    135.                     {
    136.                         wardrobeInfo[i] = new WardrobeSettings();
    137.                         wardrobeInfo[i].slot = wardrobePiece.Key;
    138.                         wardrobeInfo[i].recipe = wardrobePiece.Value;
    139.                         recipe.wardrobeSet.Add(wardrobeInfo[i]);
    140.                         i++;
    141.                     }
    142.                 }
    143.                 return recipe;
    144.             }
    145.         }
    146.     }
    147. }
    148.  
    149.  
    Then, put these save and load functions in any other class derived from monobehaviour or whatever, however you want to call them. They just save / load to variables though. If you want persistence, you have to work that out on your own. Also, I didn't save the animator controller data, because in my opinion, that should be handled in classes far removed from UMA. Cheers.

    Code (CSharp):
    1. using System.Collections.Generic;
    2. using UnityEngine;
    3. using UMA;
    4. using UMA.EasyMode;
    5. using UMA.CharacterSystem;
    6.  
    7. public class SaveLoad
    8. {
    9.     public DynamicCharacterAvatar avatar;
    10.  
    11.     public void SaveRecipe()
    12.     {
    13.         EasyUMA.DNAInfo = EasyUMA.PrintDNADictionary(avatar);
    14.         EasyUMA.colorRecipe = EasyUMA.PrintColors(avatar);
    15.         EasyUMA.wardrobeRecipe = EasyUMA.PrintWardrobe(avatar);
    16.         if (avatar.umaData.umaRecipe.raceData.raceName == "HumanMale")
    17.             EasyUMA.saveAsMale = true;
    18.         else EasyUMA.saveAsMale = false;
    19.     }
    20.  
    21.     public void LoadRecipe()
    22.     {
    23.         avatar.ClearSlots();
    24.         // Maybe something like this if you need to save / load more than once, but this is just example
    25.         // EasyUMA.dnaPackedRecipe.Clear();
    26.         EasyUMA.dnaPackedRecipe.Add(EasyUMA.packedDna);
    27.         EasyUMA.totalRecipe = EasyUMA.gatherFullRecipe(avatar, EasyUMA.saveAsMale, EasyUMA.DNAInfo, EasyUMA.colorRecipe, EasyUMA.wardrobeRecipe);
    28.         var dummyRecipe = JsonUtility.ToJson(EasyUMA.totalRecipe);
    29.         avatar.LoadFromRecipeString(dummyRecipe);
    30.     }
    31. }
    Edit: I did have to leave that ToJson at the end, because it would have taken me too long to figure out how to load everything from scratch. At least you can save in generics though. If I would have had more time, I probably would have messed with the UMAPackedRecipeBase and then packed everything into a single json instead of a nested one, but maybe you guys can do that someday.
     
    Last edited: May 29, 2019
  28. m4a44

    m4a44

    Joined:
    Mar 13, 2013
    Posts:
    45
    Actually, the setup didn't work out in the end so I'm stuck trying to get this working again.

    I have a temp weapon weighted to the bone for easy testing, slotted on the avatar. The weapon bone show up in the DCA hierarchy during runtime, but the animation can't find it because UMA adds the "Root" gameobject at the top as the parent of Global (so the animation is looking for "Global/Position/Rig..." instead of "Root/Global/Position/Rig...").

    How would I fix this?
     
    Last edited: May 29, 2019
  29. SecretAnorak

    SecretAnorak

    Joined:
    Mar 19, 2014
    Posts:
    177
    "Jiggly hair madam? Why not?"
     
  30. NightcoreLV

    NightcoreLV

    Joined:
    Feb 26, 2014
    Posts:
    34
    is it possible to convert uma avatar to skinnedmesh + bones like a normal model?
     
  31. SecretAnorak

    SecretAnorak

    Joined:
    Mar 19, 2014
    Posts:
    177
    Once it's built and in-game, it is just a skinned mesh and bones.

    However, if you're wanting to save an avatar as a plain model for use outside of UMA, then you will need to take a look at Powertools: https://assetstore.unity.com/packages/tools/modeling/uma-power-tools-2-14249

    Hope that helps.
     
    NightcoreLV likes this.
  32. DavidBVal

    DavidBVal

    Joined:
    Mar 13, 2017
    Posts:
    206
    I just installed WillB's F_Base package and the model includes default hair but I don't know if it's a wardrobe recipe or how to replace it/remove it; also helmets/hats that usually hide the hair slot don't hide this model's hair. What am I missing?
     
  33. Jaimi

    Jaimi

    Joined:
    Jan 10, 2009
    Posts:
    6,208
    If it's in the base race recipe, you can remove it by following these steps:

    1. Select the new race. In the base race recipe field, double click that to highlight it in the project.
    2. Select the base race recipe in the project, and in the inspector, scroll down until you find the hair.
    3. Press the X on the header with the hair slot to remove it. Sometimes you have to do this twice,I'm not sure why.
    It should be automatically saved.
     
    DavidBVal likes this.
  34. unity_3drapidsolutions

    unity_3drapidsolutions

    Joined:
    Nov 18, 2017
    Posts:
    21
    Hello there!

    I love UMA and it has been tremendously helpful to create a realistic character with very little effort. I have a question I am hoping you may be able to help with:

    For my military characters, I want to add-on to the clothed UMA model to create a holster-like object to hold weapons or extra ammunition, since my existing clothing packs do not include these. When I connect the object to the root, the UMA loads fine but as can be expected the object does not follow the finer animation motions. When placing the model as a child to the bone structure, UMA throws errors due to modifications to the skeleton (I use Bone Builder so that FinalIK references can be predefined before runtime).

    My current idea of a hackish solution will be simply to have an Invoke() routine in start that waits a few seconds after the scene loads and then parents the object to the bone (such as hip bone) with the offset desired (to place it on hip instead of inside character). Is this the best way to accomplish this? Is there a better way?
     
  35. Jaimi

    Jaimi

    Joined:
    Jan 10, 2009
    Posts:
    6,208
    UMA should be fine adding extra bones to the bone builder. It will cause problems if there are duplicate names, so you need to make sure each object has a unique name.
    What sort of errors are you seeing?

    Also, if you want, you can use the Character Created event to parent objects after creation. You still have to deal with unique names, though.

    Another option might be to synch up your stuff to a helper obj(placing the helper obj in the skeleton at the spot you want to mount things, and then synch the transforms in the update.

     
  36. Jaimi

    Jaimi

    Joined:
    Jan 10, 2009
    Posts:
    6,208
    The current system has no issues with Mecanim and layering animations, so I have no clue why it's not working for you in your case. At this point, it's all simply Mecanim and Unity, so perhaps someone who is more familiar with the animation system can help.
    Another option might be just animating it separately and then synching up the animations using events. Painful though.
     
  37. SickaGames1

    SickaGames1

    Joined:
    Jan 15, 2018
    Posts:
    1,270
    Would love it if UMA Power Tools would give out face rigging when exporting an avatar to prefabs for Salsa.
     
    magique likes this.
  38. m4a44

    m4a44

    Joined:
    Mar 13, 2013
    Posts:
    45
    I took a look through SecretAnorak's videos again and looked at him setting up a Generic rig. Using a "Global/Position/Rig/..." hierarchy setup works fine for bones in the humanoid, but custom bones seem to need that extra Root bone up top for the animation setup just like a Generic rig.

    Having a "Root/Global/Position/Rig/..." hierarchy setup in the animation file fixes it. I wasn't expecting such a workaround as needed though since the humanoid bone animations worked as expected.
     
  39. kenamis

    kenamis

    Joined:
    Feb 5, 2015
    Posts:
    387
    Humanoid animations have a common set of bones that are used so they are much more forgiving in the rig set up. I believe Generic rigs and corresponding animations need to match exactly, so you needing to add those bones to the animation file is expected.
    The whole requirement in UMA to need global, position, etc is a pain point that hopefully will be improved. It was legacy code, but probably won't be fixed until a big refactor like UMA 3.
     
    hopeful likes this.
  40. Jaimi

    Jaimi

    Joined:
    Jan 10, 2009
    Posts:
    6,208
  41. DavidBVal

    DavidBVal

    Joined:
    Mar 13, 2017
    Posts:
    206
    What is the best way to make a uma model look at something? IE, if player approaches, move head slightly and make eyes look at him/her. I suspect this is not easy to do, model is already animated so its movements are determined by an animator...
     
  42. Jaimi

    Jaimi

    Joined:
    Jan 10, 2009
    Posts:
    6,208
    You would use IK - in the OnAnimatorIK callback in a script. Look at the SetLookatPosition function.

    https://docs.unity3d.com/Manual/nav-CouplingAnimationAndNavigation.html
     
  43. DavidBVal

    DavidBVal

    Joined:
    Mar 13, 2017
    Posts:
    206
    Thanks, I will look into that. I also noticed there's three properties in the Expression Player component that seem related to this: GazeMode GazeWeight and GazeTarget. Is there any documentation about that? I can't find anything.
     
  44. Jaimi

    Jaimi

    Joined:
    Jan 10, 2009
    Posts:
    6,208
    I'm not sure. There isn't any in the current docs. I need to make a pass at documenting the expression player.
     
  45. arteria

    arteria

    Joined:
    May 31, 2007
    Posts:
    2,189



    Today, Arteria3d released a new UMA costume - 'Knight Enforcer'

    Pack Contains:

    • 7 Slots
    • 7 Overlays
    • 7 Wardrobe Recipes
    SLOTS Included & Overlays & Wardrobe Elements for:

    • Tabbard
    • Helmet
    • Torso Armour
    • Tassets
    • Boots
    • Gauntlets
    • Shoulder Armour
    Pack can be purchased for $20 from the Arteria Website
    https://arteria3d.myshopify.com/products/knight-enforcer
     
    Firlefanz73 and Jaimi like this.
  46. hopeful

    hopeful

    Joined:
    Nov 20, 2013
    Posts:
    5,685
    I believe that's for random eye movements known as saccades. It's a nice feature, and the most readily used one, from the expressions kit.
     
  47. Jaimi

    Jaimi

    Joined:
    Jan 10, 2009
    Posts:
    6,208
    Looks great!

     
  48. DavidBVal

    DavidBVal

    Joined:
    Mar 13, 2017
    Posts:
    206
    Looks splendid, maybe you could also publish the poly count of your models since they look very complex...

     
    arteria likes this.
  49. ecurtz

    ecurtz

    Joined:
    May 13, 2009
    Posts:
    640
    Gaze target for the expression player isn't actually implemented at this point. It's recommended to use the IK target that Jaimi mentioned.
     
    DavidBVal and hopeful like this.
  50. Creiz

    Creiz

    Joined:
    Jun 6, 2017
    Posts:
    130
    I'm going to post this here too, just in case. I'm trying to create a system that features clothes destruction. I have my small script which works well, but when I call the method, the slot just disappears! Poof! It's very nice and all, but a bit boring, eh? I want to polish this up a bit, have a special effect or two.

    So I need to be able to find the mesh itself of a recipe, add it to a MeshFilter component, then use that to replace the slot like seemingly you're doing, put it into a newly created gameobject that replaces the slot itself and then I can play with it.

    For example, whenever let's say, I want to ClearSlot, I want to be able to check which recipe is in that slot, then return the mesh asset.

    Or, is there a way to name the UmaRenderer (1, 2, etc) that are created at runtime or at least tag them? I could work around it by finding them via Tag or something, dynamically add a MeshFilter component to them then call my external method to deal with the effects. I don't mind having multiple UmaRenderers for each clothing slot.