using System; using System.Collections.Generic; using System.IO; using UnityEditor; using UnityEngine; namespace WeChatWASM { [InitializeOnLoad] public class WXPlayableSettingsHelperInterface { public static WXPlayableSettingsHelper helper = new WXPlayableSettingsHelper(); } public class WXPlayableSettingsHelper { public static string projectRootPath; private static WXPlayableEditorScriptObject config; private static bool m_EnablePerfTool = false; public static bool UseIL2CPP { get { #if TUANJIE_2022_3_OR_NEWER return PlayerSettings.GetScriptingBackend(BuildTargetGroup.WeixinMiniGame) == ScriptingImplementation.IL2CPP; #else return true; #endif } } public WXPlayableSettingsHelper() { projectRootPath = System.IO.Path.GetFullPath(Application.dataPath + "/../"); } public void OnFocus() { loadData(); } public void OnLostFocus() { saveData(); } public void OnDisable() { EditorUtility.SetDirty(config); } private Vector2 scrollRoot; private bool foldBaseInfo = true; private bool foldDebugOptions = true; public void OnSettingsGUI(EditorWindow window) { scrollRoot = EditorGUILayout.BeginScrollView(scrollRoot); GUIStyle linkStyle = new GUIStyle(GUI.skin.label); linkStyle.normal.textColor = Color.yellow; linkStyle.hover.textColor = Color.yellow; linkStyle.stretchWidth = false; linkStyle.alignment = TextAnchor.UpperLeft; linkStyle.wordWrap = true; foldBaseInfo = EditorGUILayout.Foldout(foldBaseInfo, "基本信息"); if (foldBaseInfo) { EditorGUILayout.BeginVertical("frameBox", GUILayout.ExpandWidth(true)); this.formInput("appid", "小游戏试玩AppID"); this.formInput("projectName", "小游戏试玩项目名"); this.formIntPopup("orientation", "游戏方向", new[] { "Portrait", "Landscape" }, new[] { 0, 1, 2, 3 }); this.formInput("memorySize", "UnityHeap预留内存(?)", "单位MB,预分配内存值,超休闲游戏256/中轻度496/重度游戏768,需预估游戏最大UnityHeap值以防止内存自动扩容带来的峰值尖刺。预估方法请查看GIT文档《优化Unity WebGL的内存》"); GUILayout.BeginHorizontal(); string targetDst = "dst"; if (!formInputData.ContainsKey(targetDst)) { formInputData[targetDst] = ""; } EditorGUILayout.LabelField(string.Empty, GUILayout.Width(10)); GUILayout.Label(new GUIContent("导出路径(?)", "支持输入相对于项目根目录的相对路径,如:wxbuild"), GUILayout.Width(140)); formInputData[targetDst] = GUILayout.TextField(formInputData[targetDst], GUILayout.MaxWidth(EditorGUIUtility.currentViewWidth - 270)); if (GUILayout.Button(new GUIContent("打开"), GUILayout.Width(40))) { if (!formInputData[targetDst].Trim().Equals(string.Empty)) { EditorUtility.RevealInFinder(GetAbsolutePath(formInputData[targetDst])); } GUIUtility.ExitGUI(); } if (GUILayout.Button(new GUIContent("选择"), GUILayout.Width(40))) { var dstPath = EditorUtility.SaveFolderPanel("选择你的游戏导出目录", string.Empty, string.Empty); if (dstPath != string.Empty) { formInputData[targetDst] = dstPath; this.saveData(); } GUIUtility.ExitGUI(); } GUILayout.EndHorizontal(); EditorGUILayout.EndVertical(); } foldDebugOptions = EditorGUILayout.Foldout(foldDebugOptions, "调试编译选项"); if (foldDebugOptions) { EditorGUILayout.BeginVertical("frameBox", GUILayout.ExpandWidth(true)); this.formCheckbox("developBuild", "Development Build", "", false, null, OnDevelopmentBuildToggleChanged); this.formCheckbox("il2CppOptimizeSize", "Il2Cpp Optimize Size(?)", "对应于Il2CppCodeGeneration选项,勾选时使用OptimizeSize(默认推荐),生成代码小15%左右,取消勾选则使用OptimizeSpeed。游戏中大量泛型集合的高频访问建议OptimizeSpeed,在使用HybridCLR等第三方组件时只能用OptimizeSpeed。(Dotnet Runtime模式下该选项无效)", !UseIL2CPP); this.formCheckbox("profilingFuncs", "Profiling Funcs"); this.formCheckbox("webgl2", "WebGL2.0"); EditorGUILayout.EndVertical(); } EditorGUILayout.EndScrollView(); } public void OnBuildButtonGUI(EditorWindow window) { GUIStyle linkStyle = new GUIStyle(GUI.skin.label); linkStyle.normal.textColor = Color.yellow; linkStyle.hover.textColor = Color.yellow; linkStyle.stretchWidth = false; linkStyle.alignment = TextAnchor.UpperLeft; linkStyle.wordWrap = true; EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField(string.Empty, GUILayout.MinWidth(10)); if (GUILayout.Button(new GUIContent("生成并转换"), GUILayout.Width(100), GUILayout.Height(25))) { this.saveData(); if (WXPlayableConvertCore.DoExport() == WXConvertCore.WXExportError.SUCCEED) { window.ShowNotification(new GUIContent("转换完成")); } GUIUtility.ExitGUI(); } EditorGUILayout.EndHorizontal(); } private void OnDevelopmentBuildToggleChanged(bool InNewValue) { // 针对non-dev build,取消性能分析工具的集成 if (!InNewValue) { this.setData("enablePerfAnalysis", false); } } private string SDKFilePath; private void loadData() { SDKFilePath = Path.Combine(UnityUtil.GetWxSDKRootPath(), "Runtime", "wechat-playable-default", "unity-sdk", "index.js"); config = UnityUtil.GetPlayableEditorConf(); this.setData("projectName", config.ProjectConf.projectName); this.setData("appid", config.ProjectConf.Appid); this.setData("orientation", (int)config.ProjectConf.Orientation); this.setData("dst", config.ProjectConf.relativeDST); this.setData("developBuild", config.CompileOptions.DevelopBuild); this.setData("il2CppOptimizeSize", config.CompileOptions.Il2CppOptimizeSize); this.setData("profilingFuncs", config.CompileOptions.profilingFuncs); this.setData("webgl2", config.CompileOptions.Webgl2); this.setData("customNodePath", config.CompileOptions.CustomNodePath); this.setData("memorySize", config.ProjectConf.MemorySize.ToString()); } private void saveData() { config.ProjectConf.projectName = this.getDataInput("projectName"); config.ProjectConf.Appid = this.getDataInput("appid"); config.ProjectConf.Orientation = (WXScreenOritation)this.getDataPop("orientation"); config.ProjectConf.relativeDST = this.getDataInput("dst"); config.ProjectConf.DST = GetAbsolutePath(config.ProjectConf.relativeDST); config.CompileOptions.DevelopBuild = this.getDataCheckbox("developBuild"); config.CompileOptions.Il2CppOptimizeSize = this.getDataCheckbox("il2CppOptimizeSize"); config.CompileOptions.profilingFuncs = this.getDataCheckbox("profilingFuncs"); config.CompileOptions.CustomNodePath = this.getDataInput("customNodePath"); config.CompileOptions.Webgl2 = this.getDataCheckbox("webgl2"); config.ProjectConf.MemorySize = int.Parse(this.getDataInput("memorySize")); } private Dictionary formInputData = new Dictionary(); private Dictionary formIntPopupData = new Dictionary(); private Dictionary formCheckboxData = new Dictionary(); private string getDataInput(string target) { if (this.formInputData.ContainsKey(target)) return this.formInputData[target]; return ""; } private int getDataPop(string target) { if (this.formIntPopupData.ContainsKey(target)) return this.formIntPopupData[target]; return 0; } private bool getDataCheckbox(string target) { if (this.formCheckboxData.ContainsKey(target)) return this.formCheckboxData[target]; return false; } private void formCheckbox(string target, string label, string help = null, bool disable = false, Action setting = null, Action onValueChanged = null) { if (!formCheckboxData.ContainsKey(target)) { formCheckboxData[target] = false; } GUILayout.BeginHorizontal(); EditorGUILayout.LabelField(string.Empty, GUILayout.Width(10)); if (help == null) { GUILayout.Label(label, GUILayout.Width(140)); } else { GUILayout.Label(new GUIContent(label, help), GUILayout.Width(140)); } EditorGUI.BeginDisabledGroup(disable); // Toggle the checkbox value based on the disable condition bool newValue = EditorGUILayout.Toggle(disable ? false : formCheckboxData[target]); // Update the checkbox data if the value has changed and invoke the onValueChanged action if (newValue != formCheckboxData[target]) { formCheckboxData[target] = newValue; onValueChanged?.Invoke(newValue); } if (setting != null) { EditorGUILayout.LabelField("", GUILayout.Width(10)); // 配置按钮 if (GUILayout.Button(new GUIContent("设置"), GUILayout.Width(40), GUILayout.Height(18))) { setting?.Invoke(true); } EditorGUILayout.LabelField("", GUILayout.MinWidth(10)); } EditorGUI.EndDisabledGroup(); if (setting == null) EditorGUILayout.LabelField(string.Empty); GUILayout.EndHorizontal(); } private void setData(string target, string value) { if (formInputData.ContainsKey(target)) { formInputData[target] = value; } else { formInputData.Add(target, value); } } private void setData(string target, bool value) { if (formCheckboxData.ContainsKey(target)) { formCheckboxData[target] = value; } else { formCheckboxData.Add(target, value); } } private void setData(string target, int value) { if (formIntPopupData.ContainsKey(target)) { formIntPopupData[target] = value; } else { formIntPopupData.Add(target, value); } } private void formInput(string target, string label, string help = null) { if (!formInputData.ContainsKey(target)) { formInputData[target] = ""; } GUILayout.BeginHorizontal(); EditorGUILayout.LabelField(string.Empty, GUILayout.Width(10)); if (help == null) { GUILayout.Label(label, GUILayout.Width(140)); } else { GUILayout.Label(new GUIContent(label, help), GUILayout.Width(140)); } formInputData[target] = GUILayout.TextField(formInputData[target], GUILayout.MaxWidth(EditorGUIUtility.currentViewWidth - 195)); GUILayout.EndHorizontal(); } private void formIntPopup(string target, string label, string[] options, int[] values) { if (!formIntPopupData.ContainsKey(target)) { formIntPopupData[target] = 0; } GUILayout.BeginHorizontal(); EditorGUILayout.LabelField(string.Empty, GUILayout.Width(10)); GUILayout.Label(label, GUILayout.Width(140)); formIntPopupData[target] = EditorGUILayout.IntPopup(formIntPopupData[target], options, values, GUILayout.MaxWidth(EditorGUIUtility.currentViewWidth - 195)); GUILayout.EndHorizontal(); } public static bool IsAbsolutePath(string path) { // 检查是否为空或空白 if (string.IsNullOrWhiteSpace(path)) { return false; } // 在 Windows 上,检查驱动器字母或网络路径 if (Application.platform == RuntimePlatform.WindowsEditor && Path.IsPathRooted(path)) { return true; } // 在 Unix/Linux 和 macOS 上,检查是否以 '/' 开头 if (Application.platform == RuntimePlatform.OSXEditor && path.StartsWith("/")) { return true; } return false; // 否则为相对路径 } public static string GetAbsolutePath(string path) { if (IsAbsolutePath(path)) { return path; } return Path.Combine(projectRootPath, path); } } }