188 lines
		
	
	
		
			5.9 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
		
		
			
		
	
	
			188 lines
		
	
	
		
			5.9 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
|  | //#define ANALYTICS_DEBUG | ||
|  | using System; | ||
|  | using System.Collections.Generic; | ||
|  | using System.Diagnostics; | ||
|  | using System.Linq; | ||
|  | using System.Text; | ||
|  | using UnityEngine.Analytics; | ||
|  | using UnityEngine.Timeline; | ||
|  | 
 | ||
|  | namespace UnityEditor.Timeline | ||
|  | { | ||
|  |     class TimelineWindowAnalytics | ||
|  |     { | ||
|  |         const string vendorKey = "unity.timeline"; | ||
|  |         const string eventName = "timeline_editor_info"; | ||
|  |         const int version = 2; | ||
|  |         const int maxEventsPerHour = 1000; | ||
|  |         const int maxNumberOfElements = 1000; | ||
|  | 
 | ||
|  |         [Serializable] | ||
|  |         internal struct timeline_asset_stats | ||
|  | #if UNITY_2023_2_OR_NEWER | ||
|  |             : IAnalytic.IData | ||
|  | #endif | ||
|  |         { | ||
|  |             public string asset_guid; | ||
|  |             public double duration; | ||
|  |             public double frame_rate; | ||
|  |             public List<track_asset_stats> track_stats; | ||
|  |             public double mix_samples_count, ripple_samples_count, replace_samples_count; | ||
|  |             public string display_format; | ||
|  |         } | ||
|  | 
 | ||
|  |         [Serializable] | ||
|  |         internal struct track_asset_stats | ||
|  |         { | ||
|  |             public string track_type; | ||
|  |             public int clip_count; | ||
|  |             public int marker_count; | ||
|  |         } | ||
|  | 
 | ||
|  |         class WindowAnalyticsStats | ||
|  |         { | ||
|  |             internal int[] editModeSamples = new int[3]; // EditModes | ||
|  |         } | ||
|  | 
 | ||
|  | #if UNITY_2023_2_OR_NEWER | ||
|  |         [AnalyticInfo( | ||
|  |             eventName: eventName, | ||
|  |             vendorKey: vendorKey, | ||
|  |             version: version, | ||
|  |             maxEventsPerHour: maxEventsPerHour, | ||
|  |             maxNumberOfElements: maxNumberOfElements)] | ||
|  |         class TimelineWindowAnalyticsEvent : IAnalytic | ||
|  |         { | ||
|  |             public timeline_asset_stats timelineStats { get; } | ||
|  |             bool m_CanSendData; | ||
|  | 
 | ||
|  |             public TimelineWindowAnalyticsEvent() | ||
|  |             { | ||
|  |                 m_CanSendData = GenerateTimelineAssetStats(out timeline_asset_stats data); | ||
|  |                 timelineStats = data; | ||
|  |             } | ||
|  | 
 | ||
|  |             public void Send() | ||
|  |             { | ||
|  |                 if (m_CanSendData) | ||
|  |                     EditorAnalytics.SendAnalytic(this); | ||
|  |             } | ||
|  | 
 | ||
|  |             bool IAnalytic.TryGatherData(out IAnalytic.IData data, out Exception error) | ||
|  |             { | ||
|  |                 error = null; | ||
|  |                 data = timelineStats; | ||
|  |                 return true; | ||
|  |             } | ||
|  |         } | ||
|  | #endif | ||
|  | 
 | ||
|  |         static WindowAnalyticsStats analyticsStats = new WindowAnalyticsStats(); | ||
|  | 
 | ||
|  |         public void SendPlayEvent(bool start) | ||
|  |         { | ||
|  |             if (!start || !EditorAnalytics.enabled) | ||
|  |                 return; | ||
|  | 
 | ||
|  | #if UNITY_2023_2_OR_NEWER | ||
|  |             var analyticsEvent = new TimelineWindowAnalyticsEvent(); | ||
|  |             LogAnalyticsData(analyticsEvent.timelineStats); | ||
|  |             analyticsEvent.Send(); | ||
|  | #else | ||
|  |             EditorAnalytics.RegisterEventWithLimit(eventName, maxEventsPerHour, maxNumberOfElements, vendorKey, version); | ||
|  |             var ret = GenerateTimelineAssetStats(out var data); | ||
|  |             if (!ret) | ||
|  |                 return; | ||
|  |             LogAnalyticsData(data); | ||
|  |             EditorAnalytics.SendEventWithLimit(eventName, data, version); | ||
|  | #endif | ||
|  |             SendAfterSequenceChangeEvent(); | ||
|  |         } | ||
|  | 
 | ||
|  |         public void SendAfterSequenceChangeEvent() | ||
|  |         { | ||
|  |             analyticsStats = new WindowAnalyticsStats(); // Wipe Window Stats | ||
|  |         } | ||
|  | 
 | ||
|  |         public void SendManipulationEndedEvent() | ||
|  |         { | ||
|  |             analyticsStats.editModeSamples[(int)EditMode.editType]++; | ||
|  |         } | ||
|  | 
 | ||
|  |         internal static bool GenerateTimelineAssetStats(out timeline_asset_stats data) | ||
|  |         { | ||
|  |             var timeline = TimelineEditor.inspectedAsset; | ||
|  |             if (timeline == null || | ||
|  |                 !AssetDatabase.TryGetGUIDAndLocalFileIdentifier(timeline, out var guid, out long _)) | ||
|  |             { | ||
|  |                 data = new timeline_asset_stats(); | ||
|  |                 return false; | ||
|  |             } | ||
|  | 
 | ||
|  |             data = new timeline_asset_stats | ||
|  |             { | ||
|  |                 asset_guid = guid, | ||
|  |                 duration = timeline.duration, | ||
|  |                 frame_rate = timeline.editorSettings.frameRate, | ||
|  |                 track_stats = GetTrackAssetStats(timeline), | ||
|  |                 display_format = TimelinePreferences.instance.timeFormat.ConvertToString(), | ||
|  |                 mix_samples_count = analyticsStats.editModeSamples[(int)EditMode.EditType.Mix], | ||
|  |                 ripple_samples_count = analyticsStats.editModeSamples[(int)EditMode.EditType.Ripple], | ||
|  |                 replace_samples_count = analyticsStats.editModeSamples[(int)EditMode.EditType.Replace], | ||
|  |             }; | ||
|  | 
 | ||
|  |             return true; | ||
|  |         } | ||
|  | 
 | ||
|  |         static List<track_asset_stats> GetTrackAssetStats(TimelineAsset timeline) | ||
|  |         { | ||
|  |             var ret = new List<track_asset_stats>(); | ||
|  |             foreach (var track in timeline.flattenedTracks) | ||
|  |             { | ||
|  |                 ret.Add(new track_asset_stats | ||
|  |                 { | ||
|  |                     track_type = track.GetType().FullName, | ||
|  |                     clip_count = track.GetClips().Count(), | ||
|  |                     marker_count = track.GetMarkers().Count() | ||
|  |                 } | ||
|  |                 ); | ||
|  |             } | ||
|  |             return ret; | ||
|  |         } | ||
|  | 
 | ||
|  |         [Conditional("ANALYTICS_DEBUG")] | ||
|  |         static void LogAnalyticsData(timeline_asset_stats data) | ||
|  |         { | ||
|  |             UnityEngine.Debug.Log(UnityEngine.JsonUtility.ToJson(data, true)); | ||
|  |         } | ||
|  |     } | ||
|  | 
 | ||
|  |     static class ConversionUtilities | ||
|  |     { | ||
|  |         internal static string ConvertToString<T>(this T e) where T : Enum | ||
|  |         { | ||
|  |             return Enum.GetName(typeof(T), e).ToSnakeCase(); | ||
|  |         } | ||
|  | 
 | ||
|  |         static string ToSnakeCase(this string str) | ||
|  |         { | ||
|  |             var sb = new StringBuilder(); | ||
|  |             for (var i = 0; i < str.Length - 1; ++i) | ||
|  |             { | ||
|  |                 var ch = str[i]; | ||
|  |                 var nCh = str[i + 1]; | ||
|  |                 if (char.IsUpper(ch) && char.IsLower(nCh)) | ||
|  |                 { | ||
|  |                     sb.Append("_"); | ||
|  |                 } | ||
|  | 
 | ||
|  |                 sb.Append(ch.ToString().ToLower()); | ||
|  |             } | ||
|  | 
 | ||
|  |             sb.Append(str[str.Length - 1].ToString().ToLower()); | ||
|  | 
 | ||
|  |             return sb.ToString().TrimStart('_'); | ||
|  |         } | ||
|  |     } | ||
|  | } |