Search Unity

Editor Script - How to import animations?

Discussion in 'Scripting' started by baylor, May 26, 2010.

  1. baylor

    baylor

    Joined:
    Oct 29, 2009
    Posts:
    25
    We have dozens of animations and every week we get more. Manually entering them all in the editor is time consuming and boring so we've been trying to automate it using an editor script (we like writing editor scripts). But we can't figure out how to add new animations

    Here's part of the code:

    Code (csharp):
    1. ModelImporter modelImporter;
    2. string path = AssetDatabase.GetAssetPath(3DSMaxFBXthingy);
    3. modelImporter = AssetImporter.GetAtPath(path) as ModelImporter;
    4. //--- This line goes boom
    5. modelImporter.clipAnimations = new ModelImporterClipAnimation[numAnimations];
    6. ...code that reads a text file, builds ModelImporterClipAnimation, adds them to the array, calls AssetDatabase.ImportAsset, etc.
    7.  
    We have a character model already in the game. We get a reference to it. The model stores all the animations (name, start frame, end frame, wrap mode) in the property
    ModelImporterClipAnimation[] clipAnimations
    That's a normal array, not a List<> or ArrayList so it can't dynamically grow. Therefore, to add 10 animations, we create a new array of 10 items and try to set that. But it doesn't like it when we do that

    The error we get we don't understand. The above line calls System.Reflection.MethodBase.Invoke which calls HostView.OnGUI, HostView.Invoke, another Reflection.MethodBase.Invoke which finally leads to the error:
    TargetInvocationException: Exception has been thrown by the target of an invocation.
    System.Reflection.MonoMethod.Invoke

    Since that's all Unity code, we can't see what's going on or the parameters involved but i'm guessing we don't need to - i suspect the error is more our approach than any specific niggly detail in the Unity code

    i realize not many people have probably tried this but anyone have any insight into how to make this import animation thing work?
     
  2. andeeeee

    andeeeee

    Joined:
    Jul 19, 2005
    Posts:
    8,768
    Just to check, do you have splitAnimations set before you use this code?
     
  3. baylor

    baylor

    Joined:
    Oct 29, 2009
    Posts:
    25
    Yup

    Not sure if people want to see the full code but here's what we wrote:

    Code (csharp):
    1.  
    2. using UnityEditor;
    3. using UnityEngine;
    4. using System.Collections.Generic;
    5.  
    6. public class AnimsImporter : ScriptableWizard {
    7.     public GameObject doll;
    8.     public TextAsset animFile;
    9.     private ModelImporter modelImporter;
    10.  
    11.     [MenuItem("Editor/Models/Import Doll Anims")]
    12.     static void Import()
    13.     {
    14.         ScriptableWizard.DisplayWizard(
    15.             "Import anims", typeof(AnimsImporter),
    16.             "Apply  Close");
    17.     }
    18.     void OnWizardCreate()
    19.     {
    20.         apply();   
    21.     }
    22.     void apply()
    23.     {
    24.         string path = AssetDatabase.GetAssetPath(doll);
    25.         modelImporter = AssetImporter.GetAtPath(path) as ModelImporter;
    26.         loadAnimationsFromText(animFile.text);
    27.         AssetDatabase.ImportAsset(path);
    28.     }
    29.     public void loadAnimationsFromText(string animationsDescriptorFile)
    30.     {
    31.         char[]   delimiters = ";".ToCharArray();
    32.         string[] lines = animationsDescriptorFile.Split(delimiters);
    33.         modelImporter.splitAnimations = true;
    34. -!! THIS IS WHERE IT BLOWS UP !!-
    35.         modelImporter.clipAnimations = new ModelImporterClipAnimation[lines.Length];
    36.         for(int i=0;i<lines.Length;i++)
    37.         {
    38.             string line = lines[i];
    39.             modelImporter.clipAnimations[i] = parse(line);
    40.         }
    41.     }
    42.  
    43.     public ModelImporterClipAnimation parse(string textEncoding)
    44.     {
    45.         ModelImporterClipAnimation newClip = new ModelImporterClipAnimation();
    46.        
    47.         char[] delimiters = " =-".ToCharArray();
    48.  
    49.         //--- Fix the tokens (two spaces makes a blank token, not what we want)
    50.         List<string> tokens = new List<string>();
    51.         string[] badTokens = textEncoding.Split(delimiters);
    52.         foreach (string token in badTokens)
    53.         {
    54.             if (!token.Equals(string.Empty))
    55.                 tokens.Add(token.ToString());
    56.         }
    57.  
    58.         newClip.name        = tokens[0];
    59.         newClip.firstFrame  = int.Parse(tokens[1]);
    60.         newClip.lastFrame   = int.Parse(tokens[2]);
    61.         newClip.loop        = false;
    62.         newClip.wrapMode = WrapMode.Once;
    63.  
    64.         return newClip;
    65.     }
    66. }
    67.  
     
  4. Paulius-Liekis

    Paulius-Liekis

    Joined:
    Aug 31, 2009
    Posts:
    672
    I'm not sure what's happening, but I will try your script on Monday. Usually people modify ModelImporter in PreProcessModel, but your approach might be valid too (I don't know if ModelImporter can be accessed during other phases). It might be something specific to clipAnimations.
     
  5. Paulius-Liekis

    Paulius-Liekis

    Joined:
    Aug 31, 2009
    Posts:
    672
    Hi baylor,

    Sorry for taking longer than promised - I had to fix some high-priority beta1 bugs yesterday.

    There are several conclusions:

    1)
    Code (csharp):
    1. modelImporter.clipAnimations = new ModelImporterClipAnimation[1];
    throws an exception. It shouldn't. It caused by incoherent design on C# and C++ side in Unity (what happening is that members of new array are null, so that's why you get an exception). I'll raise a bug on it.

    2) Even if the one above would work this:

    Code (csharp):
    1. modelImporter.clipAnimations[i] = parse(line);
    wouldn't do what you want, because in Unity arrays are always returned as copies, so you would be modifying your local copy and modelImporter.clipAnimations wouldn't be affected.

    3) The solution is to construct your array first and then assign it to modelImporter.clipAnimations. Something like this:

    Code (csharp):
    1. ModelImporterClipAnimation[] mica = new ModelImporterClipAnimation[1];
    2. mica[0] = new ModelImporterClipAnimation();
    3. // fill mica[0]
    4. modelImporter.clipAnimations = mica;

    I hope this solves your problem.
     
  6. baylor

    baylor

    Joined:
    Oct 29, 2009
    Posts:
    25
    Thanks Paulius. Finished testing it, that works, now our life is much, much happier :)
     
  7. kennethharder

    kennethharder

    Joined:
    Feb 17, 2009
    Posts:
    2
    This is going to save us tons of time - Thanks!
     
  8. SinisterMephisto

    SinisterMephisto

    Joined:
    Feb 18, 2011
    Posts:
    179
    This method works on a 1:1 model to animation file ratio. In my case i have 4 distinct models and i want them all to share from a single animation file.
    The problems is that if i split the clips i have to manually move over 1000clips into each model.
    If i dont split the clips once i extract it from the source model(prefab) all the partitioning is lost.

    Please help cos the only version that works run time takes 6+ minutes to partition the animation.
     
  9. SinisterMephisto

    SinisterMephisto

    Joined:
    Feb 18, 2011
    Posts:
    179
    Nevermind i found a cheap trick. Loaded the prefab containing the animation into a game object then copied the animation states at runtime