Jump to content
Unity Insider Forum

Automatisches Animation Split beim Importieren


Kevin

Recommended Posts

Hiho,

 

ich hatte vor ein paar Tagen noch was nützliches entwickelt, das ich mit der Community teilen möchte.

 

Was ist es?

 

Wenn man sich animierte 3D Modelle lädt oder kauft, dann kommen diese häufig mit dutzenden von Animationen. D.h. man müsste beim importieren in den Importsettings erstmal jede Animation einzeln "heraussplitten", sofern das 3D Model nicht als Unity Package vorliegt.

Das kann enorm lästig sein, besonders wenn ein 3D Model mal mehr als 15 Animationen enthält!

 

Glücklicherweise kommen die meisten der 3D Modelle zumindest mit einer Liste der Animationen, die z.B. so aussehen kann:

 

000 - 005 t_pose
010 - 050 draw
050 - 060 Single_Shot
060 - 075 Triple_Shot
075 - 100 Multiple_Shot
100 - 115 Zoom_In
115 - 120 Z_Single_Shot
120 - 135 Z_Triple_Shot
135 - 160 Z_Multiple_Shot
160 - 175 Zoom_Out
175 - 275 Reload
275 - 375 Idle
375 - 405 Walk
410 - 435 Run
440 - 470 Jump
470 - 480 Hide
485 - 540 Zoom_in_2
540 - 640 Zoom_idle
640 - 680 Zoom_out

 

Das Alles in Unity zu übertragen kostet unnötig Zeit, daher habe ich mir gedacht, dass ich ein Skript schreibe was dies automatisiert macht.

 

Wie installiert man es?

 

Der Code findet sich am Ende dieses Beitrages, dieser muss in ein neues C# Skript im Editor Ordner gepackt werden! Alle Skripts, die das GUI des Editors beinflussen oder den Editor auf andere Art und Weise beeinflussen müssen dort hinein.

Sobald dies geschehen ist, sollte es in der Menüleiste erscheinen. Evtl. muss man den Editor nochmal neustarten.

 

Wie funktioniert es?

 

Das Skript ist eine Editor Extension, wird also in der Menüleiste von Unity erscheinen. Hier die Schritte zum erfolgreichen ausführen des Skripts:

  1. Ein 3D Model im Project View auswählen, dessen Animation Split Settings aus einer Text Datei gelesen werden sollen.
  2. Das Skript starten, standardmäßig ist es in der Menüleiste unter "My Extensions/Load Animation Splits from file" zu finden.
  3. Nun wird sich ein Dialog öffnen, der auffordert eine .txt Datei auszuwählen. In dieser Datei muss sich die Liste der Animationen finden. Der Parser liest diese dann Zeile für Zeile, wobei fast egal ist wie diese Zeilene aufgebaut sind. Die einzige Bedingung ist, dass die Reihenfolge entweder "Name-FrameStart-FrameEnd" oder "FrameStart-FrameEnd-Name" sein muss. Was zwischen zwei solcher Textblöcke (Name, FrameStart, FrameEnd) ist, ist egal. Der Parser ist so geschrieben, dass er möglichst dynamisch ist.
  4. Es kann nun dazu kommen, dass dieser Dialog angezeigt wird:
    2suegrlx.jpg
    Dies liegt an einem fehlenden Feature von Unity, wofür ich noch keinen geeigneten Workaround gefunden habe. Man muss den Dialog einfach oben rechts in der Ecke schließen und darf auf keinen Fall Apply drücken!
  5. Nun sollten die Animationen innterhalb des Model Importers eingestellt sein: pujhqv9m.jpg

Wie immer wäre es schön, wenn ihr Bugs melden würdet. Besonders wenn eine bestimmte .txt Datei mit den Animationen nicht geparst werden konnte. Das Ziel ist es nämlich, dynamisch jede beliebige Animation Split Datei parsen zu können!

 

Der Code:

 

