Files
AIGame/AIClient/AgentScripts/VerifyHomeCampAdaptationAndEntrances.cs

233 lines
10 KiB
C#

#if UNITY_EDITOR
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using UnityEditor;
using UnityEditor.SceneManagement;
using UnityEngine;
using UnityEngine.UI;
using TMPro;
using HotUpdate.UI;
public static class VerifyHomeCampAdaptationAndEntrances
{
const string ScenePath = "Assets/Bundles/Scenes/Town/HomeCamp/HomeCamp.unity";
const string PrefabPath = "Assets/Bundles/UI/Prefab/UI_MainHUD.prefab";
const float ReferenceAspect = 1080f / 1920f;
static readonly (string name, int w, int h)[] TestResolutions = new[]
{
("Standard 9:16", 1080, 1920),
("Long 9:18", 1080, 2160),
("iPhone 9:19.5", 1080, 2340),
("Android Long 9:20", 1080, 2400),
("Ultra Long 9:22", 1080, 2640),
("Wide 2:3", 1080, 1620),
("Tablet 3:4", 1080, 1440),
};
public static string Run()
{
var sb = new StringBuilder();
sb.AppendLine("=== Automated Verification: HomeCamp Camera Adaptation & Building Entrances ===");
bool allPassed = true;
// 1. Verify UI Prefab & Bindings
sb.AppendLine("\n[Phase 1: UI Prefab & Event Dispatch Verification]");
var prefab = AssetDatabase.LoadAssetAtPath<GameObject>(PrefabPath);
if (prefab == null) return "FAIL: UI_MainHUD.prefab not found";
var hudGo = UnityEngine.Object.Instantiate(prefab);
try
{
var hudView = hudGo.GetComponent<MainHudView>() ?? hudGo.AddComponent<MainHudView>();
var group = hudGo.transform.Find("Group_CampBuildings");
if (group == null)
{
sb.AppendLine("FAIL: Group_CampBuildings not found in UI_MainHUD.prefab");
return sb.ToString();
}
var triggeredKeys = new List<string>();
hudView.OnHomeActionClicked += key => triggeredKeys.Add(key);
hudView.Bind();
var expectedButtons = new (string btnName, string expectedKey, string expectedTitle)[]
{
("Btn_Building_MainHouse", "Camp", "营地大厅"),
("Btn_Building_Shop", "Shop", "小镇商店"),
("Btn_Building_Pets", "Pets", "伙伴之家"),
("Btn_Building_Fountain", "Rewards", "祈愿喷泉"),
("Btn_Building_Stall", "Daily", "集市货摊"),
};
foreach (var (btnName, expectedKey, expectedTitle) in expectedButtons)
{
var btnTf = group.Find(btnName);
if (btnTf == null)
{
sb.AppendLine($"[FAIL] Button {btnName} not found under Group_CampBuildings");
allPassed = false;
continue;
}
var btn = btnTf.GetComponent<Button>();
var tmp = btnTf.GetComponentInChildren<TextMeshProUGUI>();
if (btn == null)
{
sb.AppendLine($"[FAIL] Button component missing on {btnName}");
allPassed = false;
continue;
}
if (tmp == null || tmp.text != expectedTitle)
{
sb.AppendLine($"[FAIL] TextMeshProUGUI mismatch on {btnName}: expected '{expectedTitle}', got '{(tmp != null ? tmp.text : "null")}'");
allPassed = false;
}
// Simulate click
triggeredKeys.Clear();
btn.onClick.Invoke();
if (triggeredKeys.Count == 1 && triggeredKeys[0] == expectedKey)
{
sb.AppendLine($"[PASS] {btnName} clicked -> dispatched key '{expectedKey}', title='{tmp?.text}'");
}
else
{
sb.AppendLine($"[FAIL] {btnName} clicked -> dispatched '{string.Join(",", triggeredKeys)}', expected '{expectedKey}'");
allPassed = false;
}
}
// 2. Verify Scene Camera & Multi-resolution Adaptation
sb.AppendLine("\n[Phase 2: Scene Multi-resolution Adaptation Verification]");
var scene = EditorSceneManager.OpenScene(ScenePath, OpenSceneMode.Additive);
try
{
var root = scene.GetRootGameObjects().FirstOrDefault(g => g.name == "HomeCamp");
if (root == null)
{
sb.AppendLine("FAIL: HomeCamp root not found in " + ScenePath);
return sb.ToString();
}
var camGo = root.transform.Find("HomeCampCamera")?.gameObject;
var camera = camGo?.GetComponent<Camera>();
var backdrop = root.transform.Find("DistantLakeWaterfallCastle");
var ground = root.transform.Find("Ground");
if (camera == null || backdrop == null || ground == null)
{
sb.AppendLine("FAIL: Missing essential scene elements (Camera/Backdrop/Ground)");
return sb.ToString();
}
// Buildings in scene
var buildings = new (string name, Transform tf, Vector3 anchor)[]
{
("MainHouse", root.transform.Find("MainHouse"), new Vector3(0, 6.7f, 7.2f)),
("HouseLeft", root.transform.Find("HouseLeft"), new Vector3(-6.8f, 4.0f, 3.6f)),
("HouseRight", root.transform.Find("HouseRight"), new Vector3(7.5f, 4.2f, 4.0f)),
("Fountain", root.transform.Find("Fountain"), new Vector3(0, 2.5f, 0.8f)),
("StallRight", root.transform.Find("StallRight"), new Vector3(4.5f, 2.6f, 2.8f)),
};
foreach (var b in buildings)
{
if (b.tf == null)
{
sb.AppendLine($"[FAIL] Scene building transform missing: {b.name}");
allPassed = false;
}
}
// Simulate presenter's Resize and Anchoring for each test resolution
var hudRt = hudGo.GetComponent<RectTransform>();
foreach (var res in TestResolutions)
{
float aspect = (float)res.w / res.h;
float adaptedFov = 2f * Mathf.Atan(Mathf.Tan(25f * Mathf.Deg2Rad) * Mathf.Max(1f, ReferenceAspect / aspect)) * Mathf.Rad2Deg;
camera.fieldOfView = adaptedFov;
camera.aspect = aspect;
// Presenter runtime backdrop adaptation
const float dist = 75f;
float frustumHalfH = dist * Mathf.Tan(adaptedFov * Mathf.Deg2Rad * 0.5f);
float frustumHalfW = frustumHalfH * aspect;
float bdW = Mathf.Max(72f, frustumHalfW * 2f * 1.15f);
float bdH = Mathf.Max(56f, frustumHalfH * 1.35f);
float topHeight = frustumHalfH * 1.08f;
backdrop.localScale = new Vector3(bdW, bdH, 1f);
backdrop.position = camera.transform.position + camera.transform.forward * dist + camera.transform.up * (topHeight - bdH * 0.5f);
backdrop.rotation = camera.transform.rotation;
// Check backdrop bounds in camera space
Vector3 bdCamPos = camera.transform.InverseTransformPoint(backdrop.position);
float bdHalfW = backdrop.lossyScale.x * 0.5f;
float bdHalfH = backdrop.lossyScale.y * 0.5f;
float bdMinX = bdCamPos.x - bdHalfW;
float bdMaxX = bdCamPos.x + bdHalfW;
float bdMaxY = bdCamPos.y + bdHalfH;
bool leakLeft = bdMinX > -frustumHalfW;
bool leakRight = bdMaxX < frustumHalfW;
bool leakTop = bdMaxY < frustumHalfH;
if (leakLeft || leakRight || leakTop)
{
sb.AppendLine($"[FAIL] {res.name}: Backdrop Leak! Left={leakLeft}, Right={leakRight}, Top={leakTop}");
allPassed = false;
}
else
{
sb.AppendLine($"[PASS] {res.name} (FOV={adaptedFov:F1}°): Backdrop 100% covers visible frustum (X:[{bdMinX:F1},{bdMaxX:F1}] vs Frustum:[{-frustumHalfW:F1},{frustumHalfW:F1}], Y_top:{bdMaxY:F1} vs Frustum:{frustumHalfH:F1})");
}
// Check building world-to-screen UI anchoring
Vector2 canvasSize = new Vector2(1080f, 1080f / aspect);
foreach (var (bName, bTf, anchor) in buildings)
{
Vector3 worldPos = bTf != null ? bTf.position : anchor;
worldPos.x = anchor.x; worldPos.y = anchor.y; worldPos.z = anchor.z;
Vector3 vp = camera.WorldToViewportPoint(worldPos);
if (vp.z <= 0)
{
sb.AppendLine($"[FAIL] {res.name}: {bName} is behind camera");
allPassed = false;
continue;
}
float x = (vp.x - 0.5f) * canvasSize.x;
float y = (vp.y - 0.5f) * canvasSize.y;
float halfW = canvasSize.x * 0.5f;
float halfH = canvasSize.y * 0.5f;
float clampedX = Mathf.Clamp(x, -halfW + 110f + 15f, halfW - 110f - 15f);
float clampedY = Mathf.Clamp(y, -halfH + 280f, halfH - 180f);
// Ensure coordinates are finite and inside safe canvas
if (float.IsNaN(clampedX) || float.IsNaN(clampedY) || Mathf.Abs(clampedX) > halfW || Mathf.Abs(clampedY) > halfH)
{
sb.AppendLine($"[FAIL] {res.name}: {bName} anchoredPosition invalid: ({clampedX}, {clampedY})");
allPassed = false;
}
}
}
}
finally
{
EditorSceneManager.CloseScene(scene, true);
}
}
finally
{
UnityEngine.Object.DestroyImmediate(hudGo);
}
sb.AppendLine($"\nOverall Result: {(allPassed ? "ALL TESTS PASSED" : "TESTS FAILED")}");
return sb.ToString();
}
}
#endif