using System; using System.Collections.Generic; using System.Linq; using UnityEditor; using UnityEngine; using UnityEngine.Assertions; using UnityEngine.Serialization; using UnityEditor.Timeline; using UnityEngine.Playables; using UnityEngine.Timeline; public class CampToolBobo : EditorWindow { [MenuItem("Window/CampTool/Refactor Build Process")] public static void RefactorBuildProcess() { var selectedObjects = Selection.objects; if (selectedObjects.Length != 1) Debug.LogError($"Expect only one target, got {selectedObjects.Length}"); var targetGo = selectedObjects[0] as GameObject; if (targetGo is null) { Debug.LogError($"Target {selectedObjects[0].name} not a game object."); return; } foreach (Transform child in targetGo.transform) { RefactorStep(child); } } private static void RefactorStep(Transform step) { Debug.Log($"咱瞅瞅步骤{step.name}有没有重复的..."); bool hasRepeat = false; foreach (Transform miniStep in step) { if (miniStep.childCount > 0) continue; var goAnimRoot = new GameObject { name = "AnimRoot", transform = { parent = miniStep, } }; goAnimRoot.transform.SetLocalPositionAndRotation(Vector3.zero, Quaternion.identity); goAnimRoot.transform.localScale = Vector3.one; var newMiniStep = Instantiate(miniStep.gameObject, goAnimRoot.transform); newMiniStep.transform.SetLocalPositionAndRotation(Vector3.zero, Quaternion.identity); newMiniStep.transform.localScale = Vector3.one; newMiniStep.name = "obj"; DestroyImmediate(miniStep.GetComponent()); DestroyImmediate(miniStep.GetComponent()); DestroyImmediate(newMiniStep.transform.GetChild(0).gameObject); } var children = step.Cast().ToList(); children.Sort((Transform t1, Transform t2) => { var a = int.Parse(t1.name); var b = int.Parse(t2.name); if (a != b) return a.CompareTo(b); Debug.Log($" 旭哥我跟你讲啊,{a}有重复的啊!"); hasRepeat = true; return a.CompareTo(b); }); for (int i = 0; i < step.childCount; i++) { Undo.SetTransformParent(children[i], children[i].parent, "SortChildren"); children[i].SetSiblingIndex(i); } if (!hasRepeat) Debug.Log($"哦,好像没有。"); } [MenuItem("Window/CampTool/Rename Child of AnimRoot")] public static void RenameChildOfAnimRoot() { var selectedObjects = Selection.objects; foreach (var selectedObject in selectedObjects) { var go = selectedObject as GameObject; Assert.IsTrue(go); foreach (var stepTransform in go.transform.Cast()) { foreach (var miniStep in stepTransform.Cast()) { var animRoot = miniStep.GetChild(0); if (animRoot.name == "AnimRoot") { animRoot.GetChild(0).name = "obj"; } } } } } [MenuItem("Window/CampTool/Scale Animation Clips")] public static void OpenScaleAnimationClipsWindow() { GetWindow("Rescale Animation Clips"); } [SerializeField] private AnimationClip[] animationClips; private float _targetLength = 5f; private void OnGUI() { GUILayout.Label("Animation Clip Rescaler", EditorStyles.boldLabel); // Field for selecting animation clips var serializedObject = new SerializedObject(this); var clipsProperty = serializedObject.FindProperty("animationClips"); if (clipsProperty != null) EditorGUILayout.PropertyField(clipsProperty, true); else { Debug.Log("Bad..."); } serializedObject.ApplyModifiedProperties(); // Target duration field _targetLength = EditorGUILayout.FloatField("Target Length (seconds):", _targetLength); // Button to scale animation clips if (GUILayout.Button("Scale Animation Clips")) { ScaleAnimationClips(); } } private void ScaleAnimationClips() { if (animationClips == null || animationClips.Length == 0) { Debug.LogWarning("No animation clips selected."); return; } foreach (var clip in animationClips) { if (!clip) continue; // Get the original length of the clip float originalLength = clip.length; if (originalLength <= 0) { Debug.LogWarning($"Clip '{clip.name}' has no length to scale."); continue; } // Calculate the new speed to scale the animation float scaleFactor = originalLength / _targetLength; // Scale the animation clip speed by adjusting its frame rates ScaleClip(clip, scaleFactor); EditorUtility.SetDirty(clip); // Mark as modified } // AssetDatabase.SaveAssets(); // Save the modified asset // AssetDatabase.Refresh(); Debug.Log("Animation clips scaled successfully."); } private void ScaleClip(AnimationClip clip, float scaleFactor) { // Debug.Log($"{scaleFactor} for {clip}"); // Scale all curves in the clip foreach (var binding in AnimationUtility.GetCurveBindings(clip)) { var curve = AnimationUtility.GetEditorCurve(clip, binding); var curveKeyFrames = curve.keys; // Adjust keyframe times for (int i = 0; i < curveKeyFrames.Length; i++) { // Debug.Log($" before: {curveKeyFrames[i].time}"); // var t = curveKeyFrames[i].time / scaleFactor; // curve.keys[i].time = t; curveKeyFrames[i].time /= scaleFactor; // Debug.Log($" after: {curveKeyFrames[i].time}"); // Debug.Log($" after t: {t}"); } curve.keys = curveKeyFrames; AnimationUtility.SetEditorCurve(clip, binding, curve); } foreach (var binding in AnimationUtility.GetObjectReferenceCurveBindings(clip)) { var curve = AnimationUtility.GetEditorCurve(clip, binding); for (int i = 0; i < curve.keys.Length; i++) { curve.keys[i].time /= scaleFactor; } AnimationUtility.SetEditorCurve(clip, binding, curve); } // Scale events foreach (var animEvent in AnimationUtility.GetAnimationEvents(clip)) { animEvent.time /= scaleFactor; } // Apply changes back to the clip AnimationUtility.SetAnimationEvents(clip, AnimationUtility.GetAnimationEvents(clip)); Debug.Log($"Scaled clip '{clip.name}' to {clip.length / scaleFactor} seconds."); } #region Duplicate Timeline [MenuItem("Window/CampTool/CreateTimelineSteps")] public static void ShowDuplicateTimelineWindow() { GetWindow("复制Timeline轨道"); } #endregion } public class DuplicateTimeline : EditorWindow { private PlayableDirector _playableDirector; private Animator _animator; private void OnGUI() { GUILayout.Label("复制Timeline轨道", EditorStyles.boldLabel); _playableDirector = (PlayableDirector)EditorGUILayout.ObjectField("PlayableDirector", _playableDirector, typeof(PlayableDirector), true); _animator = (Animator)EditorGUILayout.ObjectField("Dynamic", _animator, typeof(Animator), true); if (!GUILayout.Button("走你")) return; try { var asset = _playableDirector?.playableAsset as TimelineAsset; DuplicateTracks(asset, _playableDirector, _animator); } catch (Exception e) { Debug.Log($"{e.Message}"); } } private static void DuplicateTracks(TimelineAsset timeline, PlayableDirector director, Animator animator) { Undo.RecordObject(timeline, "Duplicate Timeline Track"); var originTrack = timeline.GetOutputTracks().ToList()[0]; var clips = originTrack.GetClips().ToList(); foreach (var clip in clips) { var newTrack = timeline.CreateTrack(originTrack.GetType(), null, originTrack.name + ": " + clip.displayName); director.SetGenericBinding(newTrack, animator); Debug.Log(clip.TryMoveToTrack(newTrack) ? $"{clip.displayName} 完成。" : $"{clip.displayName} 失败。"); } timeline.DeleteTrack(originTrack); AssetDatabase.SaveAssets(); EditorUtility.SetDirty(timeline); Debug.Log("处理完成!"); } }