A game about forced loneliness, made by TACStudios
at master 723 lines 25 kB view raw
1using System; 2using System.Collections; 3using System.Linq; 4using System.Collections.Generic; 5using UnityEngine; 6using UnityEditorInternal; 7 8namespace UnityEditor.U2D.Sprites 9{ 10 internal class SpriteRectModel : ScriptableObject, ISerializationCallbackReceiver 11 { 12 [Serializable] 13 struct StringGUID 14 { 15 [SerializeField] 16 string m_StringGUID; 17 18 public StringGUID(GUID guid) 19 { 20 m_StringGUID = guid.ToString(); 21 } 22 23 public static implicit operator GUID(StringGUID d) => new GUID(d.m_StringGUID); 24 public static implicit operator StringGUID(GUID d) => new StringGUID(d); 25 } 26 27 [Serializable] 28 class StringGUIDList : IReadOnlyList<GUID> 29 { 30 [SerializeField] 31 List<StringGUID> m_List = new List<StringGUID>(); 32 33 GUID IReadOnlyList<GUID>.this[int index] 34 { 35 get => m_List[index]; 36 } 37 38 public StringGUID this[int index] 39 { 40 get => m_List[index]; 41 set => m_List[index] = value; 42 } 43 44 IEnumerator<GUID> IEnumerable<GUID>.GetEnumerator() 45 { 46 // Not used for now 47 throw new NotImplementedException(); 48 } 49 50 public int Count => m_List.Count; 51 52 public IEnumerator GetEnumerator() 53 { 54 return m_List.GetEnumerator(); 55 } 56 57 public void Clear() 58 { 59 m_List.Clear(); 60 } 61 62 public void RemoveAt(int i) 63 { 64 m_List.RemoveAt(i); 65 } 66 67 public void Add(StringGUID value) 68 { 69 m_List.Add(value); 70 } 71 } 72 73 /// <summary> 74 /// List of all SpriteRects 75 /// </summary> 76 [SerializeField] private List<SpriteRect> m_SpriteRects; 77 /// <summary> 78 /// List of all names in the Name-FileId Table 79 /// </summary> 80 [SerializeField] private List<string> m_SpriteNames; 81 /// <summary> 82 /// List of all FileIds in the Name-FileId Table 83 /// </summary> 84 [SerializeField] private StringGUIDList m_SpriteFileIds; 85 [SerializeField] 86 int m_Version = 0; 87 int m_CurrentVersion = 0; 88 89 /// <summary> 90 /// HashSet of all names currently in use by SpriteRects 91 /// </summary> 92 private HashSet<string> m_NamesInUse; 93 private HashSet<GUID> m_InternalIdsInUse; 94 95 public IReadOnlyList<SpriteRect> spriteRects => m_SpriteRects; 96 public IReadOnlyList<string> spriteNames => m_SpriteNames; 97 public IReadOnlyList<GUID> spriteFileIds => m_SpriteFileIds; 98 99 private SpriteRectModel() 100 { 101 m_SpriteNames = new List<string>(); 102 m_SpriteFileIds = new StringGUIDList(); 103 Clear(); 104 } 105 106 public void RegisterUndo(IUndoSystem undoSystem, string undoMessage) 107 { 108 undoSystem.RegisterCompleteObjectUndo(this, undoMessage); 109 m_CurrentVersion++; 110 m_Version = m_CurrentVersion; 111 } 112 113 public bool VersionChanged(bool resetVersion) 114 { 115 var versionChanged = m_Version != m_CurrentVersion; 116 if (resetVersion) 117 m_CurrentVersion = m_Version; 118 return versionChanged; 119 } 120 121 public void SetSpriteRects(IList<SpriteRect> newSpriteRects) 122 { 123 m_SpriteRects.Clear(); 124 m_SpriteRects.InsertRange(0, newSpriteRects); 125 m_NamesInUse = new HashSet<string>(); 126 m_InternalIdsInUse = new HashSet<GUID>(); 127 for (var i = 0; i < m_SpriteRects.Count; ++i) 128 { 129 m_NamesInUse.Add(m_SpriteRects[i].name); 130 m_InternalIdsInUse.Add(m_SpriteRects[i].spriteID); 131 } 132 } 133 134 public void SetNameFileIdPairs(IEnumerable<SpriteNameFileIdPair> pairs) 135 { 136 m_SpriteNames.Clear(); 137 m_SpriteFileIds.Clear(); 138 139 foreach (var pair in pairs) 140 AddNameFileIdPair(pair.name, pair.GetFileGUID()); 141 } 142 143 public int FindIndex(Predicate<SpriteRect> match) 144 { 145 int i = 0; 146 foreach (var spriteRect in m_SpriteRects) 147 { 148 if (match.Invoke(spriteRect)) 149 return i; 150 i++; 151 } 152 return -1; 153 } 154 155 public void Clear() 156 { 157 m_SpriteRects = new List<SpriteRect>(); 158 m_NamesInUse = new HashSet<string>(); 159 m_InternalIdsInUse = new HashSet<GUID>(); 160 } 161 162 public bool Add(SpriteRect spriteRect, bool shouldReplaceInTable = false) 163 { 164 if (spriteRect.spriteID.Empty()) 165 { 166 spriteRect.spriteID = GUID.Generate(); 167 } 168 else 169 { 170 if (IsInternalIdInUsed(spriteRect.spriteID)) 171 return false; 172 } 173 174 if (shouldReplaceInTable) 175 { 176 // replace id from sprite to file id table 177 if (!UpdateIdInNameIdPair(spriteRect.name, spriteRect.spriteID)) 178 { 179 // add it into file id table if update wasn't successful i.e. it doesn't exist yet 180 AddNameFileIdPair(spriteRect.name, spriteRect.spriteID); 181 } 182 } 183 else 184 { 185 // Since we are not replacing the file id table, 186 // look for any existing id and set it to the SpriteRect 187 var index = m_SpriteNames.FindIndex(x => x == spriteRect.name); 188 if (index >= 0) 189 { 190 if (IsInternalIdInUsed(m_SpriteFileIds[index])) 191 return false; 192 spriteRect.spriteID = m_SpriteFileIds[index]; 193 } 194 else 195 AddNameFileIdPair(spriteRect.name, spriteRect.spriteID); 196 } 197 198 m_SpriteRects.Add(spriteRect); 199 m_NamesInUse.Add(spriteRect.name); 200 m_InternalIdsInUse.Add(spriteRect.spriteID); 201 return true; 202 } 203 204 public void Remove(SpriteRect spriteRect) 205 { 206 m_SpriteRects.Remove(spriteRect); 207 m_NamesInUse.Remove(spriteRect.name); 208 m_InternalIdsInUse.Remove(spriteRect.spriteID); 209 } 210 211 /// <summary> 212 /// Checks whether or not the name is currently in use by any of the SpriteRects in the texture. 213 /// </summary> 214 /// <param name="rectName">The name to check for</param> 215 /// <returns>True if the name is currently in use</returns> 216 public bool IsNameUsed(string rectName) 217 { 218 return m_NamesInUse.Contains(rectName); 219 } 220 221 /// <summary> 222 /// Checks whether or not the id is currently in use by any of the SpriteRects in the texture. 223 /// </summary> 224 /// <param name="rectName">The id to check for</param> 225 /// <returns>True if the name is currently in use</returns> 226 public bool IsInternalIdInUsed(GUID internalId) 227 { 228 return m_InternalIdsInUse.Contains(internalId); 229 } 230 231 public List<SpriteRect> GetSpriteRects() 232 { 233 return m_SpriteRects; 234 } 235 236 public bool Rename(string oldName, string newName, GUID fileId) 237 { 238 if (!IsNameUsed(oldName)) 239 return false; 240 if (IsNameUsed(newName)) 241 return false; 242 243 var index = m_SpriteNames.FindIndex(x => x == oldName); 244 if (index >= 0) 245 { 246 m_SpriteNames.RemoveAt(index); 247 m_SpriteFileIds.RemoveAt(index); 248 } 249 250 index = m_SpriteNames.FindIndex(x => x == newName); 251 if (index >= 0) 252 m_SpriteFileIds[index] = fileId; 253 else 254 AddNameFileIdPair(newName, fileId); 255 256 m_NamesInUse.Remove(oldName); 257 m_NamesInUse.Add(newName); 258 return true; 259 } 260 261 void AddNameFileIdPair(string spriteName, GUID fileId) 262 { 263 m_SpriteNames.Add(spriteName); 264 m_SpriteFileIds.Add(fileId); 265 } 266 267 bool UpdateIdInNameIdPair(string spriteName, GUID newFileId) 268 { 269 var index = m_SpriteNames.FindIndex(x => x == spriteName); 270 if (index >= 0) 271 { 272 m_SpriteFileIds[index] = newFileId; 273 return true; 274 } 275 276 return false; 277 } 278 279 public void ClearUnusedFileID() 280 { 281 m_SpriteNames.Clear(); 282 m_SpriteFileIds.Clear(); 283 foreach (var sprite in m_SpriteRects) 284 { 285 m_SpriteNames.Add(sprite.name); 286 m_SpriteFileIds.Add(sprite.spriteID); 287 } 288 } 289 290 void ISerializationCallbackReceiver.OnBeforeSerialize() 291 {} 292 293 void ISerializationCallbackReceiver.OnAfterDeserialize() 294 { 295 SetSpriteRects(new List<SpriteRect>(m_SpriteRects)); 296 } 297 } 298 299 internal class OutlineSpriteRect : SpriteRect 300 { 301 public List<Vector2[]> outlines; 302 303 public OutlineSpriteRect(SpriteRect rect) 304 { 305 this.name = rect.name; 306 this.originalName = rect.originalName; 307 this.pivot = rect.pivot; 308 this.alignment = rect.alignment; 309 this.border = rect.border; 310 this.rect = rect.rect; 311 this.spriteID = rect.spriteID; 312 outlines = new List<Vector2[]>(); 313 } 314 } 315 316 internal abstract partial class SpriteFrameModuleBase : SpriteEditorModuleModeSupportBase 317 { 318 [Serializable] 319 internal class SpriteFrameModulePersistentState : ScriptableSingleton<SpriteFrameModulePersistentState> 320 { 321 public PivotUnitMode pivotUnitMode = PivotUnitMode.Normalized; 322 } 323 324 protected SpriteRectModel m_RectsCache; 325 protected ITextureDataProvider m_TextureDataProvider; 326 protected ISpriteEditorDataProvider m_SpriteDataProvider; 327 protected ISpriteNameFileIdDataProvider m_NameFileIdDataProvider; 328 string m_ModuleName; 329 330 internal enum PivotUnitMode 331 { 332 Normalized, 333 Pixels 334 } 335 336 static PivotUnitMode pivotUnitMode 337 { 338 get => SpriteFrameModulePersistentState.instance.pivotUnitMode; 339 set => SpriteFrameModulePersistentState.instance.pivotUnitMode = value; 340 } 341 342 protected SpriteFrameModuleBase(string name, ISpriteEditor sw, IEventSystem es, IUndoSystem us, IAssetDatabase ad) 343 { 344 spriteEditor = sw; 345 eventSystem = es; 346 undoSystem = us; 347 assetDatabase = ad; 348 m_ModuleName = name; 349 } 350 351 // implements ISpriteEditorModule 352 353 public override void OnModuleActivate() 354 { 355 m_SpriteDataProvider = GetDataProvider<ISpriteEditorDataProvider>(); 356 spriteImportMode = SpriteFrameModule.GetSpriteImportMode(m_SpriteDataProvider); 357 m_TextureDataProvider = GetDataProvider<ITextureDataProvider>(); 358 m_NameFileIdDataProvider = GetDataProvider<ISpriteNameFileIdDataProvider>(); 359 360 m_TextureDataProvider.RegisterDataChangeCallback(OnTextureDataProviderChanged); 361 OnTextureDataProviderChanged(m_TextureDataProvider); 362 InitSpriteRectCache(); 363 364 AddMainUI(spriteEditor.GetMainVisualContainer()); 365 undoSystem.RegisterUndoCallback(UndoCallback); 366 foreach (var mode in modes) 367 { 368 mode.OnAddToModule(this); 369 } 370 } 371 372 void OnTextureDataProviderChanged(ITextureDataProvider obj) 373 { 374 int width, height; 375 m_TextureDataProvider.GetTextureActualWidthAndHeight(out width, out height); 376 textureActualWidth = width; 377 textureActualHeight = height; 378 } 379 380 void InitSpriteRectCache() 381 { 382 if(m_RectsCache != null) 383 ScriptableObject.DestroyImmediate(m_RectsCache); 384 385 var spriteList = m_SpriteDataProvider.GetSpriteRects().ToList(); 386 if (m_NameFileIdDataProvider == null) 387 m_NameFileIdDataProvider = new DefaultSpriteNameFileIdDataProvider(spriteList); 388 var nameFileIdPairs = m_NameFileIdDataProvider.GetNameFileIdPairs(); 389 390 m_RectsCache = ScriptableObject.CreateInstance<SpriteRectModel>(); 391 m_RectsCache.hideFlags = HideFlags.HideAndDontSave; 392 393 m_RectsCache.SetSpriteRects(spriteList); 394 spriteEditor.spriteRects = spriteList; 395 m_RectsCache.SetNameFileIdPairs(nameFileIdPairs); 396 397 if (spriteEditor.selectedSpriteRect != null) 398 spriteEditor.selectedSpriteRect = m_RectsCache.spriteRects.FirstOrDefault(x => x.spriteID == spriteEditor.selectedSpriteRect.spriteID); 399 } 400 401 public override void OnModuleDeactivate() 402 { 403 foreach (var mode in modes) 404 { 405 mode.OnRemoveFromModule(this); 406 } 407 if (m_RectsCache != null) 408 { 409 undoSystem.ClearUndo(m_RectsCache); 410 ScriptableObject.DestroyImmediate(m_RectsCache); 411 spriteEditor.spriteRects = m_SpriteDataProvider.GetSpriteRects().ToList(); 412 m_RectsCache = null; 413 } 414 m_TextureDataProvider.UnregisterDataChangeCallback(OnTextureDataProviderChanged); 415 undoSystem.UnregisterUndoCallback(UndoCallback); 416 RemoveMainUI(spriteEditor.GetMainVisualContainer()); 417 } 418 419 public override bool ApplyRevert(bool apply) 420 { 421 if (apply) 422 { 423 var array = m_RectsCache != null ? m_RectsCache.spriteRects.ToArray() : null; 424 var spriteDataProvider = spriteEditor.GetDataProvider<ISpriteEditorDataProvider>(); 425 var nameFileIdDataProvider = spriteEditor.GetDataProvider<ISpriteNameFileIdDataProvider>(); 426 spriteDataProvider.SetSpriteRects(array); 427 428 var spriteNames = m_RectsCache?.spriteNames; 429 var spriteFileIds = m_RectsCache?.spriteFileIds; 430 if (spriteNames != null && spriteFileIds != null && nameFileIdDataProvider != null) 431 { 432 var pairList = new List<SpriteNameFileIdPair>(spriteNames.Count); 433 for (var i = 0; i < spriteNames.Count; ++i) 434 pairList.Add(new SpriteNameFileIdPair(spriteNames[i], spriteFileIds[i])); 435 nameFileIdDataProvider.SetNameFileIdPairs(pairList.ToArray()); 436 } 437 438 var outlineDataProvider = spriteDataProvider.GetDataProvider<ISpriteOutlineDataProvider>(); 439 var physicsDataProvider = spriteDataProvider.GetDataProvider<ISpritePhysicsOutlineDataProvider>(); 440 foreach (var rect in array) 441 { 442 if (rect is OutlineSpriteRect outlineRect) 443 { 444 if (outlineRect.outlines.Count > 0) 445 { 446 outlineDataProvider.SetOutlines(outlineRect.spriteID, outlineRect.outlines); 447 physicsDataProvider.SetOutlines(outlineRect.spriteID, outlineRect.outlines); 448 } 449 } 450 } 451 452 if (m_RectsCache != null) 453 undoSystem.ClearUndo(m_RectsCache); 454 } 455 else 456 { 457 if (m_RectsCache != null) 458 { 459 undoSystem.ClearUndo(m_RectsCache); 460 InitSpriteRectCache(); 461 } 462 } 463 464 return true; 465 } 466 467 public override string moduleName 468 { 469 get { return m_ModuleName; } 470 } 471 472 // injected interfaces 473 protected IEventSystem eventSystem 474 { 475 get; 476 private set; 477 } 478 479 protected IUndoSystem undoSystem 480 { 481 get; 482 private set; 483 } 484 485 protected IAssetDatabase assetDatabase 486 { 487 get; 488 private set; 489 } 490 491 protected SpriteRect selected 492 { 493 get { return spriteEditor.selectedSpriteRect; } 494 set { spriteEditor.selectedSpriteRect = value; } 495 } 496 497 protected SpriteImportMode spriteImportMode 498 { 499 get; private set; 500 } 501 502 protected string spriteAssetPath 503 { 504 get { return assetDatabase.GetAssetPath(m_SpriteDataProvider.targetObject); } 505 } 506 507 public bool hasSelected 508 { 509 get { return spriteEditor.selectedSpriteRect != null; } 510 } 511 512 public SpriteAlignment selectedSpriteAlignment 513 { 514 get { return selected.alignment; } 515 } 516 517 public Vector2 selectedSpritePivot 518 { 519 get { return selected.pivot; } 520 } 521 522 private Vector2 selectedSpritePivotInCurUnitMode 523 { 524 get 525 { 526 return pivotUnitMode == PivotUnitMode.Pixels 527 ? ConvertFromNormalizedToRectSpace(selectedSpritePivot, selectedSpriteRect_Rect) 528 : selectedSpritePivot; 529 } 530 } 531 532 public int CurrentSelectedSpriteIndex() 533 { 534 if (m_RectsCache != null && selected != null) 535 return m_RectsCache.FindIndex(x => x.spriteID == selected.spriteID); 536 return -1; 537 } 538 539 public Vector4 selectedSpriteBorder 540 { 541 get { return ClampSpriteBorderToRect(selected.border, selected.rect); } 542 set 543 { 544 m_RectsCache.RegisterUndo(undoSystem, "Change Sprite Border"); 545 selected.border = ClampSpriteBorderToRect(value, selected.rect); 546 NotifyOnSpriteRectChanged(); 547 spriteEditor.SetDataModified(); 548 } 549 } 550 551 public Rect selectedSpriteRect_Rect 552 { 553 get { return selected.rect; } 554 set 555 { 556 m_RectsCache.RegisterUndo(undoSystem, "Change Sprite rect"); 557 selected.rect = ClampSpriteRect(value, textureActualWidth, textureActualHeight); 558 NotifyOnSpriteRectChanged(); 559 spriteEditor.SetDataModified(); 560 } 561 } 562 563 public string selectedSpriteName 564 { 565 get { return selected.name; } 566 set 567 { 568 if (selected.name == value) 569 return; 570 if (m_RectsCache.IsNameUsed(value)) 571 return; 572 573 string oldName = selected.name; 574 string newName = InternalEditorUtility.RemoveInvalidCharsFromFileName(value, true); 575 576 // These can only be changed in sprite multiple mode 577 if (string.IsNullOrEmpty(selected.originalName) && (newName != oldName)) 578 selected.originalName = oldName; 579 580 // Is the name empty? 581 if (string.IsNullOrEmpty(newName)) 582 newName = oldName; 583 584 // Did the rename succeed? 585 if (m_RectsCache.Rename(oldName, newName, selected.spriteID)) 586 { 587 m_RectsCache.RegisterUndo(undoSystem, "Change Sprite Name"); 588 selected.name = newName; 589 NotifyOnSpriteRectChanged(); 590 spriteEditor.SetDataModified(); 591 } 592 } 593 } 594 595 public int spriteCount 596 { 597 get { return m_RectsCache.spriteRects.Count; } 598 } 599 600 public Vector4 GetSpriteBorderAt(int i) 601 { 602 return m_RectsCache.spriteRects[i].border; 603 } 604 605 public Rect GetSpriteRectAt(int i) 606 { 607 return m_RectsCache.spriteRects[i].rect; 608 } 609 610 public int textureActualWidth { get; private set; } 611 public int textureActualHeight { get; private set; } 612 613 public void SetSpritePivotAndAlignment(Vector2 pivot, SpriteAlignment alignment) 614 { 615 m_RectsCache.RegisterUndo(undoSystem, "Change Sprite Pivot"); 616 selected.alignment = alignment; 617 selected.pivot = SpriteEditorUtility.GetPivotValue(alignment, pivot); 618 NotifyOnSpriteRectChanged(); 619 spriteEditor.SetDataModified(); 620 } 621 622 public bool containsMultipleSprites 623 { 624 get { return spriteImportMode == SpriteImportMode.Multiple; } 625 } 626 627 protected void SnapPivotToSnapPoints(Vector2 pivot, out Vector2 outPivot, out SpriteAlignment outAlignment) 628 { 629 Rect rect = selectedSpriteRect_Rect; 630 631 // Convert from normalized space to texture space 632 Vector2 texturePos = new Vector2(rect.xMin + rect.width * pivot.x, rect.yMin + rect.height * pivot.y); 633 634 Vector2[] snapPoints = GetSnapPointsArray(rect); 635 636 // Snapping is now a firm action, it will always snap to one of the snapping points. 637 SpriteAlignment snappedAlignment = SpriteAlignment.Custom; 638 float nearestDistance = float.MaxValue; 639 for (int alignment = 0; alignment < snapPoints.Length; alignment++) 640 { 641 float distance = (texturePos - snapPoints[alignment]).magnitude * m_Zoom; 642 if (distance < nearestDistance) 643 { 644 snappedAlignment = (SpriteAlignment)alignment; 645 nearestDistance = distance; 646 } 647 } 648 649 outAlignment = snappedAlignment; 650 outPivot = ConvertFromTextureToNormalizedSpace(snapPoints[(int)snappedAlignment], rect); 651 } 652 653 protected void SnapPivotToPixels(Vector2 pivot, out Vector2 outPivot, out SpriteAlignment outAlignment) 654 { 655 outAlignment = SpriteAlignment.Custom; 656 657 Rect rect = selectedSpriteRect_Rect; 658 float unitsPerPixelX = 1.0f / rect.width; 659 float unitsPerPixelY = 1.0f / rect.height; 660 outPivot.x = Mathf.Round(pivot.x / unitsPerPixelX) * unitsPerPixelX; 661 outPivot.y = Mathf.Round(pivot.y / unitsPerPixelY) * unitsPerPixelY; 662 } 663 664 private void UndoCallback() 665 { 666 if(m_RectsCache.VersionChanged(true)) 667 NotifyOnSpriteRectChanged(); 668 UIUndoCallback(); 669 } 670 671 protected static Rect ClampSpriteRect(Rect rect, float maxX, float maxY) 672 { 673 // Clamp rect to width height 674 rect = FlipNegativeRect(rect); 675 Rect newRect = new Rect(); 676 677 newRect.xMin = Mathf.Clamp(rect.xMin, 0, maxX - 1); 678 newRect.yMin = Mathf.Clamp(rect.yMin, 0, maxY - 1); 679 newRect.xMax = Mathf.Clamp(rect.xMax, 1, maxX); 680 newRect.yMax = Mathf.Clamp(rect.yMax, 1, maxY); 681 682 // Prevent width and height to be 0 value after clamping. 683 if (Mathf.RoundToInt(newRect.width) == 0) 684 newRect.width = 1; 685 if (Mathf.RoundToInt(newRect.height) == 0) 686 newRect.height = 1; 687 688 return SpriteEditorUtility.RoundedRect(newRect); 689 } 690 691 protected static Rect FlipNegativeRect(Rect rect) 692 { 693 Rect newRect = new Rect(); 694 695 newRect.xMin = Mathf.Min(rect.xMin, rect.xMax); 696 newRect.yMin = Mathf.Min(rect.yMin, rect.yMax); 697 newRect.xMax = Mathf.Max(rect.xMin, rect.xMax); 698 newRect.yMax = Mathf.Max(rect.yMin, rect.yMax); 699 700 return newRect; 701 } 702 703 protected static Vector4 ClampSpriteBorderToRect(Vector4 border, Rect rect) 704 { 705 Rect flipRect = FlipNegativeRect(rect); 706 float w = flipRect.width; 707 float h = flipRect.height; 708 709 Vector4 newBorder = new Vector4(); 710 711 // Make sure borders are within the width/height and left < right and top < bottom 712 newBorder.x = Mathf.RoundToInt(Mathf.Clamp(border.x, 0, Mathf.Min(Mathf.Abs(w - border.z), w))); // Left 713 newBorder.z = Mathf.RoundToInt(Mathf.Clamp(border.z, 0, Mathf.Min(Mathf.Abs(w - newBorder.x), w))); // Right 714 715 newBorder.y = Mathf.RoundToInt(Mathf.Clamp(border.y, 0, Mathf.Min(Mathf.Abs(h - border.w), h))); // Bottom 716 newBorder.w = Mathf.RoundToInt(Mathf.Clamp(border.w, 0, Mathf.Min(Mathf.Abs(h - newBorder.y), h))); // Top 717 718 return newBorder; 719 } 720 721 protected virtual void NotifyOnSpriteRectChanged() { } 722 } 723}