TimelineRecorder.cs

October 8, 2023 ยท View on GitHub

using System.Collections; using System.IO; using System.Linq; using UnityEditor.Animations; using UnityEngine; using UnityEngine.Playables; using UnityEngine.Timeline; #if UNITY_EDITOR using UnityEditor; #endif

public class TimelineRecorder : MonoBehaviour { [SerializeField] public PlayableDirector director; [SerializeField] public GameObject rootBone; [HideInInspector] public GameObjectRecorder m_Recorder;

// 0.05f : low compression, slow, high quality // 0.1f : high compression, fast, low quality [SerializeField] public float positionError = 0.05f; [SerializeField] public float rotationError = 0.05f; }

#if UNITY_EDITOR [CustomEditor(typeof(TimelineRecorder))] [CanEditMultipleObjects] public class TimelineRecorderCustomEditor : Editor { static TimelineRecorder Instance;

public override void OnInspectorGUI() { base.OnInspectorGUI(); Instance = target as TimelineRecorder;

EditorGUILayout.HelpBox("SHOW THE GAME WINDOW IN THE FOREGROUND", MessageType.Warning, true);

EditorGUILayout.Space(10);
if (GUILayout.Button("\nRecording\n")) {
  Instance.StartCoroutine(RecordingTimeline());
}

}

private IEnumerator RecordingTimeline() { var timelineAsset = Instance.director.playableAsset as TimelineAsset; var clipFrameCount = Mathf.RoundToInt((float) Instance.director.duration * (float) timelineAsset.editorSettings.frameRate);

int frame = 0;
Instance.director.time = 0;

yield return new WaitForSeconds(1);
Instance.director.Play();
Instance.director.playableGraph.GetRootPlayable(0).SetSpeed(0);

AnimationClip clip = new AnimationClip();
clip.name = Path.GetFileNameWithoutExtension(AssetDatabase.GetAssetPath(timelineAsset));
clip.frameRate = 60;

Instance.m_Recorder = new GameObjectRecorder(Instance.rootBone);
Instance.m_Recorder.BindComponentsOfType<Transform>(Instance.rootBone, true);

while (frame < clipFrameCount) {
  Instance.director.time = frame / timelineAsset.editorSettings.frameRate;
  Instance.director.playableGraph.GetRootPlayable(0).SetSpeed(0);
  yield return new WaitForEndOfFrame();
  Instance.m_Recorder.TakeSnapshot(0.01666666666666667f);
  Debug.Log(frame + ": done");
  frame++;
}

Debug.Log("finished");

var options = new CurveFilterOptions();
options.keyframeReduction = true;
options.scaleError = 0.05f;
options.positionError = Instance.positionError;
options.rotationError = Instance.rotationError;

Instance.m_Recorder.SaveToClip(clip, 60, options);

var assetPath = $"{System.IO.Path.GetDirectoryName(AssetDatabase.GetAssetPath(timelineAsset))}/{clip.name}.anim";
if (File.Exists(assetPath)) {
  AssetDatabase.DeleteAsset(assetPath);
}

AssetDatabase.CreateAsset(clip, assetPath);
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();

} } #endif