/// <summary>Draws a box describing the internal details of the `playable`.</summary> internal void DoInternalDetailsGUI(AnimancerPlayable playable) { if (Event.current.type == EventType.Layout) { var text = ObjectPool.AcquireStringBuilder(); playable.AppendInternalDetails(text, "", " - "); _UpdateListLabel = text.ReleaseToString(); } if (_UpdateListLabel == null) { return; } if (_InternalDetailsStyle == null) { _InternalDetailsStyle = new GUIStyle(GUI.skin.box) { alignment = TextAnchor.MiddleLeft, wordWrap = false, stretchWidth = true, } } ; using (ObjectPool.Disposable.AcquireContent(out var content, _UpdateListLabel, null, false)) { var height = _InternalDetailsStyle.CalcHeight(content, 0); var area = GUILayoutUtility.GetRect(0, height, _InternalDetailsStyle); GUI.Box(area, content, _InternalDetailsStyle); CheckContextMenu(area, playable); } }
/************************************************************************************************************************/ private void SelectCharacter(int index) { var text = ObjectPool.AcquireStringBuilder(); for (int i = 0; i < _Characters.Length; i++) { var active = i == index; _Characters[i].SetActive(active); if (i > 0) { text.AppendLine(); } if (active) { text.Append("<b>"); } text.Append(1 + i) .Append(" = ") .Append(_Characters[i].name); if (active) { text.Append("</b>"); } } _Text.text = text.ReleaseToString(); }
/************************************************************************************************************************/ #endregion /************************************************************************************************************************/ #region Methods /************************************************************************************************************************/ /// <summary>Uses <see cref="GatherNameToSprites"/> and creates new animations from those groups.</summary> private static void GenerateAnimationsBySpriteName(List <Sprite> sprites) { if (sprites.Count == 0) { return; } sprites.Sort(NaturalCompare); var nameToSprites = new Dictionary <string, List <Sprite> >(); GatherNameToSprites(sprites, nameToSprites); var pathToSprites = new Dictionary <string, List <Sprite> >(); var message = ObjectPool.AcquireStringBuilder() .Append("Do you wish to generate the following animations?"); const int MaxLines = 25; var line = 0; foreach (var nameToSpriteGroup in nameToSprites) { var path = AssetDatabase.GetAssetPath(nameToSpriteGroup.Value[0]); path = Path.GetDirectoryName(path); path = Path.Combine(path, nameToSpriteGroup.Key + ".anim"); pathToSprites.Add(path, nameToSpriteGroup.Value); if (++line <= MaxLines) { message.AppendLine() .Append("- ") .Append(path) .Append(" (") .Append(nameToSpriteGroup.Value.Count) .Append(" frames)"); } } if (line > MaxLines) { message.AppendLine() .Append("And ") .Append(line - MaxLines) .Append(" others."); } if (!EditorUtility.DisplayDialog("Generate Sprite Animations?", message.ReleaseToString(), "Generate", "Cancel")) { return; } foreach (var pathToSpriteGroup in pathToSprites) { CreateAnimation(pathToSpriteGroup.Key, pathToSpriteGroup.Value.ToArray()); } AssetDatabase.SaveAssets(); }
/************************************************************************************************************************/ /// <summary> /// Logs a description of the issues found when comparing the properties animated by the `state` to the /// properties that actually exist on the target <see cref="GameObject"/> and its children. /// </summary> public void LogIssues(AnimancerState state, MatchType match) { var animator = state.Root?.Component.Animator; var newMatch = match; var message = ObjectPool.AcquireStringBuilder(); switch (match) { default: case MatchType.Unknown: message.Append("The animation bindings are still being checked."); Debug.Log(EditorGUIUtility.systemCopyBuffer = message.ReleaseToString(), animator); break; case MatchType.Correct: message.Append("No issues were found when comparing the properties animated by '") .Append(state) .Append("' to the Rig of '") .Append(animator.name) .Append("'."); Debug.Log(EditorGUIUtility.systemCopyBuffer = message.ReleaseToString(), animator); break; case MatchType.Empty: message.Append("'") .Append(state) .Append("' does not animate any properties so it will not do anything."); Debug.Log(EditorGUIUtility.systemCopyBuffer = message.ReleaseToString(), animator); break; case MatchType.Warning: message.Append("Possible Bug Detected: some of the details of '") .Append(state) .Append("' do not match the Rig of '") .Append(animator.name) .Append("' so the animation might not work correctly."); newMatch = GetMatchType(animator, state, message); Debug.LogWarning(EditorGUIUtility.systemCopyBuffer = message.ReleaseToString(), animator); break; case MatchType.Error: message.Append("Possible Bug Detected: the details of '") .Append(state) .Append("' do not match the Rig of '") .Append(animator.name) .Append("' so the animation might not work correctly."); newMatch = GetMatchType(animator, state, message); Debug.LogError(EditorGUIUtility.systemCopyBuffer = message.ReleaseToString(), animator); break; } if (newMatch != match) { Debug.LogWarning($"{nameof(MatchType)} changed from {match} to {newMatch}" + " between the initial check and the button press."); } }
/************************************************************************************************************************/ /// <summary>Copies all of the <see cref="_NewBindingPaths"/> to the system clipboard.</summary> private void CopyAll() { var text = ObjectPool.AcquireStringBuilder(); for (int i = 0; i < _NewBindingPaths.Count; i++) { text.AppendLine(_NewBindingPaths[i]); } EditorGUIUtility.systemCopyBuffer = text.ReleaseToString(); }
/************************************************************************************************************************/ private static void Visit <T>(T item, Func <T, IEnumerable <T> > getDependancies, List <T> sorted, Dictionary <T, bool> visiting, bool ignoreCycles) { if (item == null) { return; } if (visiting.TryGetValue(item, out var isVisiting)) { if (isVisiting && !ignoreCycles) { // If you found a cyclic dependancy, build it into a string and throw an exception. var text = ObjectPool.AcquireStringBuilder() .Append("Cyclic dependancy found: ") .Append(item); var dependancy = item; do { var dependancies = getDependancies(dependancy); foreach (var otherDependancy in dependancies) { visiting.TryGetValue(otherDependancy, out isVisiting); if (isVisiting) { break; } } text.Append(" -> ").Append(dependancy); }while (!visiting.Comparer.Equals(dependancy, item)); throw new ArgumentException(text.ReleaseToString()); } } else { visiting[item] = true; var dependancies = getDependancies(item); if (dependancies != null) { foreach (var dependancy in dependancies) { Visit(dependancy, getDependancies, sorted, visiting, ignoreCycles); } } visiting[item] = false; sorted.Add(item); } }
/************************************************************************************************************************/ /// <summary> /// Logs a description of the issues found when comparing the properties animated by the `state` to the /// properties that actually exist on the target <see cref="GameObject"/> and its children. /// </summary> public void LogIssues(AnimancerState state, MatchType match) { var animator = state.Root?.Component.Animator; var newMatch = match; var message = ObjectPool.AcquireStringBuilder(); switch (match) { default: case MatchType.Correct: message.Append("No issues were found when comparing the properties animated by '") .Append(state) .Append("' to the Rig of '") .Append(animator.name) .Append("'."); Debug.Log(EditorGUIUtility.systemCopyBuffer = message.ReleaseToString(), animator); break; case MatchType.Empty: message.Append("'") .Append(state) .Append("' does not animate any properties so it will not do anything."); Debug.Log(EditorGUIUtility.systemCopyBuffer = message.ReleaseToString(), animator); break; case MatchType.Warning: message.Append("Some of the properties animated by '") .Append(state) .Append("' do not exist in the Rig of '") .Append(animator.name) .Append("' so they will have no effect."); newMatch = GetMatchType(state, message); Debug.LogWarning(EditorGUIUtility.systemCopyBuffer = message.ReleaseToString(), animator); break; case MatchType.Error: message.Append("None of the properties animated by '") .Append(state) .Append("' exist in the Rig of '") .Append(animator.name) .Append("' so they will have no effect."); newMatch = GetMatchType(state, message); Debug.LogError(EditorGUIUtility.systemCopyBuffer = message.ReleaseToString(), animator); break; } if (newMatch != match) { Debug.LogWarning($"{nameof(MatchType)} changed from {match} to {newMatch}" + " between the initial check and the button press."); } }
private static string GetSelectorLabel(Type fieldType, Type newType) { if (newType == null) { return("Null"); } if (!UseTypeHierarchy) { return(newType.GetNameCS(UseFullNames)); } var label = ObjectPool.AcquireStringBuilder(); if (fieldType.IsInterface)// Interface. { while (true) { if (label.Length > 0) { label.Insert(0, '/'); } var displayType = newType.IsGenericType ? newType.GetGenericTypeDefinition() : newType; label.Insert(0, displayType.GetNameCS(UseFullNames)); newType = newType.BaseType; if (newType == null || !fieldType.IsAssignableFrom(newType)) { break; } } } else// Base Class. { while (true) { if (label.Length > 0) { label.Insert(0, '/'); } label.Insert(0, newType.GetNameCS(UseFullNames)); newType = newType.BaseType; if (newType == null) { break; } if (fieldType.IsAbstract) { if (newType == fieldType) { break; } } else { if (newType == fieldType.BaseType) { break; } } } } return(label.ReleaseToString()); }
/************************************************************************************************************************/ /// <summary> /// Asks the user if they want to modify the target <see cref="Sprite"/>s and calls <see cref="Modify"/> /// on each of them before saving any changes. /// </summary> protected void AskAndApply() { if (!EditorUtility.DisplayDialog("Are You Sure?", AreYouSure + "\n\nThis operation cannot be undone.", "Modify", "Cancel")) { return; } PrepareToApply(); var pathToSprites = new Dictionary <string, List <Sprite> >(); var sprites = Sprites; for (int i = 0; i < sprites.Count; i++) { var sprite = sprites[i]; var path = AssetDatabase.GetAssetPath(sprite); if (!pathToSprites.TryGetValue(path, out var spritesAtPath)) { pathToSprites.Add(path, spritesAtPath = new List <Sprite>()); } spritesAtPath.Add(sprite); } foreach (var asset in pathToSprites) { var importer = (TextureImporter)AssetImporter.GetAtPath(asset.Key); var spriteSheet = importer.spritesheet; var hasError = false; sprites = asset.Value; for (int iSprite = 0; iSprite < sprites.Count; iSprite++) { var sprite = sprites[iSprite]; for (int iSpriteData = 0; iSpriteData < spriteSheet.Length; iSpriteData++) { ref var spriteData = ref spriteSheet[iSpriteData]; if (spriteData.name == sprite.name && spriteData.rect == sprite.rect) { Modify(ref spriteData, sprite); sprites.RemoveAt(iSprite--); if (spriteData.rect.xMin < 0 || spriteData.rect.yMin < 0 || spriteData.rect.xMax >= sprite.texture.width || spriteData.rect.xMax >= sprite.texture.height) { hasError = true; Debug.LogError($"This modification would have put '{sprite.name}' out of bounds," + $" so '{asset.Key}' was not modified."); } break; } } } if (!hasError) { importer.spritesheet = spriteSheet; EditorUtility.SetDirty(importer); importer.SaveAndReimport(); } if (sprites.Count > 0) { var message = ObjectPool.AcquireStringBuilder() .Append("Unable to find data at '") .Append(asset.Key) .Append("' for ") .Append(sprites.Count) .Append(" Sprites:"); for (int i = 0; i < sprites.Count; i++) { message.AppendLine() .Append(" - ") .Append(sprites[i].name); } Debug.LogError(message.ReleaseToString(), importer); } }