- Home /
FBX animation properties inspector, events with code
need to edit fbx animations with code: http://screencast.com/t/eoSdcJzQ this is the inspector part where it can be edited without changing exported animation (animation tab of fbx properties inspector)
P.S. there is a way to do it for separate animations, but in this case I would like to change import settings, not the animation itself.
AnimationUtility.SetAnimationEvents(c, new AnimationEvent[] {
new AnimationEvent() { functionName = "onEvent", floatParameter = c.length, intParameter = 0 ,time = 0}
});
Would FbxAnimListPostprocessor perhaps help?
http://wiki.unity3d.com/index.php/FbxAnimListPostprocessor
Answer by dnkira · Dec 23, 2014 at 12:12 PM
Made a little Editor Script: - Setting Animation Events in export settings - Exporting editable clones - Setting Clones for Animation component
using System.Collections.Generic;
using System.Linq;
using UnityEditor;
using UnityEngine;
public class AnimationProperties
{
public readonly string Name;
public readonly float Length;
public float procTime = 0;
public AnimationProperties(string Name, float Length)
{
// TODO: Complete member initialization
this.Name = Name;
this.Length = Length;
}
}
public class FBXEventEditor : EditorWindow
{
private const string ANIMATION_FOLDER = "Animation";
[MenuItem("Animation/EBX animation events")]
public static void ShowWindow()
{
EditorWindow.GetWindow(typeof(FBXEventEditor));
}
Object _targetObject;
Object obj;
List<AnimationProperties> animationProperties = new List<AnimationProperties>();
void OnGUI()
{
obj = EditorGUILayout.ObjectField("FBX Asset", obj, typeof(GameObject), false);
if (GUILayout.Button("ReRead"))
{
_targetObject = null;
}
if (obj != _targetObject)
{
_targetObject = obj;
UpdateAnimationList();
}
EditorGUILayout.BeginHorizontal();
GUILayout.Label("Name", GUILayout.Width(100));
GUILayout.Label("Length", GUILayout.Width(43));
GUILayout.Label("Proc", GUILayout.Width(43));
EditorGUILayout.EndHorizontal();
for (int i = 0; i < animationProperties.Count; i++)
{
AnimationProperties ap = animationProperties[i];
EditorGUILayout.BeginHorizontal();
GUILayout.Label(ap.Name, GUILayout.Width(100));
GUILayout.Label(ap.Length.ToString(), GUILayout.Width(43));
ap.procTime = EditorGUILayout.Slider(ap.procTime, 0, 1);
EditorGUILayout.EndHorizontal();
}
if (GUILayout.Button("Set import events"))
{
SaveExportSettings();
}
GUILayout.Space(20);
if (GUILayout.Button("Export Editable"))
{
ExportEditable();
}
if (GUILayout.Button("Set exported 2 selected"))
{
SetAnimationClips();
}
}
private void ExportEditable()
{
AssetDatabase.Refresh(ImportAssetOptions.ForceUpdate);
string path = AssetDatabase.GetAssetPath(obj);
Object[] assets = AssetDatabase.LoadAllAssetsAtPath(path);
List<AnimationClip> clips = assets.ToList().Where(x => x is AnimationClip && !x.name.Contains("__preview__")).Cast<AnimationClip>().ToList();
string clipFolder = System.IO.Path.Combine(System.IO.Directory.GetParent(path).ToString(), ANIMATION_FOLDER);
if (!AssetExtentions.DirectoryExists(clipFolder))
{
Debug.Log("Create folder");
AssetDatabase.CreateFolder(System.IO.Directory.GetParent(path).ToString(), ANIMATION_FOLDER);
}
foreach (AnimationClip sourceClip in clips)
{
AnimationProperties ap = animationProperties.Where(x => x.Name == sourceClip.name).FirstOrDefault();
if (ap != null)
{
string clipPath = clipFolder + "/" + sourceClip.name + ".anim";
Debug.Log(string.Format("Saving: {0} at {1}", sourceClip.name, clipPath));
AnimationClip destClip = AssetDatabase.LoadAssetAtPath(clipPath, typeof(AnimationClip)) as AnimationClip;
if (destClip == null)
{
Debug.Log("new clip");
destClip = new AnimationClip();
AssetDatabase.CreateAsset(destClip, clipPath);
}
EditorUtility.CopySerialized(sourceClip, destClip);
List<AnimationEvent> events = new List<AnimationEvent>();
events.Add(new AnimationEvent() { time = 0, functionName = "onAnimationEvent", intParameter = (int)AnimationEventType.Start, stringParameter = destClip.name });
events.Add(new AnimationEvent() { time = destClip.length, functionName = "onAnimationEvent", intParameter = (int)AnimationEventType.End, stringParameter = destClip.name });
if (ap.procTime != 0)
{
events.Add(new AnimationEvent() { time = destClip.length * ap.procTime, functionName = "onAnimationEvent", intParameter = (int)AnimationEventType.Proc, stringParameter = destClip.name });
}
AnimationUtility.SetAnimationEvents(destClip, events.ToArray());
}
else
{
Debug.LogWarning("not found settings WTF?!:" + sourceClip.name);
}
}
Debug.Log(string.Join(",", clips.Select(x => x.name).ToArray()));
AssetDatabase.SaveAssets();
AssetDatabase.Refresh(ImportAssetOptions.ForceUpdate);
Debug.Log("Export Done");
//AssetDatabase.ImportAsset(AssetDatabase.GetAssetPath(_targetObject));
}
private void SaveExportSettings()
{
ModelImporter modelImporter = (ModelImporter)AssetImporter.GetAtPath(AssetDatabase.GetAssetPath(obj));
SerializedObject so = new SerializedObject(modelImporter);
SerializedProperty clips = so.FindProperty("m_ClipAnimations");
List<AnimationEvent[]> animationEvents = new List<AnimationEvent[]>(modelImporter.clipAnimations.Length);
for (int i = 0; i < modelImporter.clipAnimations.Length; i++)
{
modelImporter.clipAnimations[i].wrapMode = WrapMode.ClampForever;
List<AnimationEvent> events = new List<AnimationEvent>();
//events.AddRange(GetEvents(clips.GetArrayElementAtIndex(i)));//if we need 2 use old events
events.Add(new AnimationEvent() { stringParameter = animationProperties[i].Name, functionName = "onAnimationEvent", time = 0, intParameter = 0 });
if (animationProperties[i].procTime != 0)
events.Add(new AnimationEvent() { stringParameter = animationProperties[i].Name, functionName = "onAnimationEvent", time = animationProperties[i].procTime, intParameter = 1 });
events.Add(new AnimationEvent() { stringParameter = animationProperties[i].Name, functionName = "onAnimationEvent", time = 1, intParameter = 2 });
animationEvents.Add(events.ToArray());
}
// Make your changes and write them by setting clipAnimations, destroying the events.
modelImporter.clipAnimations = modelImporter.clipAnimations;
for (int i = 0; i < modelImporter.clipAnimations.Length; i++)
{
SetEvents(clips.GetArrayElementAtIndex(i), animationEvents[i]);
}
so.SetIsDifferentCacheDirty();
so.ApplyModifiedProperties();
AssetDatabase.SaveAssets();
AssetDatabase.ImportAsset(AssetDatabase.GetAssetPath(_targetObject));
}
private void UpdateAnimationList()
{
animationProperties.Clear();
ModelImporter modelImporter = (ModelImporter)AssetImporter.GetAtPath(AssetDatabase.GetAssetPath(_targetObject));
SerializedObject so = new SerializedObject(modelImporter);
SerializedProperty clips = so.FindProperty("m_ClipAnimations");
for (int i = 0; i < modelImporter.clipAnimations.Length; i++)
{
ModelImporterClipAnimation iClipAnim = modelImporter.clipAnimations[i];
animationProperties.Add(new AnimationProperties(iClipAnim.name, modelImporter.clipAnimations[i].lastFrame - modelImporter.clipAnimations[i].firstFrame));
}
}
private void SetAnimationClips()
{
Animation targetAnimationComponent = Selection.activeGameObject.animation;
if (targetAnimationComponent == null)
{
ShowNotification(new GUIContent("No animation selected!", "Select a gameobject with animation component."));
}
else
{
WrapMode savedWrapMode = targetAnimationComponent.wrapMode;
bool savedPlayAutomaticallyState = targetAnimationComponent.playAutomatically;
bool savedAnimatePhysicsState = targetAnimationComponent.animatePhysics;
DestroyImmediate(targetAnimationComponent);
targetAnimationComponent = Selection.activeGameObject.AddComponent<Animation>();
targetAnimationComponent.wrapMode = savedWrapMode;
targetAnimationComponent.playAutomatically = savedPlayAutomaticallyState;
targetAnimationComponent.animatePhysics = savedAnimatePhysicsState;
string path = AssetDatabase.GetAssetPath(obj);
Object[] assets = AssetDatabase.LoadAllAssetsAtPath(path);
List<AnimationClip> clips = assets.ToList().Where(x => x is AnimationClip && !x.name.Contains("__preview__")).Cast<AnimationClip>().ToList();
string clipFolder = System.IO.Path.Combine(System.IO.Directory.GetParent(path).ToString(), ANIMATION_FOLDER);
foreach (AnimationClip sourceClip in clips)
{
string clipPath = clipFolder + "/" + sourceClip.name + ".anim";
AnimationClip destClip = AssetDatabase.LoadAssetAtPath(clipPath, typeof(AnimationClip)) as AnimationClip;
targetAnimationComponent.AddClip(destClip, destClip.name);
if (targetAnimationComponent.clip == null)
{
targetAnimationComponent.clip = destClip;
}
}
}
}
public AnimationEvent[] GetEvents(SerializedProperty sp)
{
SerializedProperty serializedProperty = sp.FindPropertyRelative("events");
AnimationEvent[] array = null;
if (serializedProperty != null && serializedProperty.isArray)
{
int count = serializedProperty.arraySize;
array = new AnimationEvent[count];
for (int i = 0; i < count; i++)
{
AnimationEvent animationEvent = new AnimationEvent();
SerializedProperty eventProperty = serializedProperty.GetArrayElementAtIndex(i);
animationEvent.floatParameter = eventProperty.FindPropertyRelative("floatParameter").floatValue;
animationEvent.functionName = eventProperty.FindPropertyRelative("functionName").stringValue;
animationEvent.intParameter = eventProperty.FindPropertyRelative("intParameter").intValue;
animationEvent.objectReferenceParameter = eventProperty.FindPropertyRelative("objectReferenceParameter").objectReferenceValue;
animationEvent.stringParameter = eventProperty.FindPropertyRelative("data").stringValue;
animationEvent.time = eventProperty.FindPropertyRelative("time").floatValue;
array[i] = animationEvent;
}
}
return array;
}
public void SetEvents(SerializedProperty sp, AnimationEvent[] newEvents)
{
SerializedProperty serializedProperty = sp.FindPropertyRelative("events");
if (serializedProperty != null && serializedProperty.isArray && newEvents != null && newEvents.Length > 0)
{
serializedProperty.ClearArray();
for (int i = 0; i < newEvents.Length; i++)
{
AnimationEvent animationEvent = newEvents[i];
serializedProperty.InsertArrayElementAtIndex(serializedProperty.arraySize);
SerializedProperty eventProperty = serializedProperty.GetArrayElementAtIndex(i);
eventProperty.FindPropertyRelative("floatParameter").floatValue = animationEvent.floatParameter;
eventProperty.FindPropertyRelative("functionName").stringValue = animationEvent.functionName;
eventProperty.FindPropertyRelative("intParameter").intValue = animationEvent.intParameter;
eventProperty.FindPropertyRelative("objectReferenceParameter").objectReferenceValue = animationEvent.objectReferenceParameter;
eventProperty.FindPropertyRelative("data").stringValue = animationEvent.stringParameter;
eventProperty.FindPropertyRelative("time").floatValue = animationEvent.time;
}
}
}
}
@dnkira , This script is outdated. Any chance you've updated it? Thanks
Your answer
Follow this Question
Related Questions
3ds Max importer is broken 3 Answers
Button doesn't work when returning to scene (android) 1 Answer
Should I initialize my Unity events? 1 Answer
3d max importing problems 0 Answers