using System;
using System.Collections.Generic;
using System.IO;
using UnityEditor;
using Object = UnityEngine.Object;
public static class LoadSplitAnimationText {
/// <summary>Removes all invalid characters at the beginning of the text (e.g. whitespace).</summary>
static string RemoveStartCharacters(string text)
{
 int count = 0;
 foreach(char c in text)
 {
  if (!char.IsLetter(c) && !char.IsNumber(c) && c != '_')
count++;
  else
break;
 }
 return text.Substring(count);
}
static ModelImporterClipAnimation ParseLine(string text)
{
 //Two cases, either the text starts with the numbers or it starts with the
 //name.
 //Remove all the invalid stuff at the beginning...
 text = RemoveStartCharacters(text);
 string firstNumber = "";
 string secondNumber = "";
 string name = "";
 //Does it start with a number?
 if(char.IsNumber(text[0]))
 {
  int index = 0;
  //Read the first number...
  while(char.IsNumber(text[index]))
  {
firstNumber += text[index];
index++;
  }
  //Remove all the stuff between first and second number
  text = RemoveStartCharacters(text.Substring(index));
  index = 0;
  //Read the second number...
  while (char.IsNumber(text[index]))
  {
secondNumber += text[index];
index++;
  }
  //Remove all the stuff between second number and name
  text = RemoveStartCharacters(text.Substring(index));
  index = 0;
  //Read the name
  while (index <= text.Length - 1 && (char.IsLetter(text[index]) || char.IsWhiteSpace(text[index]) || text[index] == '_' || char.IsNumber(text[index])))
  {
if (!char.IsWhiteSpace(text[index])) name += text[index];
index++;
  }
 }
 //Does it start with a letter?
 else
 {
  int index = 0;
  //Read the name
  while (char.IsLetter(text[index]) || char.IsWhiteSpace(text[index]) || char.IsNumber(text[index]) || text[index] == '_')
  {
//If there was a whitespace and right afterwards a number, stop creating the name (the digit is the first digit of the
//animation start number)
if(index > 0 && char.IsWhiteSpace(text[index-1]) && char.IsNumber(text[index]))
 break;
if (!char.IsWhiteSpace(text[index])) name += text[index];
index++;
  }
  //Remove all the stuff between name and first number
  text = RemoveStartCharacters(text.Substring(index));
  index = 0;
  //Read first number...
  while (char.IsNumber(text[index]))
  {
firstNumber += text[index];
index++;
  }
  //Remove all the stuff between first and second number
  text = RemoveStartCharacters(text.Substring(index));
  index = 0;
  //Read the second number...
  while (index <= text.Length - 1 && char.IsNumber(text[index]))
  {
secondNumber += text[index];
index++;
  }
 }
 int firstIntNumber, secondIntNumber;
 //Try converting the strings to floats...
 if(!Int32.TryParse(firstNumber, out firstIntNumber))
 {
  EditorUtility.DisplayDialog("Error", string.Format("Error while parsing the animation frame start: '{0}'!", firstNumber), "Close");
  return null;
 }
 if (!Int32.TryParse(secondNumber, out secondIntNumber))
 {
  EditorUtility.DisplayDialog("Error", string.Format("Error while parsing the animation frame ende: '{0}'!", secondNumber), "Close");
  return null;
 }
 //...and return the result
 return new ModelImporterClipAnimation { firstFrame = firstIntNumber, lastFrame = secondIntNumber,  name = name };
}
static bool IsLineValid(string line)
{
 //Check for any valid character...
 foreach(char c in line)
 {
  if (char.IsNumber(c) || char.IsNumber(c)) return true;
 }
 return false;
}
[MenuItem("My Extensions/Load Animation Splits from file")]
static void CreateWizard()
{
 //Get the selected object in the project view.
 Object selectedObj = Selection.activeObject;
 //Create the importer...
 string assetPath = AssetDatabase.GetAssetPath(selectedObj);
 AssetImporter assetImporter = AssetImporter.GetAtPath(assetPath);
 if (assetImporter == null || !(assetImporter is ModelImporter))
 {
  EditorUtility.DisplayDialog("Error", "The selected file is no model! Only animations of models can be split.", "Close");
  return;
 }
 ModelImporter importer = assetImporter as ModelImporter;
 //Show open file dialog to user, to let him choose the txt file
 string file = EditorUtility.OpenFilePanel("Select the animation list text file!", "", "txt");
 //Check if the file is valid
 if (string.IsNullOrEmpty(file) || !File.Exists(file))
 {
  EditorUtility.DisplayDialog("Error", "No file selected, or the file does not exist!", "Close");
  return;
 }
 //This List will contain all the animations.
 List<ModelImporterClipAnimation> anims = new List<ModelImporterClipAnimation>();
 //Open the stream and begin reading
 using(FileStream stream = new FileStream(file, FileMode.Open))
 {
  using(StreamReader reader = new StreamReader(stream))
  {
//Stores the content of the current line.
string content = reader.ReadLine();
//If the first line is empty throw an error
if(string.IsNullOrEmpty(content))
{
 EditorUtility.DisplayDialog("Error", "The selected text file is empty!", "Close");
 return;
}
//Loop while the current line is valid.
while(!string.IsNullOrEmpty(content))
{
 if(!IsLineValid(content))
 {
  content = reader.ReadLine();
  continue;
 }
 //Parse the line...
 ModelImporterClipAnimation desc = ParseLine(content);

 if (desc == null)
  return; //Something went wrong.
 //Add the animation description to the list
 anims.Add(desc);
 //Read next line.
 content = reader.ReadLine();
}
  }
 }
 //anims contains now all the animation information...
 importer.splitAnimations = true;
 //Assing the filled list
 importer.clipAnimations = anims.ToArray();

 //And finally reimport the asset.
 AssetDatabase.ImportAsset(assetPath, ImportAssetOptions.ForceUpdate);
 //Hack to disable the "Unapplied Import Settings" Bug, see http://answers.unity3d.com/questions/14246/editor-class-texture-importer-apply-import-setting.html
 //Note: Doesn't always work!
 Selection.objects = new Object[0];
}
}

 

P.S.: Wieso formatiert sich der denn nicht anständig? :mellow:

Link zu diesem Kommentar
Auf anderen Seiten teilen

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Gast
Auf dieses Thema antworten...

×   Du hast formatierten Text eingefügt.   Formatierung jetzt entfernen

  Only 75 emoji are allowed.

×   Dein Link wurde automatisch eingebettet.   Einbetten rückgängig machen und als Link darstellen

×   Dein vorheriger Inhalt wurde wiederhergestellt.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Lädt...
×
×
  • Neu erstellen...