0
\$\begingroup\$

I want to split all the animations inside a FBX by using a .txt file which contains start times and names for the animations in this FBX.

I found a script that does exactely that here.

However, this script uses the PostProcessor, and this means that I have little possiblity over when it's called.

Instead, I would like this to happen when I click a certain menu item. To do that, I have added a new menu item to the Unity toolbar like this:

public class NewBehaviourScript : MonoBehaviour
{
    [MenuItem("Tools/FBX/Split")]
    private static void SplitFBX()
    {

However, as soon as I add the line

 ModelImporter modelImporter = assetImporter as ModelImporter;

from the other script, the Editor says that

 "The name assetImporter isn't available in the current context".

Does anybody see why this compiler error occurs?

I have also tried

"public class NewBehaviourScript : AssetPostprocessor" 

but it gave me the error "An object reference is required for the non-static field, method or property "AssetPostprocessor.assetImporter".

This is the entire script:

using UnityEngine;
using UnityEditor;
using UnityEditor.VersionControl;
using System.IO;
using System.Collections;
using System.Text.RegularExpressions;
using System;

public class NewBehaviourScript : MonoBehaviour
{
    [MenuItem("Tools/FBX/Split")]
    private static void SplitFBX()
    {
        var selected = Selection.activeObject;
        string assetPath = AssetDatabase.GetAssetPath(selected);

        EditorUtility.DisplayDialog("", assetPath, "Ok");

        try
        {
            // Remove 6 chars because dataPath and assetPath both contain "assets" directory
            string fileAnim = Application.dataPath + Path.ChangeExtension(assetPath, ".txt").Substring(6);

            if (!File.Exists(fileAnim))
            {
                return;
            }

            StreamReader file = new StreamReader(fileAnim);

            string sAnimList = file.ReadToEnd();
            file.Close();

            if (EditorUtility.DisplayDialog("FBX Animation Import from file", fileAnim, "Import", "Cancel"))
            {
                System.Collections.ArrayList List = new ArrayList();
                pParseAnimFile(sAnimList, ref List);

                ModelImporter modelImporter = assetImporter as ModelImporter;
                modelImporter.clipAnimations = (ModelImporterClipAnimation[])List.ToArray(typeof(ModelImporterClipAnimation));

                EditorUtility.DisplayDialog("Imported animations", "Number of imported clips: " + modelImporter.clipAnimations.GetLength(0).ToString(), "OK");
            }
        }
        catch (Exception e)
        {
            // EditorUtility.DisplayDialog("Imported animations error: ", e.Message, "OK");
        }
    }

    void pParseAnimFile(string sAnimList, ref System.Collections.ArrayList List)
    {
        Regex regexString = new Regex(" *(?<firstFrame>[0-9]+) *- *(?<lastFrame>[0-9]+) *(?<loop>(loop|noloop| )) *(?<name>[^\r^\n]*[^\r^\n^ ])",
                                      RegexOptions.Compiled | RegexOptions.ExplicitCapture);

        Match match = regexString.Match(sAnimList, 0);
        while (match.Success)
        {
            ModelImporterClipAnimation clip = new ModelImporterClipAnimation();

            if (match.Groups["firstFrame"].Success)
            {
                clip.firstFrame = System.Convert.ToInt32(match.Groups["firstFrame"].Value, 10);
            }
            if (match.Groups["lastFrame"].Success)
            {
                clip.lastFrame = System.Convert.ToInt32(match.Groups["lastFrame"].Value, 10);
            }
            if (match.Groups["loop"].Success)
            {
                clip.loop = match.Groups["loop"].Value == "loop";
            }
            if (match.Groups["name"].Success)
            {
                clip.name = match.Groups["name"].Value;
            }

            List.Add(clip);

            match = regexString.Match(sAnimList, match.Index + match.Length);
        }
    }
}
\$\endgroup\$
5
  • 1
    \$\begingroup\$ You've been using C# long enough now to know that you can't use a variable that hasn't been declared. assetImporter is a member variable of AssetPostprocessor. By making your script a MonoBehaviour, it is no longer an AssetPostprocessor, so it has no assetImporter member. Instead of asking "why is my compiler telling me this obviously illegal thing is illegal?" you probably want to ask "how can I make a menu item that splits animations based on a text file?" \$\endgroup\$
    – DMGregory
    May 29, 2021 at 20:52
  • \$\begingroup\$ @DMGregory I have also tried "public class NewBehaviourScript : AssetPostprocessor" but it gave me the error "An object reference is required for the non-static field, method or property "AssetPostprocessor.assetImporter". \$\endgroup\$
    – tmighty
    May 29, 2021 at 20:54
  • 1
    \$\begingroup\$ Right, because assetImporter is a non-static member. You need an instance of the class to be able to access its non-static members — you can't do it from a static member. Again, this is C# 101 material. If you're finding it surprising, you might want to work your way through some more beginner C# tutorials to make sure you understand the language you're using. \$\endgroup\$
    – DMGregory
    May 29, 2021 at 21:06
  • \$\begingroup\$ @DMGregory So it's obviously complicated / not doable the way I planned it, else you would have simply told me what to do change. And yes, I really don't understand what this weird line is supposed to mean: "ModelImporter modelImporter = assetImporter as ModelImporter;". Doesn't make sense to me. It would have been nice if somebody told me in words what it means, then I would perhaps be able to figure out a solution. \$\endgroup\$
    – tmighty
    May 29, 2021 at 21:19
  • 1
    \$\begingroup\$ That's a pretty ordinary cast. It's taking the importer member of the processor, which is of a base class, and casting it to a more derived class — the one used for importing models. That way subsequent code can access members specific to the model importer, that might not be universal for all asset importers. \$\endgroup\$
    – DMGregory
    May 29, 2021 at 21:31

1 Answer 1

0
\$\begingroup\$

I got the solution:

I have re-written the code because some variable names were not easy to understand.

And as DMGregory pointed out, now that the ModelImporter is called from a different location (and not from the PostProcessor), it doesn't know which FBX we're dealing with, so we need to get the ModelImporter from the Asset.

Here is what it looks like:

using UnityEngine;
using UnityEditor;
using UnityEditor.VersionControl;
using System.IO;
using System.Collections;
using System.Text.RegularExpressions;
using System;
using System.Collections.Generic;

public class NewBehaviourScript : AssetPostprocessor
{
    [MenuItem("Tools/FBX/Split")]
    private static void SplitFBX()
    {
        var nSelectedObject = Selection.activeObject;
        string sAssetPath = AssetDatabase.GetAssetPath(nSelectedObject);

        EditorUtility.DisplayDialog("", sAssetPath, "Ok");

        try
        {
            string sPathTextfile = Application.dataPath + Path.ChangeExtension(sAssetPath, ".txt").Substring(6);// Remove 6 chars because dataPath and assetPath both contain "assets" directory

            if (!File.Exists(sPathTextfile))
            {
                return;
            }

            var sLines = new List<string>();

            var lines = File.ReadAllLines(sPathTextfile);
            foreach (string line in lines)
            {
                sLines.Add(line);
            }

            System.Collections.ArrayList nList = new ArrayList();
            pParseAnimFile(sLines, ref nList);

            ModelImporter nModelImporter = (ModelImporter)AssetImporter.GetAtPath(sAssetPath);

            nModelImporter.clipAnimations = (ModelImporterClipAnimation[])nList.ToArray(typeof(ModelImporterClipAnimation));

            EditorUtility.DisplayDialog("Imported animations", "Number of imported clips: " + nModelImporter.clipAnimations.GetLength(0).ToString(), "OK");
        }
        catch (Exception e)
        {
            // EditorUtility.DisplayDialog("Imported animations error: ", e.Message, "OK");
        }
    }


    private static void pParseAnimFile(List<string> sAnimList, ref System.Collections.ArrayList uList)
    {
        // My text file has lines that look like this: animation name, 0, 76
        for (int i = 0; i < sAnimList.Count; i++)
        {
            string s = sAnimList[i];
            List<string> sItems = SplitReturnEmptyListIfInputIsEmptyEx(s, ",");

            string sName = sItems[0].Trim();
            string sFrom = sItems[1].Trim();
            string sTo = sItems[2].Trim();

            ModelImporterClipAnimation nClip = new ModelImporterClipAnimation();
            nClip.firstFrame = float.Parse(sFrom);
            nClip.lastFrame = float.Parse(sTo);
            nClip.loop = false;
            nClip.name = sName;
            uList.Add(nClip);
        }
    }
    public static List<string> SplitReturnEmptyListIfInputIsEmptyEx(string u, string uSplitBy)
    {
        var n = new List<string>();
        if (string.IsNullOrEmpty(u))
        {
        }
        // do nothing, else we get a "" string as the only item in our list
        else
        {
            var sDelimitersAsStringArray = new string[] { uSplitBy };
            var sLines = u.Split(sDelimitersAsStringArray, StringSplitOptions.None);
            foreach (string sLine in sLines)
                n.Add(sLine);
        }

        return n;
    }
}
\$\endgroup\$
5
  • \$\begingroup\$ Did you test this code to see if it actually accomplishes what you want it to do? \$\endgroup\$
    – DMGregory
    May 29, 2021 at 22:25
  • \$\begingroup\$ @DMGregory Both approaches do not work. With both approaches, nModelImporter is null. \$\endgroup\$
    – tmighty
    May 29, 2021 at 22:55
  • 2
    \$\begingroup\$ Yep. That's what happens when you try to take something that's not a ModelImporter and try to cast it to be one. Just conceptually, the code you've written here cannot work. How would it know which file's animations to modify - you never tell the asset importer which FBX it's supposed to be importing. It doesn't even know it's supposed to be importing a model file. \$\endgroup\$
    – DMGregory
    May 29, 2021 at 23:05
  • \$\begingroup\$ So I would have to say something like new MyFBXFile.AssetPostProcessor().assetImporter(), correct? \$\endgroup\$
    – tmighty
    May 29, 2021 at 23:32
  • \$\begingroup\$ Yes, I think I got it: ModelImporter nModelImporter = (ModelImporter)AssetImporter.GetAtPath(sAssetPath); \$\endgroup\$
    – tmighty
    May 29, 2021 at 23:36

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .