Files
AIGame/AIClient/AgentScripts/VerifyStartupPresentation.cs

174 lines
10 KiB
C#

using System;
using System.IO;
using System.Linq;
using System.Reflection;
using AIGame.EditorTools;
using AOT.UI;
using Newtonsoft.Json;
using TMPro;
using UnityEditor;
using UnityEditor.SceneManagement;
using UnityEngine;
public static class VerifyStartupPresentation
{
static string Output => Path.GetFullPath(Path.Combine(Application.dataPath, "../../Tools/output/startup_presentation"));
public static string Build() => BuildBootLoading.Build();
public static string Export()
{
if (EditorApplication.isPlaying || BuildPipeline.isBuildingPlayer) throw new Exception("Editor must be idle.");
if (EditorUserBuildSettings.activeBuildTarget != BuildTarget.WebGL) throw new Exception("Expected WebGL target.");
if (BuildBootLoading.ValidateConfigured() != null) throw new Exception(BuildBootLoading.ValidateConfigured());
Directory.CreateDirectory(Output);
string file = Path.Combine(Output,"export.json");
File.WriteAllText(file,JsonConvert.SerializeObject(new {status="building",utc=DateTime.UtcNow}));
try
{
string result = AOT.Editor.WeChat.ExportWeChatBasePackage.Export();
bool passed = result.StartsWith("ok minigame=",StringComparison.Ordinal);
File.WriteAllText(file,JsonConvert.SerializeObject(new {status=passed?"completed":"failed",result,utc=DateTime.UtcNow},Formatting.Indented));
if (!passed) throw new Exception(result);
return result;
}
catch(Exception error)
{
File.WriteAllText(file,JsonConvert.SerializeObject(new {status="failed",error=error.ToString(),utc=DateTime.UtcNow},Formatting.Indented));
throw;
}
}
const string SceneKey = "AIGame.StartupPresentation.Setup";
public static string Prepare()
{
if (EditorApplication.isPlaying) throw new Exception("Stop Play Mode first.");
for (int i=0; i<UnityEngine.SceneManagement.SceneManager.sceneCount; i++)
if (UnityEngine.SceneManagement.SceneManager.GetSceneAt(i).isDirty) throw new Exception("Existing scene has unsaved changes.");
if (!string.IsNullOrEmpty(SessionState.GetString(SceneKey, ""))) throw new Exception("Restore prior preview first.");
SessionState.SetString(SceneKey, JsonConvert.SerializeObject(EditorSceneManager.GetSceneManagerSetup()));
EditorSceneManager.NewScene(NewSceneSetup.EmptyScene, NewSceneMode.Single);
return Resolution(1080,1920,true);
}
public static string Restore()
{
if (EditorApplication.isPlaying) throw new Exception("Stop Play Mode first.");
string saved = SessionState.GetString(SceneKey, "");
if (string.IsNullOrEmpty(saved)) throw new Exception("No scene setup saved.");
EditorSceneManager.RestoreSceneManagerSetup(JsonConvert.DeserializeObject<SceneSetup[]>(saved));
var type = typeof(Editor).Assembly.GetType("UnityEditor.GameView");
var window = EditorWindow.GetWindow(type);
type.GetProperty("selectedSizeIndex", BindingFlags.Instance|BindingFlags.Public|BindingFlags.NonPublic)
.SetValue(window,SessionState.GetInt(SceneKey+".Size",0)); window.Repaint();
SessionState.EraseString(SceneKey);
return "Original scenes and Game View restored.";
}
public static string Phone() => Resolution(1080,2338,false);
public static string Plain() => Resolution(1080,1920,false);
static string Resolution(int width, int height, bool remember)
{
var editor = typeof(Editor).Assembly;
var type = editor.GetType("UnityEditor.GameView");
var window = EditorWindow.GetWindow(type);
var selected = type.GetProperty("selectedSizeIndex",BindingFlags.Instance|BindingFlags.Public|BindingFlags.NonPublic);
if (remember) SessionState.SetInt(SceneKey+".Size",(int)selected.GetValue(window));
var sizesType = editor.GetType("UnityEditor.GameViewSizes");
var sizes = typeof(ScriptableSingleton<>).MakeGenericType(sizesType).GetProperty("instance").GetValue(null);
var group = sizesType.GetMethod("GetGroup").Invoke(sizes,new[]{sizesType.GetProperty("currentGroupType").GetValue(sizes)});
int count = (int)group.GetType().GetMethod("GetTotalCount").Invoke(group,null), index=count;
for (int i=0;i<count;i++)
{
var size = group.GetType().GetMethod("GetGameViewSize").Invoke(group,new object[]{i});
if ((int)size.GetType().GetProperty("width").GetValue(size)==width && (int)size.GetType().GetProperty("height").GetValue(size)==height) { index=i;break; }
}
if(index==count)
{
var size = Activator.CreateInstance(editor.GetType("UnityEditor.GameViewSize"),new object[]{Enum.ToObject(editor.GetType("UnityEditor.GameViewSizeType"),1),width,height,"Startup "+width+"x"+height});
group.GetType().GetMethod("AddCustomSize").Invoke(group,new[]{size});
}
selected.SetValue(window,index);window.Repaint();
return "Preview resolution "+width+"x"+height;
}
public static string Start()
{
if (!EditorApplication.isPlaying) throw new Exception("Enter Play Mode first.");
Application.runInBackground = true;
var camera = new GameObject("StartupPreviewCamera", typeof(Camera)).GetComponent<Camera>();
camera.clearFlags = CameraClearFlags.SolidColor;
camera.backgroundColor = new Color(.54f, .72f, .76f);
camera.cullingMask = 0;
var prefab = AssetDatabase.LoadAssetAtPath<GameObject>(BuildBootLoading.PrefabPath);
var view = BootPatchView.Create(prefab.GetComponent<BootPatchView>());
view.SetStatus("正在布置营地…");
view.SetProgress(.76f);
return "Preview started from the real local prefab; no network requests.";
}
public static string Check()
{
var view = UnityEngine.Object.FindObjectOfType<BootPatchView>();
if (view == null) throw new Exception("Loading preview missing.");
var texts = view.GetComponentsInChildren<TMP_Text>();
foreach (var text in texts)
{
text.ForceMeshUpdate();
if (!text.font.HasCharacters(text.text)) throw new Exception("Missing glyphs: " + text.text);
if (text.isTextOverflowing) throw new Exception("Text overflow: " + text.name);
}
var image = view.transform.Find("Artwork").GetComponent<UnityEngine.UI.Image>();
var canvas = view.GetComponent<RectTransform>();
var art = (RectTransform)image.transform;
if (art.rect.width + 1 < canvas.rect.width || art.rect.height + 1 < canvas.rect.height)
throw new Exception("Artwork does not cover the viewport.");
float ratio = image.sprite.rect.width / image.sprite.rect.height;
if (Mathf.Abs(art.rect.width / art.rect.height - ratio) > .001f) throw new Exception("Artwork stretched.");
var font = texts[0].font;
if (font.atlasPopulationMode != AtlasPopulationMode.Static || font.sourceFontFile != null)
throw new Exception("Boot font must not depend on runtime glyph generation.");
float shown = (float)typeof(BootPatchView).GetField("_shown", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(view);
view.SetProgress(.1f);
float target = (float)typeof(BootPatchView).GetField("_target", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(view);
if (target < .75f) throw new Exception("Progress moved backward.");
Directory.CreateDirectory(Output);
string report = JsonConvert.SerializeObject(new { passed=true, screen=new [] {Screen.width,Screen.height}, chineseGlyphs=true,
noTextOverflow=true, aspectPreserved=true, progressMonotonic=true, shown, target,
splash=PlayerSettings.SplashScreen.show, fontAtlas=new[]{font.atlasWidth,font.atlasHeight},
bootReference=BuildBootLoading.ValidateConfigured() }, Formatting.Indented);
File.WriteAllText(Path.Combine(Output, "validation-" + Screen.width + "x" + Screen.height + ".json"), report);
return report;
}
public static string Complete()
{
var view = UnityEngine.Object.FindObjectOfType<BootPatchView>();
view.SetStatus("准备就绪,即将出发"); view.SetProgress(1f); view.Hide();
return "Fade requested; inspect completion on the following frames.";
}
public static string Capture()
{
var canvas = UnityEngine.Object.FindObjectOfType<BootPatchView>().GetComponent<Canvas>();
var camera = GameObject.Find("StartupPreviewCamera").GetComponent<Camera>();
var mode = canvas.renderMode; var worldCamera = canvas.worldCamera; float plane = canvas.planeDistance;
var previous = RenderTexture.active; int mask = camera.cullingMask;
int width = Screen.width, height = Screen.height;
var target = new RenderTexture(width,height,24);
var pixels = new Texture2D(width,height,TextureFormat.RGB24,false);
string path = Path.Combine(Output,"loading-"+width+"x"+height+".png");
try
{
camera.targetTexture=target; camera.cullingMask=1<<LayerMask.NameToLayer("UI");
canvas.renderMode=RenderMode.ScreenSpaceCamera;canvas.worldCamera=camera;canvas.planeDistance=1;
Canvas.ForceUpdateCanvases();camera.Render();RenderTexture.active=target;
pixels.ReadPixels(new Rect(0,0,width,height),0,0);pixels.Apply();
Directory.CreateDirectory(Output);File.WriteAllBytes(path,pixels.EncodeToPNG());
}
finally
{
RenderTexture.active=previous;camera.targetTexture=null;camera.cullingMask=mask;
canvas.renderMode=mode;canvas.worldCamera=worldCamera;canvas.planeDistance=plane;Canvas.ForceUpdateCanvases();
target.Release();UnityEngine.Object.Destroy(target);UnityEngine.Object.Destroy(pixels);
}
return path;
}
public static string CheckHidden()
{
if (UnityEngine.Object.FindObjectOfType<BootPatchView>() != null) throw new Exception("Fade did not finish.");
return "PASS: full progress and fade completed; loading overlay inactive.";
}
}