Aucune description
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

Analytics.cs 22KB

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