using System; using System.Collections.Generic; using System.IO; using System.Linq; using AIGame.EditorTools; using Newtonsoft.Json.Linq; using UnityEditor; using UnityEditor.SceneManagement; using UnityEngine; using UnityEngine.SceneManagement; using UnityEngine.UI; /// Pipeline-only migration and verification of the Common nine-slice controls. public static class FixUiNineSlice { static string Repo => Directory.GetParent(Application.dataPath).Parent.FullName; static readonly string[] Names = { "UI_MainHUD", "UI_ConfirmDialog" }; public static string Apply() { if (EditorApplication.isPlayingOrWillChangePlaymode) throw new Exception("Use Edit Mode."); int changed = UiSpriteNineSliceRules.ApplyExisting(); foreach (string name in Names) { string path = Path.Combine(Repo, "Doc/UIPrefabCreater", name + ".json"); if (string.IsNullOrEmpty(JsonUIPrefabBuilder.BuildFromJsonFile(path))) throw new Exception("Prefab build failed: " + name); } return "Updated " + changed + " standard control imports; rebuilt 2 JSON UI prefabs. " + Verify(); } public static string Verify() { int checks = 0, controls = 0, slicedImages = 0, states = 0; Action check = (ok, message) => { if (!ok) throw new Exception(message); checks++; }; var scene = EditorSceneManager.NewPreviewScene(); try { foreach (string guid in AssetDatabase.FindAssets("t:Texture2D", new[] { "Assets/Bundles/UI/Texture/Common" })) { string path = AssetDatabase.GUIDToAssetPath(guid); if (!UiSpriteNineSliceRules.IsStandardControl(path)) continue; controls++; var importer = (TextureImporter)AssetImporter.GetAtPath(path); importer.GetSourceTextureWidthAndHeight(out int width, out int height); var sprite = AssetDatabase.LoadAssetAtPath(path); check(sprite != null && sprite.border == UiSpriteNineSliceRules.GetBorder(path, width, height), "Border mismatch: " + path); var settings = new TextureImporterSettings(); importer.ReadTextureSettings(settings); check(settings.spriteMeshType == SpriteMeshType.FullRect, "Not Full Rect: " + path); check(sprite.border.x > 0 && sprite.border.y > 0 && sprite.border.z > 0 && sprite.border.w > 0, "Empty border: " + path); check(sprite.border.x + sprite.border.z < sprite.rect.width && sprite.border.y + sprite.border.w < sprite.rect.height, "No stretchable center: " + path); var probeGo = new GameObject("NineSliceProbe", typeof(RectTransform), typeof(NineSliceProbe)); SceneManager.MoveGameObjectToScene(probeGo, scene); try { var image = probeGo.GetComponent(); image.sprite = sprite; image.type = Image.Type.Sliced; foreach (Vector2 size in new[] { sprite.rect.size, sprite.rect.size * 2f, sprite.rect.size * new Vector2(3f, 1.25f) }) { image.rectTransform.sizeDelta = size; using (var vh = new VertexHelper()) { image.Populate(vh); var vertices = new List(); vh.GetUIVertexStream(vertices); var x = vertices.Select(v => v.position.x).Distinct().OrderBy(v => v).ToArray(); var y = vertices.Select(v => v.position.y).Distinct().OrderBy(v => v).ToArray(); check(x.Length == 4 && y.Length == 4, "Not a nine-slice mesh: " + path); check(Mathf.Abs(x[1] - x[0] - sprite.border.x) < .01f && Mathf.Abs(x[3] - x[2] - sprite.border.z) < .01f && Mathf.Abs(y[1] - y[0] - sprite.border.y) < .01f && Mathf.Abs(y[3] - y[2] - sprite.border.w) < .01f, "Stretched corner borders: " + path); } } } finally { UnityEngine.Object.DestroyImmediate(probeGo); } } check(controls == 26, "Expected 26 standard control sprites, found " + controls); foreach (string name in Names) { string json = Path.Combine(Repo, "Doc/UIPrefabCreater", name + ".json"); check(JsonUIPrefabBuilder.Validate(json).Ok, "Schema validation failed: " + name); var prefab = AssetDatabase.LoadAssetAtPath("Assets/Bundles/UI/Prefab/" + name + ".prefab"); foreach (var image in prefab.GetComponentsInChildren(true)) { if (image.sprite == null || !UiSpriteNineSliceRules.IsStandardControl(AssetDatabase.GetAssetPath(image.sprite))) continue; slicedImages++; check(image.type == Image.Type.Sliced && image.hasBorder, "Prefab nine-slice missing: " + name + "/" + image.name); } foreach (var button in prefab.GetComponentsInChildren