A game about forced loneliness, made by TACStudios
1using System;
2using System.Collections.Generic;
3using System.Linq;
4using UnityEditor.Build;
5using UnityEditor.Build.Reporting;
6using UnityEngine.Playables;
7using UnityEngine.SceneManagement;
8using UnityEngine.Timeline;
9
10namespace UnityEditor.Timeline.Analytics
11{
12 class TimelineSceneInfo
13 {
14 public Dictionary<string, int> trackCount = new Dictionary<string, int>
15 {
16 {"ActivationTrack", 0},
17 {"AnimationTrack", 0},
18 {"AudioTrack", 0},
19 {"ControlTrack", 0},
20 {"PlayableTrack", 0},
21 {"UserType", 0},
22 {"Other", 0}
23 };
24
25 public Dictionary<string, int> userTrackTypesCount = new Dictionary<string, int>();
26 public HashSet<TimelineAsset> uniqueDirectors = new HashSet<TimelineAsset>();
27 public int numTracks = 0;
28 public int minDuration = int.MaxValue;
29 public int maxDuration = int.MinValue;
30 public int minNumTracks = int.MaxValue;
31 public int maxNumTracks = int.MinValue;
32 public int numRecorded = 0;
33 }
34
35 [Serializable]
36 struct TrackInfo
37 {
38 public string name;
39 public double percent;
40 }
41
42 [Serializable]
43 class TimelineEventInfo
44 {
45 public int num_timelines;
46 public int min_duration, max_duration;
47 public int min_num_tracks, max_num_tracks;
48 public double recorded_percent;
49 public List<TrackInfo> track_info = new List<TrackInfo>();
50 public string most_popular_user_track = string.Empty;
51
52 public TimelineEventInfo(TimelineSceneInfo sceneInfo)
53 {
54 num_timelines = sceneInfo.uniqueDirectors.Count;
55 min_duration = sceneInfo.minDuration;
56 max_duration = sceneInfo.maxDuration;
57 min_num_tracks = sceneInfo.minNumTracks;
58 max_num_tracks = sceneInfo.maxNumTracks;
59 recorded_percent = Math.Round(100.0 * sceneInfo.numRecorded / sceneInfo.numTracks, 1);
60
61 foreach (KeyValuePair<string, int> kv in sceneInfo.trackCount.Where(x => x.Value > 0))
62 {
63 track_info.Add(new TrackInfo()
64 {
65 name = kv.Key,
66 percent = Math.Round(100.0 * kv.Value / sceneInfo.numTracks, 1)
67 });
68 }
69
70 if (sceneInfo.userTrackTypesCount.Any())
71 {
72 most_popular_user_track = sceneInfo.userTrackTypesCount
73 .First(x => x.Value == sceneInfo.userTrackTypesCount.Values.Max()).Key;
74 }
75 }
76
77 public static bool IsUserType(Type t)
78 {
79 string nameSpace = t.Namespace;
80 return string.IsNullOrEmpty(nameSpace) || !nameSpace.StartsWith("UnityEngine.Timeline");
81 }
82 }
83
84
85 static class TimelineAnalytics
86 {
87 static TimelineSceneInfo _timelineSceneInfo = new TimelineSceneInfo();
88
89 class TimelineAnalyticsPreProcess : IPreprocessBuildWithReport
90 {
91 public int callbackOrder { get { return 0; } }
92 public void OnPreprocessBuild(BuildReport report)
93 {
94 _timelineSceneInfo = new TimelineSceneInfo();
95 }
96 }
97
98 class TimelineAnalyticsProcess : IProcessSceneWithReport
99 {
100 public int callbackOrder
101 {
102 get { return 0; }
103 }
104
105 public void OnProcessScene(Scene scene, BuildReport report)
106 {
107 PlayableDirector[] directorsInScene = GetAllDirectorsInScene();
108 IEnumerable<TimelineAsset> timelines = directorsInScene.Select(pd => pd.playableAsset).OfType<TimelineAsset>().Distinct();
109
110 foreach (var timeline in timelines)
111 {
112 if (_timelineSceneInfo.uniqueDirectors.Add(timeline))
113 {
114 _timelineSceneInfo.numTracks += timeline.flattenedTracks.Count();
115 _timelineSceneInfo.minDuration = Math.Min(_timelineSceneInfo.minDuration, (int)(timeline.duration * 1000));
116 _timelineSceneInfo.maxDuration = Math.Max(_timelineSceneInfo.maxDuration, (int)(timeline.duration * 1000));
117 _timelineSceneInfo.minNumTracks = Math.Min(_timelineSceneInfo.minNumTracks, timeline.flattenedTracks.Count());
118 _timelineSceneInfo.maxNumTracks = Math.Max(_timelineSceneInfo.maxNumTracks, timeline.flattenedTracks.Count());
119
120 foreach (var track in timeline.flattenedTracks)
121 {
122 string key = track.GetType().Name;
123 if (_timelineSceneInfo.trackCount.ContainsKey(key))
124 {
125 _timelineSceneInfo.trackCount[key]++;
126 }
127 else
128 {
129 if (TimelineEventInfo.IsUserType(track.GetType()))
130 {
131 _timelineSceneInfo.trackCount["UserType"]++;
132 if (_timelineSceneInfo.userTrackTypesCount.ContainsKey(key))
133 _timelineSceneInfo.userTrackTypesCount[key]++;
134 else
135 _timelineSceneInfo.userTrackTypesCount[key] = 1;
136 }
137 else
138 _timelineSceneInfo.trackCount["Other"]++;
139 }
140
141 if (track.clips.Any(x => x.recordable))
142 _timelineSceneInfo.numRecorded++;
143 else
144 {
145 var animationTrack = track as AnimationTrack;
146 if (animationTrack != null)
147 {
148 if (animationTrack.CanConvertToClipMode())
149 _timelineSceneInfo.numRecorded++;
150 }
151 }
152 }
153 }
154 }
155 }
156 }
157
158 static PlayableDirector[] GetAllDirectorsInScene()
159 {
160#if UNITY_2023_1_OR_NEWER
161 return UnityEngine.Object.FindObjectsByType<PlayableDirector>(UnityEngine.FindObjectsSortMode.None);
162#else
163 return UnityEngine.Object.FindObjectsOfType<PlayableDirector>();
164#endif
165 }
166
167 class TimelineAnalyticsPostProcess : IPostprocessBuildWithReport
168 {
169 public int callbackOrder { get { return 0; } }
170 public void OnPostprocessBuild(BuildReport report)
171 {
172 if (_timelineSceneInfo.uniqueDirectors.Count > 0)
173 {
174 var timelineEvent = new TimelineEventInfo(_timelineSceneInfo);
175 EditorAnalytics.SendEventTimelineInfo(timelineEvent);
176 }
177 }
178 }
179 }
180}