using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using AIGame.EditorTools;
using HotUpdate.UI;
using Newtonsoft.Json.Linq;
using TMPro;
using UnityEditor;
using UnityEditor.SceneManagement;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
using UnityEngine.EventSystems;
using System.Reflection;
using System.Threading.Tasks;
/// Pipeline-only import, JSON-to-Prefab build, isolated rendering and binding checks.
public static class BuildHomeUi
{
static string Repo => Directory.GetParent(Application.dataPath).Parent.FullName;
const string Folder = "Assets/Bundles/UI/Texture/Home";
static readonly string[] Icons = { "coin", "diamond", "energy", "avatar", "tasks", "gift", "mail", "trophy", "bag", "home", "battle", "pets", "chest", "calendar", "settings", "pinball" };
static readonly string[] Names = { "UI_MainHUD" };
static string Design => Path.Combine(Repo, "Doc/Design/UI/Home");
public static string Build()
{
if (EditorApplication.isPlaying) throw new Exception("Build in Edit Mode.");
Directory.CreateDirectory(Folder);
File.Copy(Path.Combine(Design, "home_camp_source.png"), Folder + "/home_camp.png", true);
var atlas = new Texture2D(2, 2, TextureFormat.RGBA32, false);
try
{
if (!atlas.LoadImage(File.ReadAllBytes(Path.Combine(Design, "home_icons_source.png")))) throw new Exception("Invalid icon sheet.");
for (int i = 0; i < Icons.Length; i++)
{
int col = i % 4, row = 3 - i / 4;
int x = Mathf.RoundToInt(col * atlas.width / 4f), y = Mathf.RoundToInt(row * atlas.height / 4f);
int w = Mathf.RoundToInt((col + 1) * atlas.width / 4f) - x;
int h = Mathf.RoundToInt((row + 1) * atlas.height / 4f) - y;
var icon = new Texture2D(w, h, TextureFormat.RGBA32, false);
try { icon.SetPixels(CleanSlice(atlas.GetPixels(x, y, w, h), w, h)); icon.Apply(); File.WriteAllBytes(Folder + "/home_ico_" + Icons[i] + ".png", icon.EncodeToPNG()); }
finally { UnityEngine.Object.DestroyImmediate(icon); }
}
}
finally { UnityEngine.Object.DestroyImmediate(atlas); }
AssetDatabase.Refresh();
foreach (var path in Directory.GetFiles(Folder, "*.png"))
{
var importer = (TextureImporter)AssetImporter.GetAtPath(path);
importer.textureType = TextureImporterType.Sprite;
importer.spriteImportMode = SpriteImportMode.Single;
importer.spritePixelsPerUnit = 100;
var settings = new TextureImporterSettings(); importer.ReadTextureSettings(settings);
settings.spriteMeshType = SpriteMeshType.FullRect; importer.SetTextureSettings(settings);
importer.alphaIsTransparency = true; importer.mipmapEnabled = false;
importer.textureCompression = TextureImporterCompression.Uncompressed;
importer.filterMode = FilterMode.Bilinear; importer.wrapMode = TextureWrapMode.Clamp;
importer.maxTextureSize = 2048; importer.SaveAndReimport();
}
foreach (var name in Names)
{
var path = Path.Combine(Repo, "Doc/UIPrefabCreater", name + ".json");
var validation = JsonUIPrefabBuilder.Validate(path);
if (!validation.Ok) throw new Exception(string.Join("\n", validation.Errors));
if (string.IsNullOrEmpty(JsonUIPrefabBuilder.BuildFromJsonFile(path))) throw new Exception("Build failed: " + name);
}
return "Imported 17 Home sprites; built Candy home HUD prefab from JSON.";
}
// Atlas extraction: discard isolated fragments spilling in from adjacent cells.
// This preserves original icon pixels and alpha; it does not repaint the source artwork.
static Color[] CleanSlice(Color[] pixels, int width, int height)
{
var visited = new bool[pixels.Length];
var components = new List>();
for (int start = 0; start < pixels.Length; start++)
{
if (visited[start] || pixels[start].a < 0.01f) continue;
var queue = new Queue(); var component = new List(); queue.Enqueue(start); visited[start] = true;
while (queue.Count > 0)
{
int p = queue.Dequeue(); component.Add(p);
int px = p % width, py = p / width;
for (int dy = -1; dy <= 1; dy++) for (int dx = -1; dx <= 1; dx++)
{
int nx = px + dx, ny = py + dy;
if (nx < 0 || ny < 0 || nx >= width || ny >= height) continue;
int next = ny * width + nx;
if (visited[next] || pixels[next].a < 0.01f) continue;
visited[next] = true; queue.Enqueue(next);
}
}
components.Add(component);
}
int largest = components.Count > 0 ? components.Max(c => c.Count) : 0;
foreach (var c in components.Where(c => c.Count < largest * 0.03f))
foreach (int p in c) pixels[p] = Color.clear;
return pixels;
}
public static string Verify()
{
int checks = 0;
Action check = (ok, message) => { if (!ok) throw new Exception(message); checks++; };
foreach (var name in Names)
{
var doc = JObject.Parse(File.ReadAllText(Path.Combine(Repo, "Doc/UIPrefabCreater", name + ".json")));
var prefab = AssetDatabase.LoadAssetAtPath("Assets/Bundles/UI/Prefab/" + name + ".prefab");
check(prefab != null, "Missing prefab " + name);
var scene = EditorSceneManager.NewPreviewScene();
var go = UnityEngine.Object.Instantiate(prefab); SceneManager.MoveGameObjectToScene(go, scene);
try
{
foreach (var path in ((JObject)doc["assets"]["sprites"]).Properties())
check(AssetDatabase.LoadAssetAtPath((string)path.Value) != null, "Missing sprite " + path.Name);
var images = go.GetComponentsInChildren(true);
check(images.All(i => i.sprite != null), "Unresolved prefab image " + name);
check(go.GetComponentsInChildren().Single().referenceResolution == new Vector2(1080, 1920), "Wrong resolution");
foreach (var b in doc["bindings"])
check(go.GetComponentsInChildren(true).Any(t => t.name == (string)b), "Missing binding " + b);
check(!go.GetComponentsInChildren(true).Any(t => t.name == "UI_ThemeSwitcher"), "Unexpected theme toolbar");
var view = go.AddComponent(); view.Bind();
string called = null; int invocations = 0, reconnect = 0;
view.OnHomeActionClicked += k => { called = k; invocations++; };
view.OnReconnectClicked += () => reconnect++;
view.Bind(); // Bind is safe to repeat.
foreach (var e in doc["events"])
{
var button = go.GetComponentsInChildren