121 lines
7.3 KiB
C#
121 lines
7.3 KiB
C#
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;
|
|
|
|
/// <summary>Pipeline-only migration and verification of the Common nine-slice controls.</summary>
|
|
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<bool, string> 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<Sprite>(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<NineSliceProbe>();
|
|
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<UIVertex>(); 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<GameObject>("Assets/Bundles/UI/Prefab/" + name + ".prefab");
|
|
foreach (var image in prefab.GetComponentsInChildren<Image>(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<Button>(true))
|
|
foreach (var sprite in new[] { button.spriteState.highlightedSprite, button.spriteState.pressedSprite, button.spriteState.disabledSprite })
|
|
{
|
|
if (sprite == null || !UiSpriteNineSliceRules.IsStandardControl(AssetDatabase.GetAssetPath(sprite))) continue;
|
|
states++;
|
|
check(sprite.border.sqrMagnitude > 0, "Button state border missing: " + button.name);
|
|
}
|
|
}
|
|
// A wrong JSON Image type must fail validation instead of producing a distorted prefab.
|
|
string invalidPath = Path.Combine(Repo, "Tools/output/ui_nine_slice_invalid.json");
|
|
try
|
|
{
|
|
var doc = JObject.Parse(File.ReadAllText(Path.Combine(Repo, "Doc/UIPrefabCreater/UI_ConfirmDialog.json")));
|
|
var comp = doc["nodes"].SelectMany(n => n["components"]).First(c => (string)c["sprite"] == "ui_panel_popup");
|
|
comp["imageType"] = "Simple";
|
|
File.WriteAllText(invalidPath, doc.ToString());
|
|
var validation = JsonUIPrefabBuilder.Validate(invalidPath);
|
|
check(!validation.Ok && validation.Errors.Any(e => e.Contains("imageType: Sliced")), "Wrong Image type was accepted.");
|
|
}
|
|
finally { if (File.Exists(invalidPath)) File.Delete(invalidPath); }
|
|
check(UiSpriteNineSliceRules.ApplyExisting() == 0, "Migration is not idempotent.");
|
|
}
|
|
finally { EditorSceneManager.ClosePreviewScene(scene); }
|
|
var result = new JObject { ["controls"] = controls, ["slicedPrefabImages"] = slicedImages, ["buttonStates"] = states, ["checks"] = checks, ["passed"] = true };
|
|
File.WriteAllText(Path.Combine(Repo, "Tools/output/ui_nine_slice_verification.json"), result.ToString());
|
|
return checks + " checks passed: " + controls + " sprites, " + slicedImages + " sliced prefab images, " + states + " button states; three stretch sizes preserve corner borders.";
|
|
}
|
|
}
|
|
|
|
public sealed class NineSliceProbe : Image
|
|
{
|
|
public void Populate(VertexHelper vh) { base.OnPopulateMesh(vh); }
|
|
}
|