Skip to content

Instantly share code, notes, and snippets.

@SolarianZ
Last active May 7, 2024 04:58
Show Gist options
  • Save SolarianZ/8e582f2498e1f223b89519b4505c037a to your computer and use it in GitHub Desktop.
Save SolarianZ/8e582f2498e1f223b89519b4505c037a to your computer and use it in GitHub Desktop.
{"category": "Unity Engine/Editor/Extensions", "keywords": "Unity, Editor, AnimationClip, AnimationEvent, Rename"} Log/Rename AnimationEvents of selected AnimationClip/FBXs in Unity Editor.
using System;
using System.Linq;
using UnityEditor;
using UnityEngine;
using UnityEngine.UIElements;
public static class AnimationClipEventTool
{
#region Log Animation Events
[MenuItem("Tools/Bamboo/Animation/Log Events of Selected AnimationClips")]
public static void LogEventsOfSelectedAnimationClips()
{
var eventCount = 0;
var fbxs = Selection.GetFiltered<GameObject>(SelectionMode.Assets);
foreach (var fbx in fbxs)
{
var fbxPath = AssetDatabase.GetAssetPath(fbx);
var allAssets = AssetDatabase.LoadAllAssetRepresentationsAtPath(fbxPath);
var clips = allAssets.Where(a => a is AnimationClip).Cast<AnimationClip>();
foreach (var clip in clips)
{
eventCount += LogAnimationEvents(clip);
}
}
var animationClips = Selection.GetFiltered<AnimationClip>(SelectionMode.Unfiltered);
foreach (var clip in animationClips)
{
eventCount += LogAnimationEvents(clip);
}
Debug.Log($"Found {eventCount} AnimationEvent(s) in selected AnimationClip(s).");
}
[MenuItem("Tools/Bamboo/Animation/Log Events of Selected AnimationClips", validate = true)]
public static bool LogEventsOfSelectedAnimationClipsValidate()
{
var fbxs = Selection.GetFiltered<GameObject>(SelectionMode.Assets);
if (fbxs.Length > 0) return true;
var animationClips = Selection.GetFiltered<AnimationClip>(SelectionMode.Unfiltered);
return animationClips.Length > 0;
}
public static int LogAnimationEvents(AnimationClip clip)
{
var eventCount = 0;
var events = AnimationUtility.GetAnimationEvents(clip);
foreach (var evt in events)
{
eventCount++;
Debug.Log($"AnimationEvent '{evt.functionName}' on {clip}.", clip);
}
return eventCount;
}
#endregion
#region Rename Animation Event
[MenuItem("Tools/Bamboo/Animation/Rename Events of Selected AnimationClips")]
public static void RenameEventsOfSelectedAnimationClips()
{
EventNameEditorWindow.Popup((oldName, newName) =>
{
var renamedEventCount = 0;
var fbxs = Selection.GetFiltered<GameObject>(SelectionMode.Assets);
foreach (var fbx in fbxs)
{
var fbxPath = AssetDatabase.GetAssetPath(fbx);
var modelImporter = AssetImporter.GetAtPath(fbxPath) as ModelImporter;
if (!modelImporter) continue;
renamedEventCount += RenameAnimationEvents(modelImporter, oldName, newName);
}
var animationClips = Selection.GetFiltered<AnimationClip>(SelectionMode.Assets); // 不处理FBX内嵌套的AnimationClip
foreach (var clip in animationClips)
{
renamedEventCount += RenameAnimationEvents(clip, oldName, newName);
}
Debug.Log($"Renamed {renamedEventCount} AnimationEvent(s) in selected AnimationClip(s).");
});
}
[MenuItem("Tools/Bamboo/Animation/Rename Events of Selected AnimationClips", validate = true)]
public static bool RenameEventsOfSelectedAnimationClipsValidate()
{
var fbxs = Selection.GetFiltered<GameObject>(SelectionMode.Assets);
if (fbxs.Length > 0) return true;
var animationClips = Selection.GetFiltered<AnimationClip>(SelectionMode.Assets); // 不处理FBX内嵌套的AnimationClip
return animationClips.Length > 0;
}
public static int RenameAnimationEvents(AnimationClip clip, string oldName, string newName)
{
if (oldName == newName) return 0;
var eventCount = 0;
var events = AnimationUtility.GetAnimationEvents(clip);
foreach (var evt in events)
{
if (evt.functionName != oldName) continue;
eventCount++;
evt.functionName = newName;
Debug.Log($"Renamed AnimationEvent '{oldName}' to '{newName}' on {clip}.", clip);
}
if (eventCount > 0)
{
Undo.RegisterCompleteObjectUndo(clip, "Rename Animation Events");
AnimationUtility.SetAnimationEvents(clip, events);
}
return eventCount;
}
public static int RenameAnimationEvents(ModelImporter modelImporter, string oldName, string newName)
{
if (oldName == newName) return 0;
var eventCount = 0;
var clips = modelImporter.clipAnimations;
foreach (var clip in clips)
{
var events = clip.events;
foreach (var evt in events)
{
if (evt.functionName != oldName) continue;
eventCount++;
evt.functionName = newName;
Debug.Log($"Renamed AnimationEvent '{oldName}' to '{newName}' on {clip.name}.", modelImporter);
}
clip.events = events;
}
if (eventCount > 0)
{
Undo.RegisterCompleteObjectUndo(modelImporter, "Rename Animation Events");
modelImporter.clipAnimations = clips;
modelImporter.SaveAndReimport();
}
return eventCount;
}
class EventNameEditorWindow : EditorWindow
{
public static void Popup(Action<string, string> onSubmit)
{
var window = CreateInstance<EventNameEditorWindow>();
var center = EditorGUIUtility.GetMainWindowPosition().center;
window.position = new Rect(center.x - 75, center.y - 32, 150, 64);
window._onSubmit = onSubmit;
window.ShowPopup();
window.FocusOldNameInput();
}
private TextField _oldNameField;
private TextField _newNameField;
private Button _submitButton;
private Action<string, string> _onSubmit;
private void OnEnable()
{
rootVisualElement.style.paddingLeft = 1;
rootVisualElement.style.paddingRight = 1;
rootVisualElement.style.paddingTop = 2;
rootVisualElement.style.paddingBottom = 2;
// Old name
_oldNameField = new TextField
{
label = "Old Name",
isDelayed = true,
style =
{
minWidth = 40,
},
labelElement =
{
style =
{
minWidth = 68,
width = 68,
}
}
};
rootVisualElement.Add(_oldNameField);
// New name
_newNameField = new TextField
{
label = "New Name",
isDelayed = true,
style =
{
minWidth = 40,
},
labelElement =
{
style =
{
minWidth = 68,
width = 68,
}
}
};
_newNameField.RegisterValueChangedCallback(OnNewNameChanged);
rootVisualElement.Add(_newNameField);
// Submit button
_submitButton = new Button(TrySubmit)
{
text = "Ok",
};
rootVisualElement.Add(_submitButton);
}
private void OnLostFocus()
{
Close();
}
public void FocusOldNameInput()
{
_oldNameField.Focus();
}
private void OnNewNameChanged(ChangeEvent<string> evt)
{
if (evt.newValue.Contains(' '))
{
_newNameField.SetValueWithoutNotify(evt.newValue.Replace(" ", ""));
}
}
private void TrySubmit()
{
var oldName = _oldNameField.value;
var newName = _newNameField.value;
if (string.IsNullOrEmpty(newName))
{
if (!EditorUtility.DisplayDialog("Warning",
"The new name is empty, do you want to continue?",
"Yes", "No"))
{
return;
}
}
Close();
_onSubmit?.Invoke(oldName, newName);
}
}
#endregion
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment