Land Explorer in HomeCamp with boot loading, journal UI, and CDN publish tooling.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,173 @@
|
||||
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.";
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user