Analytics.cs 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605
  1. #define WRITE_TO_JSON
  2. using System;
  3. using System.Collections.Generic;
  4. using System.IO;
  5. using System.Linq;
  6. using UnityEngine.Analytics;
  7. using UnityEngine;
  8. namespace UnityEditor.U2D.Animation
  9. {
  10. [Serializable]
  11. enum AnimationToolType
  12. {
  13. UnknownTool = 0,
  14. Visibilility = 6,
  15. PreviewPose = 7,
  16. EditPose = 8,
  17. CreateBone = 9,
  18. SplitBone = 10,
  19. ReparentBone = 11,
  20. EditGeometry = 12,
  21. CreateVertex = 13,
  22. CreateEdge = 14,
  23. SplitEdge = 15,
  24. GenerateGeometry = 16,
  25. WeightSlider = 17,
  26. WeightBrush = 18,
  27. BoneInfluence = 19,
  28. GenerateWeights = 20
  29. }
  30. [Serializable]
  31. enum AnimationEventType
  32. {
  33. Truncated = -1,
  34. SelectedSpriteChanged = 0,
  35. SkeletonPreviewPoseChanged = 1,
  36. SkeletonBindPoseChanged = 2,
  37. SkeletonTopologyChanged = 3,
  38. MeshChanged = 4,
  39. MeshPreviewChanged = 5,
  40. SkinningModuleModeChanged = 6,
  41. BoneSelectionChanged = 7,
  42. BoneNameChanged = 8,
  43. CharacterPartChanged = 9,
  44. ToolChanged = 10,
  45. RestoreBindPose = 11,
  46. Copy = 12,
  47. Paste = 13,
  48. BoneDepthChanged = 14,
  49. Shortcut = 15,
  50. Visibility = 16
  51. }
  52. [Serializable]
  53. struct AnimationEvent
  54. {
  55. [SerializeField]
  56. public AnimationEventType sub_type;
  57. [SerializeField]
  58. public int repeated_event;
  59. [SerializeField]
  60. public string data;
  61. }
  62. [Serializable]
  63. struct AnimationToolUsageEvent
  64. {
  65. [SerializeField]
  66. public int instance_id;
  67. [SerializeField]
  68. public AnimationToolType animation_tool;
  69. [SerializeField]
  70. public bool character_mode;
  71. [SerializeField]
  72. public int time_start_s;
  73. [SerializeField]
  74. public int time_end_s;
  75. [SerializeField]
  76. public List<AnimationEvent> animation_events;
  77. }
  78. [Serializable]
  79. struct AnimationToolApplyEvent
  80. {
  81. [SerializeField]
  82. public bool character_mode;
  83. [SerializeField]
  84. public int instance_id;
  85. [SerializeField]
  86. public int sprite_count;
  87. [SerializeField]
  88. public int[] bone_sprite_count;
  89. [SerializeField]
  90. public int[] bone_count;
  91. [SerializeField]
  92. public int[] bone_depth;
  93. [SerializeField]
  94. public int[] bone_chain_count;
  95. [SerializeField]
  96. public int bone_root_count;
  97. }
  98. internal interface IAnimationAnalyticsModel
  99. {
  100. bool hasCharacter { get; }
  101. SkinningMode mode { get; }
  102. ITool selectedTool { get; }
  103. ITool GetTool(Tools tool);
  104. int selectedBoneCount { get; }
  105. int applicationElapseTime { get; }
  106. }
  107. internal class SkinningModuleAnalyticsModel : IAnimationAnalyticsModel
  108. {
  109. public SkinningCache skinningCache { get; private set; }
  110. public bool hasCharacter { get { return skinningCache.hasCharacter; } }
  111. public SkinningMode mode { get { return skinningCache.mode; } }
  112. public ITool selectedTool { get { return skinningCache.selectedTool; } }
  113. public SkinningModuleAnalyticsModel(SkinningCache s)
  114. {
  115. skinningCache = s;
  116. }
  117. public ITool GetTool(Tools tool)
  118. {
  119. return skinningCache.GetTool(tool);
  120. }
  121. public int selectedBoneCount { get { return skinningCache.skeletonSelection.Count; } }
  122. public int applicationElapseTime { get { return (int)EditorApplication.timeSinceStartup; } }
  123. }
  124. [Serializable]
  125. internal class AnimationAnalytics
  126. {
  127. const int k_AnimationEventElementCount = 3;
  128. const int k_AnimationToolUsageEventElementCount = 6;
  129. IAnalyticsStorage m_AnalyticsStorage;
  130. SkinningEvents m_EventBus;
  131. IAnimationAnalyticsModel m_Model;
  132. AnimationToolUsageEvent? m_CurrentEvent;
  133. int m_InstanceId;
  134. public AnimationAnalytics(IAnalyticsStorage analyticsStorage, SkinningEvents eventBus, IAnimationAnalyticsModel model, int instanceId)
  135. {
  136. m_Model = model;
  137. m_AnalyticsStorage = analyticsStorage;
  138. m_InstanceId = instanceId;
  139. m_EventBus = eventBus;
  140. m_EventBus.selectedSpriteChanged.AddListener(OnSelectedSpriteChanged);
  141. m_EventBus.skeletonPreviewPoseChanged.AddListener(OnSkeletonPreviewPoseChanged);
  142. m_EventBus.skeletonBindPoseChanged.AddListener(OnSkeletonBindPoseChanged);
  143. m_EventBus.skeletonTopologyChanged.AddListener(OnSkeletonTopologyChanged);
  144. m_EventBus.meshChanged.AddListener(OnMeshChanged);
  145. m_EventBus.meshPreviewChanged.AddListener(OnMeshPreviewChanged);
  146. m_EventBus.skinningModeChanged.AddListener(OnSkinningModuleModeChanged);
  147. m_EventBus.boneSelectionChanged.AddListener(OnBoneSelectionChanged);
  148. m_EventBus.boneNameChanged.AddListener(OnBoneNameChanged);
  149. m_EventBus.boneDepthChanged.AddListener(OnBoneDepthChanged);
  150. m_EventBus.characterPartChanged.AddListener(OnCharacterPartChanged);
  151. m_EventBus.toolChanged.AddListener(OnToolChanged);
  152. m_EventBus.restoreBindPose.AddListener(OnRestoreBindPose);
  153. m_EventBus.copy.AddListener(OnCopy);
  154. m_EventBus.paste.AddListener(OnPaste);
  155. m_EventBus.shortcut.AddListener(OnShortcut);
  156. m_EventBus.boneVisibility.AddListener(OnBoneVisibility);
  157. OnToolChanged(model.selectedTool);
  158. }
  159. public void Dispose()
  160. {
  161. m_EventBus.selectedSpriteChanged.RemoveListener(OnSelectedSpriteChanged);
  162. m_EventBus.skeletonPreviewPoseChanged.RemoveListener(OnSkeletonPreviewPoseChanged);
  163. m_EventBus.skeletonBindPoseChanged.RemoveListener(OnSkeletonBindPoseChanged);
  164. m_EventBus.skeletonTopologyChanged.RemoveListener(OnSkeletonTopologyChanged);
  165. m_EventBus.meshChanged.RemoveListener(OnMeshChanged);
  166. m_EventBus.meshPreviewChanged.RemoveListener(OnMeshPreviewChanged);
  167. m_EventBus.skinningModeChanged.RemoveListener(OnSkinningModuleModeChanged);
  168. m_EventBus.boneSelectionChanged.RemoveListener(OnBoneSelectionChanged);
  169. m_EventBus.boneNameChanged.RemoveListener(OnBoneNameChanged);
  170. m_EventBus.boneDepthChanged.AddListener(OnBoneDepthChanged);
  171. m_EventBus.characterPartChanged.RemoveListener(OnCharacterPartChanged);
  172. m_EventBus.toolChanged.RemoveListener(OnToolChanged);
  173. m_EventBus.copy.RemoveListener(OnCopy);
  174. m_EventBus.paste.RemoveListener(OnPaste);
  175. m_EventBus.shortcut.RemoveListener(OnShortcut);
  176. m_EventBus.boneVisibility.RemoveListener(OnBoneVisibility);
  177. m_AnalyticsStorage.Dispose();
  178. }
  179. void OnBoneVisibility(string s)
  180. {
  181. SetAnimationEvent(new AnimationEvent()
  182. {
  183. sub_type = AnimationEventType.Visibility,
  184. data = s
  185. });
  186. }
  187. void OnShortcut(string s)
  188. {
  189. SetAnimationEvent(new AnimationEvent()
  190. {
  191. sub_type = AnimationEventType.Shortcut,
  192. data = s
  193. });
  194. }
  195. void OnCopy()
  196. {
  197. SetAnimationEvent(new AnimationEvent()
  198. {
  199. sub_type = AnimationEventType.Copy,
  200. data = ""
  201. });
  202. }
  203. void OnPaste(bool bone , bool mesh , bool flipX , bool flipY)
  204. {
  205. SetAnimationEvent(new AnimationEvent()
  206. {
  207. sub_type = AnimationEventType.Paste,
  208. data = string.Format("b:{0} m:{1} x:{2} y:{3}", bone, mesh, flipX, flipY)
  209. });
  210. }
  211. void OnSelectedSpriteChanged(SpriteCache sprite)
  212. {
  213. SetAnimationEvent(new AnimationEvent()
  214. {
  215. sub_type = AnimationEventType.SelectedSpriteChanged,
  216. data = sprite == null ? "false" : "true"
  217. });
  218. }
  219. void OnSkeletonPreviewPoseChanged(SkeletonCache skeleton)
  220. {
  221. SetAnimationEvent(new AnimationEvent()
  222. {
  223. sub_type = AnimationEventType.SkeletonPreviewPoseChanged,
  224. data = ""
  225. });
  226. }
  227. void OnSkeletonBindPoseChanged(SkeletonCache skeleton)
  228. {
  229. SetAnimationEvent(new AnimationEvent()
  230. {
  231. sub_type = AnimationEventType.SkeletonBindPoseChanged,
  232. data = ""
  233. });
  234. }
  235. void OnSkeletonTopologyChanged(SkeletonCache skeleton)
  236. {
  237. SetAnimationEvent(new AnimationEvent()
  238. {
  239. sub_type = AnimationEventType.SkeletonTopologyChanged,
  240. data = ""
  241. });
  242. }
  243. void OnMeshChanged(MeshCache mesh)
  244. {
  245. SetAnimationEvent(new AnimationEvent()
  246. {
  247. sub_type = AnimationEventType.MeshChanged,
  248. data = ""
  249. });
  250. }
  251. void OnMeshPreviewChanged(MeshPreviewCache mesh)
  252. {
  253. }
  254. void OnSkinningModuleModeChanged(SkinningMode mode)
  255. {
  256. SetAnimationEvent(new AnimationEvent()
  257. {
  258. sub_type = AnimationEventType.SkinningModuleModeChanged,
  259. data = mode.ToString()
  260. });
  261. }
  262. void OnBoneSelectionChanged()
  263. {
  264. SetAnimationEvent(new AnimationEvent()
  265. {
  266. sub_type = AnimationEventType.BoneSelectionChanged,
  267. data = m_Model.selectedBoneCount.ToString()
  268. });
  269. }
  270. void OnBoneNameChanged(BoneCache bone)
  271. {
  272. SetAnimationEvent(new AnimationEvent()
  273. {
  274. sub_type = AnimationEventType.BoneNameChanged,
  275. data = ""
  276. });
  277. }
  278. void OnBoneDepthChanged(BoneCache bone)
  279. {
  280. SetAnimationEvent(new AnimationEvent()
  281. {
  282. sub_type = AnimationEventType.BoneDepthChanged,
  283. data = ""
  284. });
  285. }
  286. void OnCharacterPartChanged(CharacterPartCache part)
  287. {
  288. SetAnimationEvent(new AnimationEvent()
  289. {
  290. sub_type = AnimationEventType.CharacterPartChanged,
  291. data = ""
  292. });
  293. }
  294. void OnToolChanged(ITool tool)
  295. {
  296. if (tool == m_Model.GetTool(Tools.ReparentBone))
  297. StartNewEvent(AnimationToolType.ReparentBone, m_Model.applicationElapseTime);
  298. else if (tool == m_Model.GetTool(Tools.CreateBone))
  299. StartNewEvent(AnimationToolType.CreateBone, m_Model.applicationElapseTime);
  300. else if (tool == m_Model.GetTool(Tools.EditJoints))
  301. StartNewEvent(AnimationToolType.EditPose, m_Model.applicationElapseTime);
  302. else if (tool == m_Model.GetTool(Tools.EditPose))
  303. StartNewEvent(AnimationToolType.PreviewPose, m_Model.applicationElapseTime);
  304. else if (tool == m_Model.GetTool(Tools.SplitBone))
  305. StartNewEvent(AnimationToolType.SplitBone, m_Model.applicationElapseTime);
  306. else if (tool == m_Model.GetTool(Tools.CreateEdge))
  307. StartNewEvent(AnimationToolType.CreateEdge, m_Model.applicationElapseTime);
  308. else if (tool == m_Model.GetTool(Tools.CreateVertex))
  309. StartNewEvent(AnimationToolType.CreateVertex, m_Model.applicationElapseTime);
  310. else if (tool == m_Model.GetTool(Tools.EditGeometry))
  311. StartNewEvent(AnimationToolType.EditGeometry, m_Model.applicationElapseTime);
  312. else if (tool == m_Model.GetTool(Tools.GenerateGeometry))
  313. StartNewEvent(AnimationToolType.GenerateGeometry, m_Model.applicationElapseTime);
  314. else if (tool == m_Model.GetTool(Tools.SplitEdge))
  315. StartNewEvent(AnimationToolType.SplitEdge, m_Model.applicationElapseTime);
  316. else if (tool == m_Model.GetTool(Tools.Visibility))
  317. StartNewEvent(AnimationToolType.Visibilility, m_Model.applicationElapseTime);
  318. else if (tool == m_Model.GetTool(Tools.BoneInfluence))
  319. StartNewEvent(AnimationToolType.BoneInfluence, m_Model.applicationElapseTime);
  320. else if (tool == m_Model.GetTool(Tools.GenerateWeights))
  321. StartNewEvent(AnimationToolType.GenerateWeights, m_Model.applicationElapseTime);
  322. else if (tool == m_Model.GetTool(Tools.WeightBrush))
  323. StartNewEvent(AnimationToolType.WeightBrush, m_Model.applicationElapseTime);
  324. else if (tool == m_Model.GetTool(Tools.WeightSlider))
  325. StartNewEvent(AnimationToolType.WeightSlider, m_Model.applicationElapseTime);
  326. else
  327. StartNewEvent(AnimationToolType.UnknownTool, m_Model.applicationElapseTime);
  328. }
  329. void OnRestoreBindPose()
  330. {
  331. SetAnimationEvent(new AnimationEvent()
  332. {
  333. sub_type = AnimationEventType.RestoreBindPose,
  334. data = ""
  335. });
  336. }
  337. void SetAnimationEvent(AnimationEvent evt)
  338. {
  339. if (m_CurrentEvent != null)
  340. {
  341. var toolEvent = m_CurrentEvent.Value;
  342. var eventCount = toolEvent.animation_events.Count;
  343. if (eventCount > 0 && toolEvent.animation_events[eventCount - 1].sub_type == evt.sub_type && toolEvent.animation_events[eventCount - 1].data == evt.data)
  344. {
  345. var e = toolEvent.animation_events[eventCount - 1];
  346. e.repeated_event += 1;
  347. toolEvent.animation_events[eventCount - 1] = e;
  348. }
  349. else
  350. {
  351. var elementCountPlus = k_AnimationToolUsageEventElementCount + (eventCount + 1 * k_AnimationEventElementCount);
  352. if (elementCountPlus >= AnalyticConstant.k_MaxNumberOfElements)
  353. {
  354. // We reached the max number of events. Change the last one to truncated
  355. var e = toolEvent.animation_events[eventCount - 1];
  356. if (e.sub_type != AnimationEventType.Truncated)
  357. {
  358. e.sub_type = AnimationEventType.Truncated;
  359. e.repeated_event = 0;
  360. }
  361. e.repeated_event += 1;
  362. toolEvent.animation_events[eventCount - 1] = e;
  363. }
  364. else
  365. toolEvent.animation_events.Add(evt);
  366. }
  367. m_CurrentEvent = toolEvent;
  368. }
  369. }
  370. void StartNewEvent(AnimationToolType animationType, int tick)
  371. {
  372. SendLastEvent(tick);
  373. m_CurrentEvent = new AnimationToolUsageEvent()
  374. {
  375. instance_id = m_InstanceId,
  376. character_mode = m_Model.mode == SkinningMode.Character,
  377. animation_tool = animationType,
  378. time_start_s = tick,
  379. animation_events = new List<AnimationEvent>()
  380. };
  381. }
  382. void SendLastEvent(AnimationToolUsageEvent evt, int tick)
  383. {
  384. evt.time_end_s = tick;
  385. m_AnalyticsStorage.SendUsageEvent(evt);
  386. }
  387. void SendLastEvent(int tick)
  388. {
  389. if (m_CurrentEvent != null)
  390. {
  391. SendLastEvent(m_CurrentEvent.Value, tick);
  392. }
  393. m_CurrentEvent = null;
  394. }
  395. public void FlushEvent()
  396. {
  397. SendLastEvent(m_Model.applicationElapseTime);
  398. }
  399. public void SendApplyEvent(int spriteCount, int[] spriteBoneCount, BoneCache[] bones)
  400. {
  401. int[] chainBoneCount = null;
  402. int[] maxDepth = null;
  403. int[] boneCount = null;
  404. int boneRootCount = 0;
  405. GetChainBoneStatistic(bones, out chainBoneCount, out maxDepth, out boneRootCount, out boneCount);
  406. var applyEvent = new AnimationToolApplyEvent()
  407. {
  408. instance_id = m_InstanceId,
  409. character_mode = m_Model.hasCharacter,
  410. sprite_count = spriteCount,
  411. bone_sprite_count = spriteBoneCount,
  412. bone_depth = maxDepth,
  413. bone_chain_count = chainBoneCount,
  414. bone_root_count = boneRootCount,
  415. bone_count = boneCount
  416. };
  417. m_AnalyticsStorage.SendApplyEvent(applyEvent);
  418. }
  419. static void GetChainBoneStatistic(BoneCache[] bones, out int[] chainBoneCount, out int[] maxDepth, out int boneRootCount, out int[] boneCount)
  420. {
  421. List<int> chainCountList = new List<int>();
  422. List<int> boneDepthList = new List<int>();
  423. List<int> countList = new List<int>();
  424. boneRootCount = 0;
  425. foreach (var b in bones)
  426. {
  427. if (b.parentBone == null)
  428. {
  429. ++boneRootCount;
  430. var chain = 0;
  431. var chainDepth = 0;
  432. var tempBone = b;
  433. var count = 1;
  434. while (tempBone != null)
  435. {
  436. ++chainDepth;
  437. tempBone = tempBone.chainedChild;
  438. }
  439. foreach (var b1 in bones)
  440. {
  441. // if this bone is part of this root
  442. var parentBone = b1.parentBone;
  443. while (parentBone != null)
  444. {
  445. if (parentBone == b)
  446. {
  447. ++count;
  448. // the bone has a parent and the parent bone's chainedChild is not us, means we are a new chain
  449. if (b1.parentBone != null && b1.parentBone.chainedChild != b1)
  450. {
  451. ++chain;
  452. var chainDepth1 = 0;
  453. tempBone = b1;
  454. while (tempBone != null)
  455. {
  456. ++chainDepth1;
  457. tempBone = tempBone.chainedChild;
  458. }
  459. chainDepth = chainDepth1 > chainDepth ? chainDepth1 : chainDepth;
  460. }
  461. break;
  462. }
  463. parentBone = parentBone.parentBone;
  464. }
  465. }
  466. chainCountList.Add(chain);
  467. boneDepthList.Add(chainDepth);
  468. countList.Add(count);
  469. }
  470. }
  471. chainBoneCount = chainCountList.ToArray();
  472. maxDepth = boneDepthList.ToArray();
  473. boneCount = countList.ToArray();
  474. }
  475. }
  476. internal interface IAnalyticsStorage
  477. {
  478. AnalyticsResult SendUsageEvent(AnimationToolUsageEvent evt);
  479. AnalyticsResult SendApplyEvent(AnimationToolApplyEvent evt);
  480. void Dispose();
  481. }
  482. internal static class AnalyticConstant
  483. {
  484. public const int k_MaxEventsPerHour = 1000;
  485. public const int k_MaxNumberOfElements = 1000;
  486. }
  487. internal class AnalyticsJsonStorage : IAnalyticsStorage
  488. {
  489. [Serializable]
  490. struct AnimationToolEvents
  491. {
  492. [SerializeField]
  493. public List<AnimationToolUsageEvent> events;
  494. [SerializeField]
  495. public AnimationToolApplyEvent applyEvent;
  496. }
  497. AnimationToolEvents m_TotalEvents = new AnimationToolEvents()
  498. {
  499. events = new List<AnimationToolUsageEvent>(),
  500. applyEvent = new AnimationToolApplyEvent()
  501. };
  502. public AnalyticsResult SendUsageEvent(AnimationToolUsageEvent evt)
  503. {
  504. m_TotalEvents.events.Add(evt);
  505. return AnalyticsResult.Ok;
  506. }
  507. public AnalyticsResult SendApplyEvent(AnimationToolApplyEvent evt)
  508. {
  509. m_TotalEvents.applyEvent = evt;
  510. return AnalyticsResult.Ok;
  511. }
  512. public void Dispose()
  513. {
  514. try
  515. {
  516. string file = string.Format("analytics_{0}.json", System.DateTime.Now.ToString("yyyy-dd-M--HH-mm-ss"));
  517. if (System.IO.File.Exists(file))
  518. System.IO.File.Delete(file);
  519. System.IO.File.WriteAllText(file, JsonUtility.ToJson(m_TotalEvents, true));
  520. }
  521. catch (Exception ex)
  522. {
  523. Debug.Log(ex);
  524. }
  525. finally
  526. {
  527. m_TotalEvents.events.Clear();
  528. }
  529. }
  530. }
  531. [InitializeOnLoad]
  532. internal class UnityAnalyticsStorage : IAnalyticsStorage
  533. {
  534. const string k_VendorKey = "unity.2d.animation";
  535. const int k_Version = 1;
  536. static UnityAnalyticsStorage()
  537. {
  538. EditorAnalytics.RegisterEventWithLimit("u2dAnimationToolUsage", AnalyticConstant.k_MaxEventsPerHour, AnalyticConstant.k_MaxNumberOfElements, k_VendorKey, k_Version);
  539. EditorAnalytics.RegisterEventWithLimit("u2dAnimationToolApply", AnalyticConstant.k_MaxEventsPerHour, AnalyticConstant.k_MaxNumberOfElements, k_VendorKey, k_Version);
  540. }
  541. public AnalyticsResult SendUsageEvent(AnimationToolUsageEvent evt)
  542. {
  543. return EditorAnalytics.SendEventWithLimit("u2dAnimationToolUsage", evt, k_Version);
  544. }
  545. public AnalyticsResult SendApplyEvent(AnimationToolApplyEvent evt)
  546. {
  547. return EditorAnalytics.SendEventWithLimit("u2dAnimationToolApply", evt, k_Version);
  548. }
  549. public void Dispose() {}
  550. }
  551. }