Search Unity

Mecanim - Blend Trees copy and paste?

Discussion in 'Editor & General Support' started by oysterCAKE, Apr 28, 2013.

  1. oysterCAKE

    oysterCAKE

    Joined:
    Dec 3, 2012
    Posts:
    149
    Quick query - Is it possible to copy and paste blend trees? or rather to move them around.

    My movement state machine consists of several nested blend trees. The other night, I needed to create a new blend tree at the top of the tree, and couldn't find anyway to do this, other than the time consuming practice of rebuilding the whole state.
     
    Bersaelor likes this.
  2. dogzerx2

    dogzerx2

    Joined:
    Dec 27, 2009
    Posts:
    3,971
    I want to know this too...
     
    Bersaelor and ChrisAmstutz like this.
  3. dogzerx2

    dogzerx2

    Joined:
    Dec 27, 2009
    Posts:
    3,971
    bump... I'm currently copying manually a huge blend tree :-(
     
    Bersaelor and BrainSlugs83 like this.
  4. IridiumStudios

    IridiumStudios

    Joined:
    Apr 15, 2013
    Posts:
    21
    Bump: A cheat-y way to do this is to right-click somewhere, say "Copy State Machine", then paste that, then go into the sub-state, and drag out the Blend Tree into the main layer.

    Should be fixed.
     
    Wikimrar, fut and WendelinReich like this.
  5. pointcache

    pointcache

    Joined:
    Sep 22, 2012
    Posts:
    579
    how do you drag out something?
     
    Last edited: Nov 9, 2019
    marcospgp and Zomby138 like this.
  6. shokri

    shokri

    Joined:
    Feb 14, 2016
    Posts:
    2
    The same here... Is there anybody who knows the answer? It's so fraustrating to do the whole job manauly again. I hope there will be a chance to do it through editor scripting.
     
  7. IgorAherne

    IgorAherne

    Joined:
    May 15, 2013
    Posts:
    393
    what the f!

    guys, we can't reconnect the blend-tree nodes or move them around :/
    Plz fix
    Write you
     
  8. petey

    petey

    Joined:
    May 20, 2009
    Posts:
    1,824
    For the love of all things holy why cant you do this!
    Blend trees could be really good but they are so inflexible. If you want to add something to the start of a blend tree you just have to manually rebuild the whole thing!?! :mad:
    Well I'd better get back to rebuilding a whole massive blend tree (something that could take seconds).
     
    BrainSlugs83 and Zomby138 like this.
  9. IgorAherne

    IgorAherne

    Joined:
    May 15, 2013
    Posts:
    393
    Hey, at least we have the "Combined move-rotate-scale handle" for editing objects in scene view, that thing is the most important addition
     
    BrainSlugs83 likes this.
  10. petey

    petey

    Joined:
    May 20, 2009
    Posts:
    1,824
    Hehe :D, yeah I think pretty much everything gets prioritised before animation sadly, look at how long it took to get zoom in the view.:rolleyes: Blend trees as they stand are a problem though because its a wall that stops you from experimenting and creating better animation.
    You can build blend trees with through scripting, it's probably out of my skill level but I might have a look today and see if I can come up with something usable.
     
    IgorAherne likes this.
  11. petey

    petey

    Joined:
    May 20, 2009
    Posts:
    1,824
    Well I got this happening, basically you put this script on a gameObject and fill out the array -

    Press build and it creates a blend tree in the animator controller you referenced up the top.

    It's still pretty bad but it's a start and it still might be less frustrating than setting it up the standard way.

    If anyone wants to add to it, please do! I have limited experience with this stuff and the Unity folk are too busy to look at this low level fundamental stuff :p


    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using UnityEditor.Animations;
    5.  
    6. public class BlendTreeCreator : MonoBehaviour
    7. {
    8.  
    9.     public AnimatorController targetAnim;
    10.  
    11.     [System.Serializable]
    12.     public class subMotionClass
    13.     {
    14.         public string name;
    15.         public AnimationClip clip;
    16.  
    17.         [Space(12)]
    18.         public List<subMotionClass> blendTree;
    19.         public string parameter;
    20.     }
    21.     public subMotionClass motion;
    22.  
    23.     public void CheckIfStateExists()
    24.     {
    25.         for (int i = 0; i < targetAnim.layers.Length; i++)
    26.         {
    27.             for (int z = 0; z < targetAnim.layers[i].stateMachine.states.Length; z++)
    28.             {
    29.                 if (targetAnim.layers[i].stateMachine.states[z].state.name == motion.name)
    30.                 {
    31.                     //cant seem to delete states
    32.                     //DestroyImmediate(targetAnim.layers[i].stateMachine.states[z].state);
    33.                 }
    34.             }
    35.         }
    36.     }
    37.  
    38.     public void BuildInitialBlendTree()
    39.     {
    40.         CheckIfStateExists();
    41.  
    42.         Debug.Log("Building Blend Tree");
    43.         UnityEditor.Animations.BlendTree blendTree;
    44.         targetAnim.CreateBlendTreeInController(motion.name, out blendTree, 0);
    45.         blendTree.blendParameter = motion.parameter;
    46.  
    47.         PopulateBlendTree(motion, blendTree);
    48.     }
    49.  
    50.     void PopulateBlendTree(subMotionClass subMotion, BlendTree subBlendTreeNode)
    51.     {
    52.         //add Clip to blendtree
    53.         if (subMotion.clip != null)
    54.         {
    55.             subBlendTreeNode.AddChild(subMotion.clip);
    56.         }
    57.         else
    58.         //create a new blend tree
    59.         {
    60.             for (int i = 0; i < subMotion.blendTree.Count; i++)
    61.             {
    62.                 UnityEditor.Animations.BlendTree newBlendTree = subBlendTreeNode.CreateBlendTreeChild(0);
    63.                 newBlendTree.name = subMotion.blendTree[i].name;
    64.                 newBlendTree.blendParameter = subMotion.blendTree[i].parameter;
    65.                 PopulateBlendTree(subMotion.blendTree[i], newBlendTree);
    66.             }
    67.         }
    68.     }
    69. }
    And the editor script for a button (put it in an editor folder)...
    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using UnityEditor;
    5.  
    6. [CustomEditor(typeof(BlendTreeCreator))]
    7. public class BlendTreeCreatorEditor : Editor
    8. {
    9.     public override void OnInspectorGUI()
    10.     {
    11.         BlendTreeCreator script = (BlendTreeCreator)target;
    12.         base.OnInspectorGUI();
    13.  
    14.         if (GUILayout.Button("Build ", GUILayout.MinHeight(20)))
    15.         {
    16.             script.BuildInitialBlendTree();
    17.         }
    18.     }
    19. }
     
    IgorAherne and mandisaw like this.
  12. Harekelas

    Harekelas

    Joined:
    Feb 3, 2015
    Posts:
    864
    Found this piece of code from unity feedback page:
    Code (CSharp):
    1. using UnityEditor;
    2. using UnityEngine;
    3. using UnityEditor.Animations;
    4.  
    5. public class CreateBlendtreeAsset : MonoBehaviour {
    6.  
    7. [MenuItem("AnimTools/GameObject/Asset from Blendtree")]
    8. static void CreateBlendtree()
    9. {
    10. string path = "Assets/";
    11.  
    12. string currentPath = AssetDatabase.GetAssetPath(Selection.activeObject);
    13. if (currentPath != null)
    14. {
    15. path = currentPath;
    16. }
    17.  
    18. BlendTree BT = Selection.activeObject as BlendTree;
    19.  
    20. BlendTree BTcopy = Instantiate<BlendTree>(BT);
    21.  
    22. AssetDatabase.CreateAsset(BTcopy, AssetDatabase.GenerateUniqueAssetPath(path + "_" + BT.name + ".asset"));
    23. }
    24. }
    It's an editor function that can save the selected blendtree as an asset in the animator's folder, you can drag n drop the blendtree asset to other nested blendtree hierarchy to reuse them. But there also seems to be some bugs in unity with this workflow. I haven't met it yet, but no guarantees.
     
    BrainSlugs83 and IgorAherne like this.
  13. petey

    petey

    Joined:
    May 20, 2009
    Posts:
    1,824
    Awesome, thanks for sharing that!
    I’m about to be getting back into that side of things so I’ll check it out for sure.
     
  14. Harekelas

    Harekelas

    Joined:
    Feb 3, 2015
    Posts:
    864
    Just met the bug I've mentioned before, it occurs when you replace a blendtree in your animator, if the blendtree is a local asset, it will be deleted, remember to backup all your local blendtree assets so won't panic when they got deleted by unity.
     
  15. petey

    petey

    Joined:
    May 20, 2009
    Posts:
    1,824
    Okay I’ll keep that in mind, thanks for the help! I really appreciate that :)
     
  16. elyshaff

    elyshaff

    Joined:
    Sep 30, 2016
    Posts:
    1
    This is what solved the problem for me, great idea, great tool, great developer! good job buddy.
     
    Harekelas likes this.
  17. GarveySoftware

    GarveySoftware

    Joined:
    Sep 28, 2018
    Posts:
    6
    I created an editor that works with the animator window that has the ability to duplicate blend trees. It can also be used to re-structure blend trees with drag/drop, and has a template system for saving off blend trees that you want to re-use. There are a ton of other time saving features that really help speed up editing animation controllers. Check it out here: https://assetstore.unity.com/packages/tools/animation/anim-tree-editor-161217
     
    jeffersonrcgouveia likes this.
  18. Vazili_KZ

    Vazili_KZ

    Joined:
    Sep 18, 2016
    Posts:
    25
    @Harekelas Hi man,

    Trying to use this Editor Code gave me this error :
    -----------
    Assertion failed on expression: '(metaFlags & kStrongPPtrMask) == 0'
    UnityEngine.Object:Instantiate(BlendTree)
    CreateBlendtreeAsset:CreateBlendtree() (at Assets/Scripts/Editor/CreateBlendtreeAsset.cs:20)
    -----------

    Any Idea on what might be causing this and how to fix it? Thanks
     
  19. Harekelas

    Harekelas

    Joined:
    Feb 3, 2015
    Posts:
    864
    Which unity version are you using? It may be caused by new versions changed some editor api.
     
  20. Vazili_KZ

    Vazili_KZ

    Joined:
    Sep 18, 2016
    Posts:
    25
    I'm using version 2019.2.13f1
     
  21. Harekelas

    Harekelas

    Joined:
    Feb 3, 2015
    Posts:
    864
    I haven't tried the code in 2019, guess unity changed some api broken the code. You can try going to the error line and see how to fix it.
     
  22. Vazili_KZ

    Vazili_KZ

    Joined:
    Sep 18, 2016
    Posts:
    25
    Ah i see that's what's causing the problem then for sure.
    Thing is i'm not familiar with Unity's API and have never worked with it, so unfortunately i can't try to fix this since i won't know what i'm supposed to do to begin with.
    I'm going to just remake the blend trees manually
    Thanks anyways for your replays, much appreciated :D
     
    Harekelas likes this.
  23. gerudobomb

    gerudobomb

    Joined:
    Mar 17, 2016
    Posts:
    8
    Hello!

    Anyone who finds this on Google trying to copy/paste a blend tree, I have you covered!

    I found the script on this page above and decided to rework it a little more to make it easier to use.

    How to use it
    1. Select the blend tree you'd like to copy.
    2. Click the menu item AnimTools -> Blend Tree -> Copy Tree
    3. Select (or create an empty) Blend Tree in the Animator window
    4. Click the menu item AnimTools -> Blend Tree -> Paste


    Copy this script, and save it into your project:

    Code (CSharp):
    1.  
    2. using UnityEditor;
    3. using UnityEditor.Animations;
    4. using UnityEngine;
    5. using System.Collections.Generic;
    6.  
    7. public class BlendTreeCopyPaste : MonoBehaviour
    8. {
    9.     const string workDir = "Assets/Editor/AnimTools/BlendTreeCopyPaste/";
    10.     const string filename = "btcopy_";
    11.     static int depth = 0;
    12.     static string treePath = "";
    13.     static string log = "";
    14.     static BlendTree useTree = null;
    15.     public class Pair<T1, T2>
    16.     {
    17.         public T1 First;
    18.         public T2 Second;
    19.         public Pair(T1 first, T2 second)
    20.         {
    21.             First = first;
    22.             Second = second;
    23.         }
    24.     }
    25.     static void makeWorkDirIfDoesntExist()
    26.     {
    27.         string path = "Assets";
    28.         var split = workDir.Split('/');
    29.         for (int i = 1; i < split.Length; i++)
    30.         {
    31.             string p = split[i];
    32.             string newpath = path + "/" + p;
    33.             if (!AssetDatabase.IsValidFolder(newpath))
    34.                 AssetDatabase.CreateFolder(path, p);
    35.             path = newpath;
    36.         }
    37.     }
    38.     //===========================================================
    39.     static void ClearConsole()
    40.     {
    41.         var logEntries = System.Type.GetType("UnityEditor.LogEntries, UnityEditor.dll");
    42.         var clearMethod = logEntries.GetMethod("Clear", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public);
    43.         clearMethod.Invoke(null, null);
    44.     }
    45.     //===========================================================
    46.     public static bool isBlendTree(ChildMotion motion)
    47.     {
    48.         try { var treeType = (motion.motion as BlendTree).blendType; }
    49.         catch { return false; }
    50.         return true;
    51.     }
    52.     //===========================================================
    53.     public static BlendTree getBlendTreeFromSelection()
    54.     {
    55.         BlendTree bt = useTree == null ? Selection.activeObject as BlendTree : useTree;
    56.         if (bt == null) bt = (Selection.activeObject as AnimatorState).motion as BlendTree;
    57.         return bt;
    58.     }
    59.     //===========================================================
    60.     public static string getLogPath()
    61.     {
    62.         return Application.dataPath.Substring(0, Application.dataPath.Length - "Assets".Length) + workDir + "log.txt";
    63.     }
    64.     //===========================================================
    65.     [MenuItem("AnimTools/Blend Tree/Copy Tree")]
    66.     static void CopyBlendTree()
    67.     {
    68.         int notCopied = 0;
    69.         // Get selected blendTree ...
    70.         BlendTree bt = useTree == null ? getBlendTreeFromSelection() : useTree;
    71.         if (bt == null)
    72.         {
    73.             Debug.LogError("BlendTreeCopy - Error: No selected blend tree");
    74.             return;
    75.         }
    76.         // Copy directory ...
    77.         makeWorkDirIfDoesntExist();
    78.         log = "";
    79.         depth = -1;
    80.         treePath = "btcopy_";
    81.         CopyTreeRecursive(bt, 0);
    82.         // Save log
    83.         ClearConsole();
    84.         System.IO.File.WriteAllText(getLogPath(), log);
    85.         Debug.Log("BlendTree copied!" + (notCopied > 0 ? " (" + notCopied.ToString() + " child blend trees not copied)" : ""));
    86.     }
    87.     //===========================================================
    88.     public static void CopyTreeRecursive(BlendTree t, int ichild)
    89.     {
    90.         string oldpath = treePath;
    91.  
    92.         treePath += depth.ToString() + "," + ichild.ToString() + "_";
    93.         // Save 't's motions ...
    94.         BlendTree tclone = Instantiate<BlendTree>(t);
    95.         string fpath = workDir + filename + depth.ToString() + "," + ichild.ToString() + ".asset";
    96.         AssetDatabase.CreateAsset(tclone, workDir + filename + depth.ToString() + "," + ichild.ToString() + ".asset");
    97.         log += fpath + "\n";
    98.         // Save children (recursive) ...
    99.         depth++;
    100.         for (int i = 0; i < t.children.Length; i++)
    101.             if (isBlendTree(t.children[i]))
    102.                 CopyTreeRecursive(t.children[i].motion as BlendTree, i);
    103.         depth--;
    104.         treePath = oldpath;
    105.     }
    106.     //===========================================================
    107.     [MenuItem("AnimTools/Blend Tree/Paste")]
    108.     static void PasteBlendTree()
    109.     {
    110.         try
    111.         {
    112.             BlendTree bt = useTree == null ? getBlendTreeFromSelection() : useTree;
    113.             var lines = System.IO.File.ReadAllLines(getLogPath());
    114.             List<BlendTree> trees = new List<BlendTree>();
    115.             for (int i = 0; i < lines.Length; i++)
    116.                 trees.Add(AssetDatabase.LoadAssetAtPath<BlendTree>(lines[i]));
    117.             for (int i = 1; i < lines.Length; i++)
    118.             {
    119.                 string l = lines[i].Substring((workDir + filename).Length);
    120.                 l = l.Substring(0, l.Length - ".asset".Length);
    121.                 if (l.Length == 0) continue;
    122.                 Debug.Log(l);
    123.                 var split = l.Split(',');
    124.                 int a = int.Parse(split[0]);
    125.                 int b = int.Parse(split[1]);
    126.                 trees[a].children[b].motion = trees[i];
    127.             }
    128.             pasteBlendTreeSettings(bt, trees[0]);
    129.             ClearConsole();
    130.             Debug.Log("BlendTree pasted!");
    131.         }
    132.         catch
    133.         {
    134.             Debug.LogError("BlendTree - Error pasting!");
    135.         }
    136.     }
    137.     public static void pasteBlendTreeSettings(BlendTree bt, BlendTree paste)
    138.     {
    139.         bt.blendType = paste.blendType;
    140.         bt.minThreshold = paste.minThreshold;
    141.         bt.maxThreshold = paste.maxThreshold;
    142.         bt.useAutomaticThresholds = paste.useAutomaticThresholds;
    143.         bt.hideFlags = paste.hideFlags;
    144.         bt.children = paste.children.Clone() as ChildMotion[];
    145.         bt.blendParameter = paste.blendParameter;
    146.         bt.blendParameterY = paste.blendParameterY;
    147.     }
    148. }
    149.  
    After Unity sees it, a new menu item will appear at the top of Unity (AnimTools).

    The script saves temporary files, so you can safely delete the old tree after it is copied, and also paste multiple times if you like.

    Hopefully, that makes your life easier!

    EDIT: Updated the script to not include the obsolete 'Boo.Lang' list, and instead use 'System.Collections.Generic'
     
    Last edited: Nov 13, 2020
    KDstar, Kokowolo, SammmZ and 18 others like this.
  24. demiurge180

    demiurge180

    Joined:
    Apr 29, 2020
    Posts:
    21
    Dude, thank you! Amazing that we've had the Animator for like 7 years now and being able to nest blend trees still isn't a thing.
     
  25. GrosCorps

    GrosCorps

    Joined:
    Jul 27, 2018
    Posts:
    4


    This is perfect, thank you !!!!!!!!!!
     
  26. jawaka

    jawaka

    Joined:
    Nov 15, 2015
    Posts:
    2
    Hi, there. Really would like to be able to use this and very kind of you to have created it - however, I'm getting the error "The type or namespace name 'Boo' could not be found (are you missing a using directive or an assembly reference?)" when unity tries to compile it. Any solutions?

    Have looked around for a way to include Boo.Lang somewhere and have been unsuccessful.
     
  27. jawaka

    jawaka

    Joined:
    Nov 15, 2015
    Posts:
    2
    Nevermind! Sorted it. I had an old version of Unity laying around and just copy/pasted the .dll into Assets/Plugins. Huge help!
     
  28. Noogy

    Noogy

    Joined:
    May 18, 2014
    Posts:
    132
    Thanks for posting that, @gerudobomb. I'm surprised this isn't a standard function within Unity, so the script is deeply appreciated.
     
  29. linh60bpm

    linh60bpm

    Joined:
    Oct 13, 2018
    Posts:
    8
    ~7 years for this kind of problem :confused:
    yo unity team, how to tag them this thread.
     
    PuppetzMaster and petey like this.
  30. Krnitheesh16

    Krnitheesh16

    Joined:
    Apr 22, 2020
    Posts:
    14

    Awesome work! :)
     
  31. MingyueMeow

    MingyueMeow

    Joined:
    Feb 22, 2018
    Posts:
    6
    Life saver! Work like a charm!
     
    PuppetzMaster likes this.
  32. JJRivers

    JJRivers

    Joined:
    Oct 16, 2018
    Posts:
    137
    Seriously how is this Not an in-built feature already @Unity?
     
    PuppetzMaster likes this.
  33. PuppetzMaster

    PuppetzMaster

    Joined:
    Jul 14, 2017
    Posts:
    1
    Update: It is an Issue since 9 years by now. ;)
    In four months and a bit it's 10 years, anybody planning anything for that big anniversary? @Unity maybe? How about deprecating Mecanim as a surprise for everyone?

    Or maybe in another 10 years we might see this great discovery, thanks @gerudobomb, actually implemented in Unity!? -- 147 lines of code in 10 years, that's about 0.04 lines per day, could be possible one might think --
     
  34. JNoumenon

    JNoumenon

    Joined:
    Jan 12, 2022
    Posts:
    2
    It seems the copies behave more like instances here, in that any changes made to nested child BTs is replicated between all copies and the original.
    Could it be that nested child BTs gets copied with the same GUID or something so they all count as the same entry?
     
  35. marcospgp

    marcospgp

    Joined:
    Jun 11, 2018
    Posts:
    194
    Is copy pasting blend trees possible in Unity yet?

    Update: Seems not, but I was able to do it by opening the animator controller in a text editor and copying the data from the blend tree I wanted into a target blend tree I created in the editor. Just don't mess with the lines that look like
    --- !u!206 &2243240913118475771
    .
     
    Last edited: Nov 2, 2023