Last active
May 7, 2024 04:58
-
-
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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