暂无描述
您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

SkeletonController.cs 22KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Text.RegularExpressions;
  4. using UnityEngine;
  5. namespace UnityEditor.U2D.Animation
  6. {
  7. [Serializable]
  8. internal class SkeletonController
  9. {
  10. static readonly string k_DefaultRootName = "root";
  11. static readonly string k_DefaultBoneName = "bone";
  12. static Regex s_Regex = new Regex(@"\w+_\d+$", RegexOptions.IgnoreCase);
  13. [SerializeField]
  14. Vector3 m_CreateBoneStartPosition;
  15. [SerializeField]
  16. BoneCache m_PrevCreatedBone;
  17. SkeletonCache m_Skeleton;
  18. bool m_Moved = false;
  19. ISkeletonStyle style
  20. {
  21. get
  22. {
  23. if (styleOverride != null)
  24. return styleOverride;
  25. return SkeletonStyles.Default;
  26. }
  27. }
  28. SkinningCache skinningCache => m_Skeleton.skinningCache;
  29. BoneCache selectedBone
  30. {
  31. get => selection.activeElement.ToSpriteSheetIfNeeded();
  32. set => selection.activeElement = value.ToCharacterIfNeeded();
  33. }
  34. BoneCache[] selectedBones
  35. {
  36. get => selection.elements.ToSpriteSheetIfNeeded();
  37. set => selection.elements = value.ToCharacterIfNeeded();
  38. }
  39. BoneCache rootBone => selection.root.ToSpriteSheetIfNeeded();
  40. BoneCache[] rootBones => selection.roots.ToSpriteSheetIfNeeded();
  41. public ISkeletonView view { get; set; }
  42. public ISkeletonStyle styleOverride { get; set; }
  43. public IBoneSelection selection { get; set; }
  44. public bool editBindPose { get; set; }
  45. public SkeletonCache skeleton
  46. {
  47. get => m_Skeleton;
  48. set => SetSkeleton(value);
  49. }
  50. public BoneCache hoveredBone => GetBone(view.hoveredBoneID);
  51. public BoneCache hoveredTail => GetBone(view.hoveredTailID);
  52. public BoneCache hoveredBody => GetBone(view.hoveredBodyID);
  53. public BoneCache hoveredJoint => GetBone(view.hoveredJointID);
  54. public BoneCache hotBone => GetBone(view.hotBoneID);
  55. BoneCache GetBone(int instanceID)
  56. {
  57. return BaseObject.InstanceIDToObject(instanceID) as BoneCache;
  58. }
  59. void SetSkeleton(SkeletonCache newSkeleton)
  60. {
  61. if (skeleton != newSkeleton)
  62. {
  63. m_Skeleton = newSkeleton;
  64. Reset();
  65. }
  66. }
  67. public void Reset()
  68. {
  69. view.DoCancelMultistepAction(true);
  70. }
  71. public void OnGUI()
  72. {
  73. if (skeleton == null)
  74. return;
  75. view.BeginLayout();
  76. if (view.CanLayout())
  77. LayoutBones();
  78. view.EndLayout();
  79. HandleSelectBone();
  80. HandleRotateBone();
  81. HandleMoveBone();
  82. HandleFreeMoveBone();
  83. HandleMoveJoint();
  84. HandleMoveEndPosition();
  85. HandleChangeLength();
  86. HandleCreateBone();
  87. HandleSplitBone();
  88. HandleRemoveBone();
  89. HandleCancelMultiStepAction();
  90. DrawSkeleton();
  91. DrawSplitBonePreview();
  92. DrawCreateBonePreview();
  93. DrawCursors();
  94. BatchedDrawing.Draw();
  95. }
  96. void LayoutBones()
  97. {
  98. for (var i = 0; i < skeleton.boneCount; ++i)
  99. {
  100. var bone = skeleton.GetBone(i);
  101. if (bone.isVisible && bone != hotBone)
  102. view.LayoutBone(bone.GetInstanceID(), bone.position, bone.endPosition, bone.forward, bone.up, bone.right, bone.chainedChild == null);
  103. }
  104. }
  105. void HandleSelectBone()
  106. {
  107. if (view.DoSelectBone(out var instanceID, out var additive))
  108. {
  109. var bone = GetBone(instanceID).ToCharacterIfNeeded();
  110. using (skinningCache.UndoScope(TextContent.boneSelection, true))
  111. {
  112. if (!additive)
  113. {
  114. if (!selection.Contains(bone))
  115. selectedBone = bone;
  116. }
  117. else
  118. selection.Select(bone, !selection.Contains(bone));
  119. skinningCache.events.boneSelectionChanged.Invoke();
  120. }
  121. }
  122. }
  123. void HandleRotateBone()
  124. {
  125. if (view.IsActionTriggering(SkeletonAction.RotateBone))
  126. m_Moved = false;
  127. var pivot = hoveredBone;
  128. if (view.IsActionHot(SkeletonAction.RotateBone))
  129. pivot = hotBone;
  130. if (pivot == null)
  131. return;
  132. var selectedRootBones = selection.roots.ToSpriteSheetIfNeeded();
  133. pivot = pivot.FindRoot<BoneCache>(selectedRootBones);
  134. if (pivot == null)
  135. return;
  136. if (view.DoRotateBone(pivot.position, pivot.forward, out var deltaAngle))
  137. {
  138. if (!m_Moved)
  139. {
  140. skinningCache.BeginUndoOperation(TextContent.rotateBone);
  141. m_Moved = true;
  142. }
  143. m_Skeleton.RotateBones(selectedBones, deltaAngle);
  144. InvokePoseChanged();
  145. }
  146. }
  147. void HandleMoveBone()
  148. {
  149. if (view.IsActionTriggering(SkeletonAction.MoveBone))
  150. m_Moved = false;
  151. if (view.DoMoveBone(out var deltaPosition))
  152. {
  153. if (!m_Moved)
  154. {
  155. skinningCache.BeginUndoOperation(TextContent.moveBone);
  156. m_Moved = true;
  157. }
  158. m_Skeleton.MoveBones(rootBones, deltaPosition);
  159. InvokePoseChanged();
  160. }
  161. }
  162. void HandleFreeMoveBone()
  163. {
  164. if (view.IsActionTriggering(SkeletonAction.FreeMoveBone))
  165. m_Moved = false;
  166. if (view.DoFreeMoveBone(out var deltaPosition))
  167. {
  168. if (!m_Moved)
  169. {
  170. skinningCache.BeginUndoOperation(TextContent.freeMoveBone);
  171. m_Moved = true;
  172. }
  173. m_Skeleton.FreeMoveBones(selectedBones, deltaPosition);
  174. InvokePoseChanged();
  175. }
  176. }
  177. void HandleMoveJoint()
  178. {
  179. if (view.IsActionTriggering(SkeletonAction.MoveJoint))
  180. m_Moved = false;
  181. if (view.IsActionFinishing(SkeletonAction.MoveJoint))
  182. {
  183. if (hoveredTail != null && hoveredTail.chainedChild == null && hotBone.parent == hoveredTail)
  184. hoveredTail.chainedChild = hotBone;
  185. }
  186. if (view.DoMoveJoint(out var deltaPosition))
  187. {
  188. if (!m_Moved)
  189. {
  190. skinningCache.BeginUndoOperation(TextContent.moveJoint);
  191. m_Moved = true;
  192. }
  193. //Snap to parent endPosition
  194. if (hoveredTail != null && hoveredTail.chainedChild == null && hotBone.parent == hoveredTail)
  195. deltaPosition = hoveredTail.endPosition - hotBone.position;
  196. m_Skeleton.MoveJoints(selectedBones, deltaPosition);
  197. InvokePoseChanged();
  198. }
  199. }
  200. void HandleMoveEndPosition()
  201. {
  202. if (view.IsActionTriggering(SkeletonAction.MoveEndPosition))
  203. m_Moved = false;
  204. if (view.IsActionFinishing(SkeletonAction.MoveEndPosition))
  205. {
  206. if (hoveredJoint != null && hoveredJoint.parent == hotBone)
  207. hotBone.chainedChild = hoveredJoint;
  208. }
  209. if (view.DoMoveEndPosition(out var endPosition))
  210. {
  211. if (!m_Moved)
  212. {
  213. skinningCache.BeginUndoOperation(TextContent.moveEndPoint);
  214. m_Moved = true;
  215. }
  216. Debug.Assert(hotBone != null);
  217. Debug.Assert(hotBone.chainedChild == null);
  218. if (hoveredJoint != null && hoveredJoint.parent == hotBone)
  219. endPosition = hoveredJoint.position;
  220. m_Skeleton.SetEndPosition(hotBone, endPosition);
  221. InvokePoseChanged();
  222. }
  223. }
  224. void HandleChangeLength()
  225. {
  226. if (view.IsActionTriggering(SkeletonAction.ChangeLength))
  227. m_Moved = false;
  228. if (view.DoChangeLength(out var endPosition))
  229. {
  230. if (!m_Moved)
  231. {
  232. skinningCache.BeginUndoOperation(TextContent.boneLength);
  233. m_Moved = true;
  234. }
  235. Debug.Assert(hotBone != null);
  236. var direction = (Vector3)endPosition - hotBone.position;
  237. hotBone.length = Vector3.Dot(direction, hotBone.right);
  238. InvokePoseChanged();
  239. }
  240. }
  241. void HandleCreateBone()
  242. {
  243. if (view.DoCreateBoneStart(out var position))
  244. {
  245. m_PrevCreatedBone = null;
  246. if (hoveredTail != null)
  247. {
  248. m_PrevCreatedBone = hoveredTail;
  249. m_CreateBoneStartPosition = hoveredTail.endPosition;
  250. }
  251. else
  252. {
  253. m_CreateBoneStartPosition = position;
  254. }
  255. }
  256. if (view.DoCreateBone(out position))
  257. {
  258. using (skinningCache.UndoScope(TextContent.createBone))
  259. {
  260. var isChained = m_PrevCreatedBone != null;
  261. var parentBone = isChained ? m_PrevCreatedBone : rootBone;
  262. if (isChained)
  263. m_CreateBoneStartPosition = m_PrevCreatedBone.endPosition;
  264. var name = AutoBoneName(parentBone, skeleton.bones);
  265. var bone = m_Skeleton.CreateBone(parentBone, m_CreateBoneStartPosition, position, isChained, name);
  266. m_PrevCreatedBone = bone;
  267. m_CreateBoneStartPosition = bone.endPosition;
  268. InvokeTopologyChanged();
  269. InvokePoseChanged();
  270. }
  271. }
  272. }
  273. void HandleSplitBone()
  274. {
  275. if (view.DoSplitBone(out var instanceID, out var position))
  276. {
  277. using (skinningCache.UndoScope(TextContent.splitBone))
  278. {
  279. var boneToSplit = GetBone(instanceID);
  280. Debug.Assert(boneToSplit != null);
  281. var splitLength = Vector3.Dot(hoveredBone.right, position - boneToSplit.position);
  282. var name = AutoBoneName(boneToSplit, skeleton.bones);
  283. m_Skeleton.SplitBone(boneToSplit, splitLength, name);
  284. InvokeTopologyChanged();
  285. InvokePoseChanged();
  286. }
  287. }
  288. }
  289. void HandleRemoveBone()
  290. {
  291. if (view.DoRemoveBone())
  292. {
  293. using (skinningCache.UndoScope(TextContent.removeBone))
  294. {
  295. m_Skeleton.DestroyBones(selectedBones);
  296. selection.Clear();
  297. skinningCache.events.boneSelectionChanged.Invoke();
  298. InvokeTopologyChanged();
  299. InvokePoseChanged();
  300. }
  301. }
  302. }
  303. void HandleCancelMultiStepAction()
  304. {
  305. if (view.DoCancelMultistepAction(false))
  306. m_PrevCreatedBone = null;
  307. }
  308. void DrawSkeleton()
  309. {
  310. if (!view.IsRepainting())
  311. return;
  312. var isNotOnVisualElement = !skinningCache.IsOnVisualElement();
  313. if (view.IsActionActive(SkeletonAction.CreateBone) || view.IsActionHot(SkeletonAction.CreateBone))
  314. {
  315. if (isNotOnVisualElement)
  316. {
  317. var endPoint = view.GetMouseWorldPosition(Vector3.forward, Vector3.zero);
  318. if (view.IsActionHot(SkeletonAction.CreateBone))
  319. endPoint = m_CreateBoneStartPosition;
  320. if (m_PrevCreatedBone == null && hoveredTail == null)
  321. {
  322. var root = rootBone;
  323. if (root != null)
  324. view.DrawBoneParentLink(root.position, endPoint, Vector3.forward, style.GetParentLinkPreviewColor(skeleton.boneCount));
  325. }
  326. }
  327. }
  328. for (var i = 0; i < skeleton.boneCount; ++i)
  329. {
  330. var bone = skeleton.GetBone(i);
  331. if (bone.isVisible == false || bone.parentBone == null || bone.parentBone.chainedChild == bone)
  332. continue;
  333. view.DrawBoneParentLink(bone.parent.position, bone.position, Vector3.forward, style.GetParentLinkColor(bone));
  334. }
  335. for (var i = 0; i < skeleton.boneCount; ++i)
  336. {
  337. var bone = skeleton.GetBone(i);
  338. if ((view.IsActionActive(SkeletonAction.SplitBone) && hoveredBone == bone && isNotOnVisualElement) || bone.isVisible == false)
  339. continue;
  340. var isSelected = selection.Contains(bone.ToCharacterIfNeeded());
  341. var isHovered = hoveredBody == bone && view.IsActionHot(SkeletonAction.None) && isNotOnVisualElement;
  342. DrawBoneOutline(bone, style.GetOutlineColor(bone, isSelected, isHovered), style.GetOutlineScale(isSelected));
  343. }
  344. for (var i = 0; i < skeleton.boneCount; ++i)
  345. {
  346. var bone = skeleton.GetBone(i);
  347. if ((view.IsActionActive(SkeletonAction.SplitBone) && hoveredBone == bone && isNotOnVisualElement) || bone.isVisible == false)
  348. continue;
  349. DrawBone(bone, style.GetColor(bone));
  350. }
  351. }
  352. void DrawBone(BoneCache bone, Color color)
  353. {
  354. var isSelected = selection.Contains(bone.ToCharacterIfNeeded());
  355. var isNotOnVisualElement = !skinningCache.IsOnVisualElement();
  356. var isJointHovered = view.IsActionHot(SkeletonAction.None) && hoveredJoint == bone && isNotOnVisualElement;
  357. var isTailHovered = view.IsActionHot(SkeletonAction.None) && hoveredTail == bone && isNotOnVisualElement;
  358. view.DrawBone(bone.position, bone.right, Vector3.forward, bone.length, color, bone.chainedChild != null, isSelected, isJointHovered, isTailHovered, bone == hotBone);
  359. }
  360. void DrawBoneOutline(BoneCache bone, Color color, float outlineScale)
  361. {
  362. view.DrawBoneOutline(bone.position, bone.right, Vector3.forward, bone.length, color, outlineScale);
  363. }
  364. void DrawSplitBonePreview()
  365. {
  366. if (!view.IsRepainting())
  367. return;
  368. if (skinningCache.IsOnVisualElement())
  369. return;
  370. if (view.IsActionActive(SkeletonAction.SplitBone) && hoveredBone != null)
  371. {
  372. var splitLength = Vector3.Dot(hoveredBone.right, view.GetMouseWorldPosition(hoveredBone.forward, hoveredBody.position) - hoveredBone.position);
  373. var position = hoveredBone.position + hoveredBone.right * splitLength;
  374. var length = hoveredBone.length - splitLength;
  375. var isSelected = selection.Contains(hoveredBone.ToCharacterIfNeeded());
  376. {
  377. var color = style.GetOutlineColor(hoveredBone, false, false);
  378. if (color.a > 0f)
  379. view.DrawBoneOutline(hoveredBone.position, hoveredBone.right, Vector3.forward, splitLength, style.GetOutlineColor(hoveredBone, isSelected, true), style.GetOutlineScale(false));
  380. }
  381. {
  382. var color = style.GetPreviewOutlineColor(skeleton.boneCount);
  383. if (color.a > 0f)
  384. view.DrawBoneOutline(position, hoveredBone.right, Vector3.forward, length, style.GetPreviewOutlineColor(skeleton.boneCount), style.GetOutlineScale(false));
  385. }
  386. view.DrawBone(hoveredBone.position,
  387. hoveredBone.right,
  388. Vector3.forward,
  389. splitLength,
  390. style.GetColor(hoveredBone),
  391. hoveredBone.chainedChild != null,
  392. false, false, false, false);
  393. view.DrawBone(position,
  394. hoveredBone.right,
  395. Vector3.forward,
  396. length,
  397. style.GetPreviewColor(skeleton.boneCount),
  398. hoveredBone.chainedChild != null,
  399. false, false, false, false);
  400. }
  401. }
  402. void DrawCreateBonePreview()
  403. {
  404. if (!view.IsRepainting())
  405. return;
  406. if (skinningCache.IsOnVisualElement())
  407. return;
  408. var color = style.GetPreviewColor(skeleton.boneCount);
  409. var outlineColor = style.GetPreviewOutlineColor(skeleton.boneCount);
  410. var startPosition = m_CreateBoneStartPosition;
  411. var mousePosition = view.GetMouseWorldPosition(Vector3.forward, Vector3.zero);
  412. if (view.IsActionActive(SkeletonAction.CreateBone))
  413. {
  414. startPosition = mousePosition;
  415. if (hoveredTail != null)
  416. startPosition = hoveredTail.endPosition;
  417. if (outlineColor.a > 0f)
  418. view.DrawBoneOutline(startPosition, Vector3.right, Vector3.forward, 0f, outlineColor, style.GetOutlineScale(false));
  419. view.DrawBone(startPosition, Vector3.right, Vector3.forward, 0f, color, false, false, false, false, false);
  420. }
  421. if (view.IsActionHot(SkeletonAction.CreateBone))
  422. {
  423. var direction = (mousePosition - startPosition);
  424. if (outlineColor.a > 0f)
  425. view.DrawBoneOutline(startPosition, direction.normalized, Vector3.forward, direction.magnitude, outlineColor, style.GetOutlineScale(false));
  426. view.DrawBone(startPosition, direction.normalized, Vector3.forward, direction.magnitude, color, false, false, false, false, false);
  427. }
  428. }
  429. void DrawCursors()
  430. {
  431. if (!view.IsRepainting())
  432. return;
  433. view.DrawCursors(!skinningCache.IsOnVisualElement());
  434. }
  435. public static string AutoBoneName(BoneCache parent, IEnumerable<BoneCache> bones)
  436. {
  437. var parentName = "root";
  438. if (parent != null)
  439. parentName = parent.name;
  440. DissectBoneName(parentName, out var inheritedName, out _);
  441. int nameCounter = FindBiggestNameCounter(bones);
  442. if (inheritedName == k_DefaultRootName)
  443. inheritedName = k_DefaultBoneName;
  444. return $"{inheritedName}_{++nameCounter}";
  445. }
  446. public static string AutoNameBoneCopy(string originalBoneName, IEnumerable<BoneCache> bones)
  447. {
  448. DissectBoneName(originalBoneName, out var inheritedName, out _);
  449. int nameCounter = FindBiggestNameCounterForBone(inheritedName, bones);
  450. if (inheritedName == k_DefaultRootName)
  451. inheritedName = k_DefaultBoneName;
  452. return $"{inheritedName}_{++nameCounter}";
  453. }
  454. static int FindBiggestNameCounter(IEnumerable<BoneCache> bones)
  455. {
  456. var autoNameCounter = 0;
  457. foreach (var bone in bones)
  458. {
  459. DissectBoneName(bone.name, out _, out var counter);
  460. if (counter > autoNameCounter)
  461. autoNameCounter = counter;
  462. }
  463. return autoNameCounter;
  464. }
  465. static int FindBiggestNameCounterForBone(string boneName, IEnumerable<BoneCache> bones)
  466. {
  467. var autoNameCounter = 0;
  468. foreach (var bone in bones)
  469. {
  470. DissectBoneName(bone.name, out var inheritedName, out var counter);
  471. {
  472. if (inheritedName == boneName)
  473. {
  474. if (counter > autoNameCounter)
  475. autoNameCounter = counter;
  476. }
  477. }
  478. }
  479. return autoNameCounter;
  480. }
  481. static void DissectBoneName(string boneName, out string inheritedName, out int counter)
  482. {
  483. if (IsBoneNameMatchAutoFormat(boneName))
  484. {
  485. var tokens = boneName.Split('_');
  486. var lastTokenIndex = tokens.Length - 1;
  487. var tokensWithoutLast = new string[lastTokenIndex];
  488. Array.Copy(tokens, tokensWithoutLast, lastTokenIndex);
  489. inheritedName = string.Join("_", tokensWithoutLast);
  490. counter = int.Parse(tokens[lastTokenIndex]);
  491. }
  492. else
  493. {
  494. inheritedName = boneName;
  495. counter = -1;
  496. }
  497. }
  498. static bool IsBoneNameMatchAutoFormat(string boneName)
  499. {
  500. return s_Regex.IsMatch(boneName);
  501. }
  502. void InvokeTopologyChanged()
  503. {
  504. skinningCache.events.skeletonTopologyChanged.Invoke(skeleton);
  505. }
  506. internal void InvokePoseChanged()
  507. {
  508. skeleton.SetPosePreview();
  509. if (editBindPose)
  510. {
  511. skeleton.SetDefaultPose();
  512. skinningCache.events.skeletonBindPoseChanged.Invoke(skeleton);
  513. }
  514. else
  515. skinningCache.events.skeletonPreviewPoseChanged.Invoke(skeleton);
  516. }
  517. }
  518. }