Auto-publish WXSDK.

This commit is contained in:
nebulaliu 2024-03-28 14:38:29 +08:00
parent 87d21fc46b
commit 6496cbad05
37 changed files with 2711 additions and 604 deletions

View File

@ -6,6 +6,13 @@ Removed - 删除功能/接口
Fixed - 修复问题
Others - 其他
-->
## 2024-3-28 【普通更新】
* 普通:优化对团结版的导出支持
### Fixed
* 普通兼容PlayDelayed播放
* 普通兼容FMOD2.02版本
* 普通修复FState偶现报错
## 2024-3-5 【普通更新】
* 普通WXAssetBundle支持切换CDN
* 普通优化VideoPlayer组件

View File

@ -0,0 +1,74 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 1795359250, guid: 1cf430f187a0b40eda7f668318d8be23, type: 3}
m_Name: MiniGameConfig
m_EditorClassIdentifier:
ProjectConf:
projectName:
Appid:
CDN:
assetLoadType: 0
compressDataPackage: 0
VideoUrl:
DST:
StreamCDN:
bundleHashLength: 32
bundlePathIdentifier: StreamingAssets;
bundleExcludeExtensions: json;
AssetsUrl:
MemorySize: 256
HideAfterCallMain: 1
preloadFiles:
Orientation: 0
bgImageSrc: Assets/WX-WASM-SDK-V2/Runtime/wechat-default/images/background.jpg
dataFileSubPrefix:
maxStorage: 200
defaultReleaseSize: 31457280
texturesHashLength: 8
texturesPath: Assets/Textures
needCacheTextures: 1
loadingBarWidth: 240
needCheckUpdate: 0
disableHighPerformanceFallback: 0
IOSDevicePixelRatio: 0
SDKOptions:
UseFriendRelation: 0
UseCompressedTexture: 0
UseMiniGameChat: 0
PreloadWXFont: 0
CompileOptions:
DevelopBuild: 0
AutoProfile: 0
ScriptOnly: 0
Il2CppOptimizeSize: 1
profilingFuncs: 1
Webgl2: 0
fbslim: 0
DeleteStreamingAssets: 1
ProfilingMemory: 0
CleanBuild: 0
CustomNodePath:
showMonitorSuggestModal: 1
enableProfileStats: 0
enableRenderAnalysis: 0
iOSAutoGCInterval: 10000
enableIOSPerformancePlus: 0
CompressTexture:
halfSize: 0
useDXT5: 0
bundleSuffix: bundle
parallelWithBundle: 0
bundleDir:
dstMinDir:
debugMode: 0
force: 0
PlayerPrefsKeys: []

View File

@ -1,7 +1,8 @@
fileFormatVersion: 2
guid: 6581700969f174df7a88b1cdb32c1ea4
DefaultImporter:
guid: 8c9a0d25ac0fe41788d270580f6208d9
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

View File

@ -37,30 +37,30 @@ namespace WeChatWASM
{
if (Application.platform == RuntimePlatform.WindowsEditor)
{
return Path.Combine(Application.dataPath, "WX-WASM-SDK-V2/Editor/TextureEditor/Node/astcenc-sse4.1.exe");
return Path.Combine(UnityUtil.GetWxSDKRootPath(), "Editor/TextureEditor/Node/astcenc-sse4.1.exe");
}
if (UnityEngine.SystemInfo.processorType.ToLower().Contains("apple"))
{
return Path.Combine(Application.dataPath, "WX-WASM-SDK-V2/Editor/TextureEditor/Node/astcenc-neon");
return Path.Combine(UnityUtil.GetWxSDKRootPath(), "Editor/TextureEditor/Node/astcenc-neon");
}
return Path.Combine(Application.dataPath, "WX-WASM-SDK-V2/Editor/TextureEditor/Node/astcenc-avx2");
return Path.Combine(UnityUtil.GetWxSDKRootPath(), "Editor/TextureEditor/Node/astcenc-avx2");
}
public static string GetPVRTCPath()
{
return Path.Combine(Application.dataPath, "WX-WASM-SDK-V2/Editor/TextureEditor/Node/PVRTexToolCLI" + (Application.platform == RuntimePlatform.WindowsEditor ? ".exe" : string.Empty));
return Path.Combine(UnityUtil.GetWxSDKRootPath(), "Editor/TextureEditor/Node/PVRTexToolCLI" + (Application.platform == RuntimePlatform.WindowsEditor ? ".exe" : string.Empty));
}
public static string GetDXT5Path()
{
return Path.Combine(Application.dataPath, "WX-WASM-SDK-V2/Editor/TextureEditor/Node/PVRTexToolCLI" + (Application.platform == RuntimePlatform.WindowsEditor ? ".exe" : string.Empty));
return Path.Combine(UnityUtil.GetWxSDKRootPath(), "Editor/TextureEditor/Node/PVRTexToolCLI" + (Application.platform == RuntimePlatform.WindowsEditor ? ".exe" : string.Empty));
}
public static string GetPNGPath()
{
return Path.Combine(Application.dataPath, "WX-WASM-SDK-V2/Editor/TextureEditor/Node/pngquant" + (Application.platform == RuntimePlatform.WindowsEditor ? ".exe" : string.Empty));
return Path.Combine(UnityUtil.GetWxSDKRootPath(), "Editor/TextureEditor/Node/pngquant" + (Application.platform == RuntimePlatform.WindowsEditor ? ".exe" : string.Empty));
}
public static void TestASTC()

View File

@ -24,28 +24,16 @@ namespace WeChatWASM
public static void Init()
{
config = UnityUtil.GetEditorConf();
// SDKFilePath = Path.Combine(Application.dataPath, "WX-WASM-SDK-V2", "Runtime", "wechat-default", "unity-sdk", "index.js");
SDKFilePath = Path.Combine(UnityUtil.GetWxSDKRootPath(), "Runtime", "wechat-default", "unity-sdk", "index.js");
// string templateHeader = (UnityUtil.GetSDKMode() == UnityUtil.SDKMode.Package && UnityUtil.GetEngineVersion() >= UnityUtil.EngineVersion.Tuanjie) ? "PACKAGE:com.qq.wx.minigame:" : "PROJECT:";
string templateHeader = "PROJECT:";
#if PLATFORM_WEIXINMINIGAME
#if TUANJIE_2022_3_OR_NEWER
PlayerSettings.WeixinMiniGame.threadsSupport = false;
PlayerSettings.runInBackground = false;
PlayerSettings.WeixinMiniGame.compressionFormat = WeixinMiniGameCompressionFormat.Disabled;
#if UNITY_2022_3_OR_NEWER
PlayerSettings.WeixinMiniGame.template = $"{templateHeader}WXTemplate2022TJ";
#elif UNITY_2020_1_OR_NEWER
PlayerSettings.WeixinMiniGame.template = $"{templateHeader}WXTemplate2020";
#else
PlayerSettings.WeixinMiniGame.template = $"{templateHeader}WXTemplate";
#endif
PlayerSettings.WeixinMiniGame.linkerTarget = WeixinMiniGameLinkerTarget.Wasm;
PlayerSettings.WeixinMiniGame.dataCaching = false;
#if UNITY_2021_2_OR_NEWER
PlayerSettings.WeixinMiniGame.debugSymbolMode = WeixinMiniGameDebugSymbolMode.External;
#else
PlayerSettings.WeixinMiniGame.debugSymbols = true;
#endif
#else
PlayerSettings.WebGL.threadsSupport = false;
PlayerSettings.runInBackground = false;
@ -79,12 +67,23 @@ namespace WeChatWASM
public static string webglDir = "webgl"; // 导出的webgl目录
public static string miniGameDir = "minigame"; // 生成小游戏的目录
public static string audioDir = "Assets"; // 音频资源目录
public static string frameworkDir = "framework";
public static string dataFileSize = string.Empty;
public static string codeMd5 = string.Empty;
public static string dataMd5 = string.Empty;
private static string SDKFilePath = string.Empty;
public static string defaultImgSrc = "Assets/WX-WASM-SDK-V2/Runtime/wechat-default/images/background.jpg";
public static bool UseIL2CPP
{
get
{
#if TUANJIE_2022_3_OR_NEWER
return PlayerSettings.GetScriptingBackend(BuildTargetGroup.WeixinMiniGame) == ScriptingImplementation.IL2CPP;
#else
return true;
#endif
}
}
// 可以调用这个来集成
public static WXExportError DoExport(bool buildWebGL = true)
{
@ -122,10 +121,26 @@ namespace WeChatWASM
if (WXExtEnvDef.GETDEF("UNITY_2021_2_OR_NEWER") && !config.CompileOptions.DevelopBuild)
{
// 如果是2021版本官方symbols产生有BUG这里需要用工具将函数名提取出来
WeChatWASM.UnityUtil.preprocessSymbols(GetWebGLSymbolPath());
var symFile1 = "";
if(!UseIL2CPP)
{
symFile1 = Path.Combine(config.ProjectConf.DST, webglDir, "Code", "wwwroot", "_framework", "dotnet.native.js.symbols");
}
else
{
var rootPath = Directory.GetParent(Application.dataPath).FullName;
string webglDir = WXExtEnvDef.GETDEF("WEIXINMINIGAME") ? "WeixinMiniGame" : "WebGL";
symFile1 = Path.Combine(rootPath, "Library", "Bee", "artifacts", webglDir, "build", "debug_WebGL_wasm", "build.js.symbols");
}
WeChatWASM.UnityUtil.preprocessSymbols(symFile1, GetWebGLSymbolPath());
// WeChatWASM.UnityUtil.preprocessSymbols(GetWebGLSymbolPath());
}
ConvertCode();
if (!UseIL2CPP)
{
ConvertDotnetCode();
}
string dataFilePath = GetWebGLDataPath();
string wxTextDataDir = WXAssetsTextTools.GetTextMinDataDir();
string dataFilePathBackupDir = $"{wxTextDataDir}{WXAssetsTextTools.DS}slim";
@ -140,7 +155,7 @@ namespace WeChatWASM
}
File.Copy(dataFilePath, dataFilePathBackupPath);
if (config.CompileOptions.fbslim && !IsInstantGameAutoStreaming())
if (UseIL2CPP && config.CompileOptions.fbslim && !IsInstantGameAutoStreaming())
{
WXAssetsTextTools.FirstBundleSlim(dataFilePath, (result, info) =>
{
@ -248,15 +263,257 @@ namespace WeChatWASM
return output.ToString();
}
private static void ConvertDotnetCode()
{
CompressAssemblyBrotli();
ConvertDotnetRuntimeCode();
ConvertDotnetFrameworkCode();
}
private static void ConvertDotnetRuntimeCode()
{
var runtimePath = GetWeixinMiniGameFilePath("jsModuleRuntime")[0];
var dotnetJs = File.ReadAllText(runtimePath, Encoding.UTF8);
Rule[] rules =
{
new Rule()
{
old = "await *WebAssembly\\.instantiate\\(\\w*,",
newStr = $"await WebAssembly.instantiate(Module[\"wasmPath\"],",
},
new Rule()
{
old = "['\"]Expected methodFullName if trace is instrumented['\"]\\);?",
newStr = "'Expected methodFullName if trace is instrumented'); return;",
}
};
foreach (var rule in rules)
{
if (ShowMatchFailedWarning(dotnetJs, rule.old, "runtime") == false) {
dotnetJs = Regex.Replace(dotnetJs, rule.old, rule.newStr);
}
}
File.WriteAllText(Path.Combine(config.ProjectConf.DST, miniGameDir, frameworkDir, Path.GetFileName(runtimePath)), dotnetJs, new UTF8Encoding(false));
}
private static void CompressAssemblyBrotli()
{
GetWeixinMiniGameFilePath("assembly").ToList().ForEach(assembly => UnityUtil.brotli(assembly, assembly + ".br", "8"));
}
private static void ConvertDotnetFrameworkCode()
{
var target = "webgl.wasm.framework.unityweb.js";
var dotnetJsPath =
Path.Combine(config.ProjectConf.DST, webglDir, "Code", "wwwroot", "_framework", "dotnet.js");
var dotnetJs = File.ReadAllText(dotnetJsPath, Encoding.UTF8);
// todo: handle dotnet js
Rule[] rules =
{
new Rule()
{
old = "import\\.meta\\.url",
newStr = $"pathToFileURL('/{frameworkDir}/webgl.wasm.framework.unityweb.js')",
},
new Rule()
{
old = " *(\\w*) *= *(\\w*)\\(\"js-module-runtime\"\\) *, *(\\w*) *= *(\\w*)\\(\"js-module-native\"\\)",
newStr = $" $1 = $2(\"js-module-runtime\"), $3 = $4(\"js-module-native\"); $1.resolvedUrl = \"{Path.GetFileName(GetWeixinMiniGameFilePath("jsModuleRuntime")[0])}\";$3.resolvedUrl = \"{Path.GetFileName(GetWeixinMiniGameFilePath("jsModuleNative")[0])}\";",
},
new Rule()
{
old = "export *\\{ *(.*) *as *default *,(.*) *as *dotnet *,(.*) *as *exit *\\} *;",
newStr = "return {legacyEntrypoint: $1, dotnet: $2, exit: $3}",
},
new Rule()
{ // add symbol property for monoToBlazorAssetTypeMap
old = "\"js-module-runtime\": *\"dotnetjs\", *\"js-module-threads\": *\"dotnetjs\"",
newStr = "\"js-module-runtime\": \"dotnetjs\", \"js-module-threads\": \"dotnetjs\", \"symbols\": \"symbols\"",
},
new Rule()
{
old = "_e *\\|\\| *\"function\" *== *typeof *globalThis.URL[\\s\\S]*asm-features\"\\);",
newStr = ""
},
new Rule()
{
old = "if *\\(!\\(ENVIRONMENT_IS_SHELL *\\|\\| *typeof *globalThis.URL *=== *\"function\"\\)\\)[\\s\\S]*BigInt64Array *API. *Please *use *a *modern *version. *See *also *https:\\/\\/aka.ms\\/dotnet\\-wasm\\-features\"\\);",
newStr = ""
}
};
foreach (var rule in rules)
{
if (ShowMatchFailedWarning(dotnetJs, rule.old, "dotnet") == false) {
dotnetJs = Regex.Replace(dotnetJs, rule.old, rule.newStr);
}
}
var header = @"WebAssembly.validate = () => true;
const dotnet = (function () {";
var footer = @"})();
function pathToFileURL(path) {
return `file://${path}`;
}
const wxFetchFile = async (url, config) => new Promise((resolve, reject) => {
const folderPath = `${GameGlobal.manager.PLUGIN_CACHE_PATH}/${config.hash}`;
const filePath = `${folderPath}/${config.filename}`;
const fs = wx.getFileSystemManager();
var readFile = fs.readFile;
const readFileInfo = {
filePath: filePath,
success: async (res) => {
resolve(res.data);
},
fail: (err) => {
reject(err);
}
}
if (config.brotli) {
readFile = fs.readCompressedFile;
readFileInfo.compressionAlgorithm = 'br';
}
const downloadFileInfo = {
url: url,
success: (res) => {
if (res.statusCode === 200) {
try {
fs.accessSync(folderPath);
} catch (e) {
fs.mkdirSync(folderPath, true);
}
saveFile(res.tempFilePath, filePath, res.dataLength);
} else {
reject(res);
}
},
fail: (err) => {
reject(err);
}
};
const saveFile = (tempFilePath, filePath, dataLength, retry = 0) => {
try {
fs.saveFileSync(tempFilePath, filePath);
readFile(readFileInfo);
} catch (e) {
if (e.message === 'saveFileSync:fail exceeded the maximum size of the file storage limit') {
if (retry < 3) {
GameGlobal.manager.cleanCache(dataLength).then(() => {
saveFile(tempFilePath, filePath, dataLength, ++retry);
});
} else {
GameGlobal.manager.cleanAllCache().then(() => {
wx.downloadFile(downloadFileInfo);
});
}
} else {
reject(e);
}
}
};
// if not use cache or cache not exists, download file
fs.access({
path: filePath,
success: (res) => {
if (config.cache && res.errMsg === 'access:ok') {
readFile(readFileInfo);
} else {
wx.downloadFile(downloadFileInfo);
}
},
fail: (err) => {
wx.downloadFile(downloadFileInfo);
}
});
});
dotnet.dotnet.withResourceLoader((resourceType, assetName, url, requestHash, behavior) => {
const remoteUrl = `${GameGlobal.unityNamespace.DATA_CDN}Code/wwwroot/_framework/${assetName}`;
switch (resourceType) {
case 'dotnetjs':
return assetName;
case 'manifest':
return new Promise((resolve, reject) => {
wx.request({
url: remoteUrl,
success: (res) => {
resolve({
json: async () => res.data,
headers: {
get: () => undefined
}
});
},
fail: (err) => {
reject(err);
}
});
});
case 'symbols':
case 'dotnetwasm':
return new Promise((resolve, reject) => {
resolve({
arrayBuffer: async () => undefined,
status: 200,
ok: true
})
});
case 'assembly':
const config = {
filename: assetName + '.br',
cache: true,
hash: requestHash,
brotli: true
};
return wxFetchFile(remoteUrl + '.br', config).then(res => ({
arrayBuffer: async () => res,
status: 200,
ok: true,
}));
default:
return url;
}
});
var executed = false;
var unityFramework = function (module) {
if (executed) return;
executed = true;
module['noInitialRun'] = true;
return (dotnet.legacyEntrypoint(module)).then(() => {
module['callMain']();
});
};
if (typeof exports === 'object' && typeof module === 'object') module.exports = unityFramework;
else if (typeof define === 'function' && define['amd']) define([], function () {
return unityFramework;
});
else if (typeof exports === 'object') exports['unityFramework'] = unityFramework;
GameGlobal.unityNamespace.UnityModule = unityFramework;";
File.WriteAllText(Path.Combine(config.ProjectConf.DST, miniGameDir, frameworkDir, target), header + dotnetJs + footer, new UTF8Encoding(false));
}
private static void ConvertCode()
{
UnityEngine.Debug.LogFormat("[Converter] Starting to adapt framewor. Dst: " + config.ProjectConf.DST);
UnityUtil.DelectDir(Path.Combine(config.ProjectConf.DST, miniGameDir));
string text = String.Empty;
var target = "webgl.wasm.framework.unityweb.js";
if (WXExtEnvDef.GETDEF("UNITY_2020_1_OR_NEWER"))
{
text = File.ReadAllText(Path.Combine(config.ProjectConf.DST, webglDir, "Build", "webgl.framework.js"), Encoding.UTF8);
if (UseIL2CPP)
{
text = File.ReadAllText(Path.Combine(config.ProjectConf.DST, webglDir, "Build", "webgl.framework.js"), Encoding.UTF8);
}
else
{
var frameworkPath = GetWeixinMiniGameFilePath("jsModuleNative")[0];
target = Path.GetFileName(frameworkPath);
text = File.ReadAllText(frameworkPath, Encoding.UTF8);
}
}
else
{
@ -266,7 +523,10 @@ namespace WeChatWASM
for (i = 0; i < ReplaceRules.rules.Length; i++)
{
var rule = ReplaceRules.rules[i];
text = Regex.Replace(text, rule.old, rule.newStr);
// text = Regex.Replace(text, rule.old, rule.newStr);
if (ShowMatchFailedWarning(text, rule.old, "WXReplaceRules") == false) {
text = Regex.Replace(text, rule.old, rule.newStr);
}
}
string[] prefixs =
{
@ -318,7 +578,7 @@ namespace WeChatWASM
{
text += ";GameGlobal.unityNamespace.UnityModule = tuanjieFramework;";
}
else
else if (UseIL2CPP)
{
if (text.StartsWith("(") && text.EndsWith(")"))
{
@ -333,7 +593,12 @@ namespace WeChatWASM
Directory.CreateDirectory(Path.Combine(config.ProjectConf.DST, miniGameDir));
}
var header = "function createWebAudio(){window.AudioContext=window.AudioContext||window.webkitAudioContext;if(window.AudioContext){return new AudioContext();}return wx.createWebAudioContext();}\n";
if (!Directory.Exists(Path.Combine(config.ProjectConf.DST, miniGameDir, frameworkDir)))
{
Directory.CreateDirectory(Path.Combine(config.ProjectConf.DST, miniGameDir, frameworkDir));
}
var header = "var OriginalAudioContext = window.AudioContext || window.webkitAudioContext;window.AudioContext = function() {if (this instanceof window.AudioContext) {return wx.createWebAudioContext();} else {return new OriginalAudioContext();}};";
if (config.CompileOptions.DevelopBuild)
{
@ -346,8 +611,50 @@ namespace WeChatWASM
}
text = header + text;
if (!UseIL2CPP)
{
Rule[] nativeRules =
{
new Rule()
{
old = "if\\(Module\\.IsWxGame\\)return Math\\.random\\(\\)\\*256\\|0;abort\\(\"randomDevice\"\\)",
newStr = "{if(Module.IsWxGame)return Math.random()*256|0;abort(\"randomDevice\")}"
},
new Rule()
{
old = "var *_scriptDir *= *import\\.meta\\.url;",
newStr = $"var _scriptDir = \"file:///framework/webgl.wasm.framework.unityweb.js\"",
},
new Rule()
{
old = "import\\.meta\\.url",
newStr = "_scriptDir"
},
new Rule()
{
old = "Module\\[['\"](HEAP[U,F,1-9]*)['\"]\\] *=",
newStr = "Module['$1'] = GameGlobal.unityNamespace.Module['$1'] =",
},
new Rule()
{
old = "return *handleException\\(e\\);?",
newStr = "return handleException(e);} finally {if (ABORT === true) return; if (Module.calledMainCb) Module.calledMainCb(); if (GameGlobal.unityNamespace.enableProfileStats) {setTimeout(() => {SendMessage('WXSDKManagerHandler', 'OpenProfileStats');}, 10000);}"
},
new Rule()
{
old = "function *callMain\\(args *= *\\[\\]\\)",
newStr = "Object.keys(Module).forEach(key=>{if(!(key in Object.keys(GameGlobal.unityNamespace.Module))){GameGlobal.unityNamespace.Module[key]=Module[key];}});function callMain(args = [])"
},
};
foreach (var rule in nativeRules)
{
if (ShowMatchFailedWarning(text, rule.old, "native") == false) {
text = Regex.Replace(text, rule.old, rule.newStr);
}
}
}
File.WriteAllText(Path.Combine(config.ProjectConf.DST, miniGameDir, "webgl.wasm.framework.unityweb.js"), text, new UTF8Encoding(false));
File.WriteAllText(Path.Combine(config.ProjectConf.DST, miniGameDir, target), text, new UTF8Encoding(false));
UnityEngine.Debug.LogFormat("[Converter] adapt framework done! ");
}
@ -358,7 +665,8 @@ namespace WeChatWASM
PlayerSettings.WeixinMiniGame.emscriptenArgs = string.Empty;
if (WXExtEnvDef.GETDEF("UNITY_2021_2_OR_NEWER"))
{
PlayerSettings.WeixinMiniGame.emscriptenArgs += " -s EXPORTED_FUNCTIONS=_main,_sbrk,_emscripten_stack_get_base,_emscripten_stack_get_end";
// PlayerSettings.WeixinMiniGame.emscriptenArgs += " -s EXPORTED_FUNCTIONS=_main,_sbrk,_emscripten_stack_get_base,_emscripten_stack_get_end";
PlayerSettings.WeixinMiniGame.emscriptenArgs += " -s EXPORTED_FUNCTIONS=_sbrk,_emscripten_stack_get_base,_emscripten_stack_get_end";
}
#else
@ -518,6 +826,13 @@ namespace WeChatWASM
}
}
private static string[] GetWeixinMiniGameFilePath(string key)
{
var bootJson = Path.Combine(config.ProjectConf.DST, webglDir, "Code", "wwwroot", "_framework", "blazor.boot.json");
var boot = JsonMapper.ToObject(File.ReadAllText(bootJson, Encoding.UTF8));
return boot["resources"][key].Keys.Select(file => Path.Combine(config.ProjectConf.DST, webglDir, "Code", "wwwroot", "_framework", file)).ToArray();
}
private static void finishExport()
{
int code = GenerateBinFile();
@ -692,7 +1007,14 @@ namespace WeChatWASM
{
if (WXExtEnvDef.GETDEF("UNITY_2020_1_OR_NEWER"))
{
return Path.Combine(config.ProjectConf.DST, webglDir, "Build", "webgl.wasm");
if (UseIL2CPP)
{
return Path.Combine(config.ProjectConf.DST, webglDir, "Build", "webgl.wasm");
}
else
{
return GetWeixinMiniGameFilePath("wasmNative")[0];
}
}
else
{
@ -1073,6 +1395,7 @@ namespace WeChatWASM
IsInstantGameAutoStreaming() ? "true" : "false",
(config.CompileOptions.DevelopBuild && config.CompileOptions.enableRenderAnalysis) ? "true" : "false",
config.ProjectConf.IOSDevicePixelRatio.ToString(),
UseIL2CPP ? "" : "/framework",
});
List<Rule> replaceList = new List<Rule>(replaceArrayList);
@ -1238,6 +1561,15 @@ namespace WeChatWASM
return "";
#endif
}
public static bool ShowMatchFailedWarning(string text, string rule, string file)
{
if (Regex.IsMatch(text, rule) == false) {
Debug.LogWarning($"UnMatched {file} rule: {rule}");
return true;
}
return false;
}
}
}

View File

@ -0,0 +1,605 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using System.IO;
using static WeChatWASM.WXConvertCore;
using System;
using System.Reflection;
namespace WeChatWASM
{
[InitializeOnLoad]
public class WXSettingsHelperInterface
{
public static WXSettingsHelper helper = new WXSettingsHelper();
}
public class WXSettingsHelper
{
public WXSettingsHelper()
{
Type weixinMiniGamePackageHelpersType = Type.GetType("UnityEditor.WeixinPackageHelpers,UnityEditor");
if (weixinMiniGamePackageHelpersType == null)
{
weixinMiniGamePackageHelpersType = Type.GetType("UnityEditor.WeixinMiniGamePackageHelpers,UnityEditor");
}
if (weixinMiniGamePackageHelpersType != null)
{
EventInfo onSettingsGUIEvent = weixinMiniGamePackageHelpersType.GetEvent("OnPackageSettingsGUI");
EventInfo onPackageFocusEvent = weixinMiniGamePackageHelpersType.GetEvent("OnPackageFocus");
EventInfo onPackageLostFocusEvent = weixinMiniGamePackageHelpersType.GetEvent("OnPackageLostFocus");
EventInfo onBuildButtonGUIEvent = weixinMiniGamePackageHelpersType.GetEvent("OnPackageBuildButtonGUI");
if (onPackageFocusEvent != null)
{
onPackageFocusEvent.AddEventHandler(null, new Action(OnFocus));
}
if (onPackageLostFocusEvent != null)
{
onPackageLostFocusEvent.AddEventHandler(null, new Action(OnLostFocus));
}
if (onSettingsGUIEvent != null)
{
onSettingsGUIEvent.AddEventHandler(null, new Action<EditorWindow>(OnSettingsGUI));
}
if (onBuildButtonGUIEvent != null)
{
onBuildButtonGUIEvent.AddEventHandler(null, new Action<EditorWindow>(OnBuildButtonGUI));
}
}
loadData();
foldInstantGame = WXConvertCore.IsInstantGameAutoStreaming();
}
private static WXEditorScriptObject config = UnityUtil.GetEditorConf();
public void OnFocus()
{
loadData();
}
public void OnLostFocus()
{
saveData();
}
public void OnDisable()
{
EditorUtility.SetDirty(config);
}
public Texture tex;
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("cdn", "游戏资源CDN");
this.formInput("projectName", "小游戏项目名");
this.formIntPopup("orientation", "游戏方向", new[] { "Portrait", "Landscape", "LandscapeLeft", "LandscapeRight" }, 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("导出路径", 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(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();
}
foldLoadingConfig = EditorGUILayout.Foldout(foldLoadingConfig, "启动Loading配置");
if (foldLoadingConfig)
{
EditorGUILayout.BeginVertical("frameBox", GUILayout.ExpandWidth(true));
GUILayout.BeginHorizontal();
string targetBg = "bgImageSrc";
EditorGUILayout.LabelField(string.Empty, GUILayout.Width(10));
tex = (Texture)EditorGUILayout.ObjectField("启动背景图/视频封面", tex, typeof(Texture2D), false);
var currentBgSrc = AssetDatabase.GetAssetPath(tex);
if (!string.IsNullOrEmpty(currentBgSrc) && currentBgSrc != this.formInputData[targetBg])
{
this.formInputData[targetBg] = currentBgSrc;
this.saveData();
}
GUILayout.EndHorizontal();
this.formInput("videoUrl", "加载阶段视频URL");
this.formIntPopup("assetLoadType", "首包资源加载方式", new[] { "CDN", "小游戏包内" }, new[] { 0, 1 });
this.formCheckbox("compressDataPackage", "压缩首包资源(?)", "将首包资源Brotli压缩, 降低资源大小. 注意: 首次启动耗时可能会增加200ms, 仅推荐使用小游戏分包加载时节省包体大小使用");
this.formInput("bundleExcludeExtensions", "不自动缓存文件类型(?)", "(使用;分割)当请求url包含资源'cdn+StreamingAssets'时会自动缓存但StreamingAssets目录下不是所有文件都需缓存此选项配置不需要自动缓存的文件拓展名。默认值json");
this.formInput("bundleHashLength", "Bundle名称Hash长度(?)", "自定义Bundle文件名中hash部分长度默认值32用于缓存控制。");
this.formInput("preloadFiles", "预下载文件列表(?)", "使用;间隔,支持模糊匹配");
EditorGUILayout.EndVertical();
}
foldSDKOptions = EditorGUILayout.Foldout(foldSDKOptions, "SDK功能选项");
if (foldSDKOptions)
{
EditorGUILayout.BeginVertical("frameBox", GUILayout.ExpandWidth(true));
this.formCheckbox("useFriendRelation", "使用好友关系链");
this.formCheckbox("useMiniGameChat", "使用社交组件");
this.formCheckbox("preloadWXFont", "预加载微信字体(?)", "在game.js执行开始时预载微信系统字体运行期间可使用WX.GetWXFont获取微信字体");
EditorGUILayout.EndVertical();
}
foldDebugOptions = EditorGUILayout.Foldout(foldDebugOptions, "调试编译选项");
if (foldDebugOptions)
{
EditorGUILayout.BeginVertical("frameBox", GUILayout.ExpandWidth(true));
this.formCheckbox("developBuild", "Development Build");
this.formCheckbox("autoProfile", "Auto connect Profiler");
this.formCheckbox("scriptOnly", "Scripts Only Build");
this.formCheckbox("il2CppOptimizeSize", "Il2Cpp Optimize Size(?)", "对应于Il2CppCodeGeneration选项勾选时使用OptimizeSize(默认推荐)生成代码小15%左右取消勾选则使用OptimizeSpeed。游戏中大量泛型集合的高频访问建议OptimizeSpeed在使用HybridCLR等第三方组件时只能用OptimizeSpeed。(Dotnet Runtime模式下该选项无效)", !UseIL2CPP);
this.formCheckbox("profilingFuncs", "Profiling Funcs");
this.formCheckbox("profilingMemory", "Profiling Memory");
this.formCheckbox("webgl2", "WebGL2.0(beta)");
this.formCheckbox("iOSPerformancePlus", "iOSPerformancePlus(?)", "是否使用iOS高性能+渲染方案有助于提升渲染兼容性、降低WebContent进程内存");
this.formCheckbox("deleteStreamingAssets", "Clear Streaming Assets");
this.formCheckbox("cleanBuild", "Clean WebGL Build");
// this.formCheckbox("cleanCloudDev", "Clean Cloud Dev");
this.formCheckbox("fbslim", "首包资源优化(?)", "导出时自动清理UnityEditor默认打包但游戏项目从未使用的资源瘦身首包资源体积建议所有游戏启用。(Dotnet Runtime模式下该选项暂不支持)", !UseIL2CPP, (res) =>
{
var fbWin = EditorWindow.GetWindow(typeof(WXFbSettingWindow), false, "首包资源优化配置面板", true);
fbWin.minSize = new Vector2(680, 350);
fbWin.Show();
});
this.formCheckbox("showMonitorSuggestModal", "显示优化建议弹窗");
this.formCheckbox("enableProfileStats", "显示性能面板");
this.formCheckbox("enableRenderAnalysis", "显示渲染日志(dev only)");
EditorGUILayout.EndVertical();
}
#if UNITY_INSTANTGAME
foldInstantGame = EditorGUILayout.Foldout(foldInstantGame, "Instant Game - AutoStreaming");
if (foldInstantGame)
{
EditorGUILayout.BeginVertical("frameBox", GUILayout.ExpandWidth(true));
this.formInput("bundlePathIdentifier", "Bundle Path Identifier");
this.formInput("dataFileSubPrefix", "Data File Sub Prefix");
EditorGUI.BeginDisabledGroup(true);
this.formCheckbox("autoUploadFirstBundle", "构建后自动上传首包(?)", "仅在开启AutoStreaming生效", true);
EditorGUI.EndDisabledGroup();
GUILayout.BeginHorizontal();
EditorGUILayout.LabelField(string.Empty, GUILayout.Width(10));
GUILayout.Label(new GUIContent("清理AS配置(?)", "如需关闭AutoStreaming选用默认发布方案则需要清理AS配置项目。"), GUILayout.Width(140));
EditorGUI.BeginDisabledGroup(WXConvertCore.IsInstantGameAutoStreaming());
if(GUILayout.Button(new GUIContent("恢复"),GUILayout.Width(60))){
string identifier = config.ProjectConf.bundlePathIdentifier;
string[] identifiers = identifier.Split(";");
string idStr = "";
foreach (string id in identifiers)
{
if (id != "AS" && id != "CUS/CustomAB")
{
idStr += id + ";";
}
}
config.ProjectConf.bundlePathIdentifier = idStr.Trim(';');
if (config.ProjectConf.dataFileSubPrefix == "CUS")
{
config.ProjectConf.dataFileSubPrefix = "";
}
this.loadData();
}
EditorGUI.EndDisabledGroup();
GUILayout.EndHorizontal();
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField(string.Empty);
if (GUILayout.Button(new GUIContent("了解Instant Game AutoStreaming", ""), linkStyle))
{
Application.OpenURL("https://github.com/wechat-miniprogram/minigame-unity-webgl-transform/blob/main/Design/InstantGameGuide.md");
}
EditorGUILayout.EndHorizontal();
EditorGUILayout.EndVertical();
}
#endif
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();
if (GUILayout.Button(new GUIContent("更多配置项"), GUILayout.Width(100), GUILayout.Height(25)))
{
var config = AssetDatabase.LoadAssetAtPath<UnityEngine.Object>("Assets/WX-WASM-SDK-V2/Editor/MiniGameConfig.asset");
Selection.activeObject = config;
GUIUtility.ExitGUI();
}
if (GUILayout.Button(new GUIContent("WebGL转小游戏(不常用)"), GUILayout.Width(150), GUILayout.Height(25)))
{
this.saveData();
if (WXConvertCore.DoExport(false) == WXConvertCore.WXExportError.SUCCEED)
{
window.ShowNotification(new GUIContent("转换完成"));
}
GUIUtility.ExitGUI();
}
EditorGUILayout.LabelField(string.Empty, GUILayout.MinWidth(10));
if (GUILayout.Button(new GUIContent("生成并转换"), GUILayout.Width(100), GUILayout.Height(25)))
{
this.saveData();
if (WXConvertCore.DoExport() == WXConvertCore.WXExportError.SUCCEED)
{
if (!WXConvertCore.IsInstantGameAutoStreaming())
window.ShowNotification(new GUIContent("转换完成"));
else
{
#if (UNITY_WEBGL || WEIXINMINIGAME) && UNITY_INSTANTGAME
// 上传首包资源
if (!string.IsNullOrEmpty(WXConvertCore.FirstBundlePath) && File.Exists(WXConvertCore.FirstBundlePath))
{
if (Unity.InstantGame.IGBuildPipeline.UploadWeChatDataFile(WXConvertCore.FirstBundlePath))
{
Debug.Log("转换完成并成功上传首包资源");
window.ShowNotification(new GUIContent("转换完成并成功上传"));
}
else
{
Debug.LogError("首包资源上传失败请检查网络以及Auto Streaming配置是否正确。");
window.ShowNotification(new GUIContent("上传失败"));
}
}
else
{
Debug.LogError("转换失败");
window.ShowNotification(new GUIContent("转换失败"));
}
#else
window.ShowNotification(new GUIContent("转换完成"));
#endif
}
}
GUIUtility.ExitGUI();
}
EditorGUILayout.EndHorizontal();
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField(string.Empty);
if (GUILayout.Button(new GUIContent("了解如何实现自定义构建", ""), linkStyle))
{
Application.OpenURL("https://github.com/wechat-miniprogram/minigame-unity-webgl-transform/blob/main/Design/DevelopmentQAList.md#13%E5%A6%82%E4%BD%95%E8%87%AA%E5%AE%9A%E4%B9%89%E6%8E%A5%E5%85%A5%E6%9E%84%E5%BB%BA%E6%B5%81%E7%A8%8B");
GUIUtility.ExitGUI();
}
EditorGUILayout.EndHorizontal();
}
private Vector2 scrollRoot;
private bool foldBaseInfo = true;
private bool foldLoadingConfig = true;
private bool foldSDKOptions = true;
private bool foldDebugOptions = true;
private bool foldInstantGame = false;
private Dictionary<string, string> formInputData = new Dictionary<string, string>();
private Dictionary<string, int> formIntPopupData = new Dictionary<string, int>();
private Dictionary<string, bool> formCheckboxData = new Dictionary<string, bool>();
private string SDKFilePath;
private void addBundlePathIdentifier(string value)
{
string identifier = config.ProjectConf.bundlePathIdentifier;
if (identifier[identifier.Length - 1] != ';')
{
identifier += ";";
}
identifier += value;
config.ProjectConf.bundlePathIdentifier = identifier;
}
private void loadData()
{
// SDKFilePath = Path.Combine(Application.dataPath, "WX-WASM-SDK-V2", "Runtime", "wechat-default", "unity-sdk", "index.js");
SDKFilePath = Path.Combine(UnityUtil.GetWxSDKRootPath(), "Runtime", "wechat-default", "unity-sdk", "index.js");
config = UnityUtil.GetEditorConf();
// Instant Game
if (WXConvertCore.IsInstantGameAutoStreaming())
{
config.ProjectConf.CDN = WXConvertCore.GetInstantGameAutoStreamingCDN();
string identifier = config.ProjectConf.bundlePathIdentifier;
string[] identifiers = identifier.Split(';');
bool AS = false;
bool CUS = false;
foreach (string id in identifiers)
{
if (id == "AS")
{
AS = true;
}
if (id == "CUS/CustomAB")
{
CUS = true;
}
}
if (!AS)
{
this.addBundlePathIdentifier("AS");
}
if (!CUS)
{
this.addBundlePathIdentifier("CUS/CustomAB");
}
if (config.ProjectConf.dataFileSubPrefix != "CUS")
{
config.ProjectConf.dataFileSubPrefix = "CUS";
}
}
this.setData("projectName", config.ProjectConf.projectName);
this.setData("appid", config.ProjectConf.Appid);
this.setData("cdn", config.ProjectConf.CDN);
this.setData("assetLoadType", config.ProjectConf.assetLoadType);
this.setData("compressDataPackage", config.ProjectConf.compressDataPackage);
this.setData("videoUrl", config.ProjectConf.VideoUrl);
this.setData("orientation", (int)config.ProjectConf.Orientation);
this.setData("dst", config.ProjectConf.DST);
this.setData("bundleHashLength", config.ProjectConf.bundleHashLength.ToString());
this.setData("bundlePathIdentifier", config.ProjectConf.bundlePathIdentifier);
this.setData("bundleExcludeExtensions", config.ProjectConf.bundleExcludeExtensions);
this.setData("preloadFiles", config.ProjectConf.preloadFiles);
this.setData("developBuild", config.CompileOptions.DevelopBuild);
this.setData("autoProfile", config.CompileOptions.AutoProfile);
this.setData("scriptOnly", config.CompileOptions.ScriptOnly);
this.setData("il2CppOptimizeSize", config.CompileOptions.Il2CppOptimizeSize);
this.setData("profilingFuncs", config.CompileOptions.profilingFuncs);
this.setData("profilingMemory", config.CompileOptions.ProfilingMemory);
this.setData("deleteStreamingAssets", config.CompileOptions.DeleteStreamingAssets);
this.setData("cleanBuild", config.CompileOptions.CleanBuild);
this.setData("customNodePath", config.CompileOptions.CustomNodePath);
this.setData("webgl2", config.CompileOptions.Webgl2);
this.setData("iOSPerformancePlus", config.CompileOptions.enableIOSPerformancePlus);
this.setData("fbslim", config.CompileOptions.fbslim);
this.setData("useFriendRelation", config.SDKOptions.UseFriendRelation);
this.setData("useMiniGameChat", config.SDKOptions.UseMiniGameChat);
this.setData("preloadWXFont", config.SDKOptions.PreloadWXFont);
this.setData("bgImageSrc", config.ProjectConf.bgImageSrc);
tex = AssetDatabase.LoadAssetAtPath<Texture>(config.ProjectConf.bgImageSrc);
this.setData("memorySize", config.ProjectConf.MemorySize.ToString());
this.setData("hideAfterCallMain", config.ProjectConf.HideAfterCallMain);
this.setData("dataFileSubPrefix", config.ProjectConf.dataFileSubPrefix);
this.setData("maxStorage", config.ProjectConf.maxStorage.ToString());
this.setData("defaultReleaseSize", config.ProjectConf.defaultReleaseSize.ToString());
this.setData("texturesHashLength", config.ProjectConf.texturesHashLength.ToString());
this.setData("texturesPath", config.ProjectConf.texturesPath);
this.setData("needCacheTextures", config.ProjectConf.needCacheTextures);
this.setData("loadingBarWidth", config.ProjectConf.loadingBarWidth.ToString());
this.setData("needCheckUpdate", config.ProjectConf.needCheckUpdate);
this.setData("disableHighPerformanceFallback", config.ProjectConf.disableHighPerformanceFallback);
this.setData("showMonitorSuggestModal", config.CompileOptions.showMonitorSuggestModal);
this.setData("enableProfileStats", config.CompileOptions.enableProfileStats);
this.setData("enableRenderAnalysis", config.CompileOptions.enableRenderAnalysis);
this.setData("autoUploadFirstBundle", true);
}
private void saveData()
{
config.ProjectConf.projectName = this.getDataInput("projectName");
config.ProjectConf.Appid = this.getDataInput("appid");
config.ProjectConf.CDN = this.getDataInput("cdn");
config.ProjectConf.assetLoadType = this.getDataPop("assetLoadType");
config.ProjectConf.compressDataPackage = this.getDataCheckbox("compressDataPackage");
config.ProjectConf.VideoUrl = this.getDataInput("videoUrl");
config.ProjectConf.Orientation = (WXScreenOritation)this.getDataPop("orientation");
config.ProjectConf.DST = this.getDataInput("dst");
config.ProjectConf.bundleHashLength = int.Parse(this.getDataInput("bundleHashLength"));
config.ProjectConf.bundlePathIdentifier = this.getDataInput("bundlePathIdentifier");
config.ProjectConf.bundleExcludeExtensions = this.getDataInput("bundleExcludeExtensions");
config.ProjectConf.preloadFiles = this.getDataInput("preloadFiles");
config.CompileOptions.DevelopBuild = this.getDataCheckbox("developBuild");
config.CompileOptions.AutoProfile = this.getDataCheckbox("autoProfile");
config.CompileOptions.ScriptOnly = this.getDataCheckbox("scriptOnly");
config.CompileOptions.Il2CppOptimizeSize = this.getDataCheckbox("il2CppOptimizeSize");
config.CompileOptions.profilingFuncs = this.getDataCheckbox("profilingFuncs");
config.CompileOptions.ProfilingMemory = this.getDataCheckbox("profilingMemory");
config.CompileOptions.DeleteStreamingAssets = this.getDataCheckbox("deleteStreamingAssets");
config.CompileOptions.CleanBuild = this.getDataCheckbox("cleanBuild");
config.CompileOptions.CustomNodePath = this.getDataInput("customNodePath");
config.CompileOptions.Webgl2 = this.getDataCheckbox("webgl2");
config.CompileOptions.enableIOSPerformancePlus = this.getDataCheckbox("iOSPerformancePlus");
config.CompileOptions.fbslim = this.getDataCheckbox("fbslim");
config.SDKOptions.UseFriendRelation = this.getDataCheckbox("useFriendRelation");
config.SDKOptions.UseMiniGameChat = this.getDataCheckbox("useMiniGameChat");
config.SDKOptions.PreloadWXFont = this.getDataCheckbox("preloadWXFont");
config.ProjectConf.bgImageSrc = this.getDataInput("bgImageSrc");
config.ProjectConf.MemorySize = int.Parse(this.getDataInput("memorySize"));
config.ProjectConf.HideAfterCallMain = this.getDataCheckbox("hideAfterCallMain");
config.ProjectConf.dataFileSubPrefix = this.getDataInput("dataFileSubPrefix");
config.ProjectConf.maxStorage = int.Parse(this.getDataInput("maxStorage"));
config.ProjectConf.defaultReleaseSize = int.Parse(this.getDataInput("defaultReleaseSize"));
config.ProjectConf.texturesHashLength = int.Parse(this.getDataInput("texturesHashLength"));
config.ProjectConf.texturesPath = this.getDataInput("texturesPath");
config.ProjectConf.needCacheTextures = this.getDataCheckbox("needCacheTextures");
config.ProjectConf.loadingBarWidth = int.Parse(this.getDataInput("loadingBarWidth"));
config.ProjectConf.needCheckUpdate = this.getDataCheckbox("needCheckUpdate");
config.ProjectConf.disableHighPerformanceFallback = this.getDataCheckbox("disableHighPerformanceFallback");
config.CompileOptions.showMonitorSuggestModal = this.getDataCheckbox("showMonitorSuggestModal");
config.CompileOptions.enableProfileStats = this.getDataCheckbox("enableProfileStats");
config.CompileOptions.enableRenderAnalysis = this.getDataCheckbox("enableRenderAnalysis");
}
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 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();
}
private void formCheckbox(string target, string label, string help = null, bool disable = false, Action<bool> setting = 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);
formCheckboxData[target] = EditorGUILayout.Toggle(disable ? false : formCheckboxData[target]);
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();
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 6aad761bd858d8d4880b225d799ed346
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,18 +1,12 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using System.IO;
using static WeChatWASM.WXConvertCore;
using System;
namespace WeChatWASM
{
public class WXEditorWin : EditorWindow
{
private static WXEditorScriptObject config;
private bool fbSlimSupport = true;
[MenuItem("微信小游戏 / 转换小游戏", false, 1)]
public static void Open()
{
@ -30,549 +24,25 @@ namespace WeChatWASM
return WXConvertCore.DoExport(buildWebGL);
}
// Start is called before the first frame update
void Start()
{
foldInstantGame = WXConvertCore.IsInstantGameAutoStreaming();
}
// Update is called once per frame
void Update()
{
}
public void OnFocus()
{
loadData();
WXSettingsHelperInterface.helper.OnFocus();
}
public void OnLostFocus()
{
saveData();
WXSettingsHelperInterface.helper.OnLostFocus();
}
public void OnDisable()
{
EditorUtility.SetDirty(config);
WXSettingsHelperInterface.helper.OnDisable();
}
public Texture tex;
public void OnGUI()
{
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");
this.formInput("appid", "游戏AppID");
this.formInput("cdn", "游戏资源CDN");
this.formInput("projectName", "小游戏项目名");
this.formIntPopup("orientation", "游戏方向", new[] { "Portrait", "Landscape", "LandscapeLeft", "LandscapeRight" }, 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("导出路径", 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(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();
}
foldLoadingConfig = EditorGUILayout.Foldout(foldLoadingConfig, "启动Loading配置");
if (foldLoadingConfig)
{
EditorGUILayout.BeginVertical("frameBox");
GUILayout.BeginHorizontal();
string targetBg = "bgImageSrc";
EditorGUILayout.LabelField(string.Empty, GUILayout.Width(10));
tex = (Texture)EditorGUILayout.ObjectField("启动背景图/视频封面", tex, typeof(Texture2D), false);
var currentBgSrc = AssetDatabase.GetAssetPath(tex);
if (!string.IsNullOrEmpty(currentBgSrc) && currentBgSrc != this.formInputData[targetBg])
{
this.formInputData[targetBg] = currentBgSrc;
this.saveData();
}
GUILayout.EndHorizontal();
this.formInput("videoUrl", "加载阶段视频URL");
this.formIntPopup("assetLoadType", "首包资源加载方式", new[] { "CDN", "小游戏包内" }, new[] { 0, 1 });
this.formCheckbox("compressDataPackage", "压缩首包资源(?)", "将首包资源Brotli压缩, 降低资源大小. 注意: 首次启动耗时可能会增加200ms, 仅推荐使用小游戏分包加载时节省包体大小使用");
this.formInput("bundleExcludeExtensions", "不自动缓存文件类型(?)", "(使用;分割)当请求url包含资源'cdn+StreamingAssets'时会自动缓存但StreamingAssets目录下不是所有文件都需缓存此选项配置不需要自动缓存的文件拓展名。默认值json");
this.formInput("bundleHashLength", "Bundle名称Hash长度(?)", "自定义Bundle文件名中hash部分长度默认值32用于缓存控制。");
this.formInput("preloadFiles", "预下载文件列表(?)", "使用;间隔,支持模糊匹配");
EditorGUILayout.EndVertical();
}
foldSDKOptions = EditorGUILayout.Foldout(foldSDKOptions, "SDK功能选项");
if (foldSDKOptions)
{
EditorGUILayout.BeginVertical("frameBox");
this.formCheckbox("useFriendRelation", "使用好友关系链");
this.formCheckbox("useMiniGameChat", "使用社交组件");
this.formCheckbox("preloadWXFont", "预加载微信字体(?)", "在game.js执行开始时预载微信系统字体运行期间可使用WX.GetWXFont获取微信字体");
EditorGUILayout.EndVertical();
}
foldDebugOptions = EditorGUILayout.Foldout(foldDebugOptions, "调试编译选项");
if (foldDebugOptions)
{
EditorGUILayout.BeginVertical("frameBox");
this.formCheckbox("developBuild", "Development Build");
this.formCheckbox("autoProfile", "Auto connect Profiler");
this.formCheckbox("scriptOnly", "Scripts Only Build");
this.formCheckbox("il2CppOptimizeSize", "Il2Cpp Optimize Size(?)", "对应于Il2CppCodeGeneration选项勾选时使用OptimizeSize(默认推荐)生成代码小15%左右取消勾选则使用OptimizeSpeed。游戏中大量泛型集合的高频访问建议OptimizeSpeed在使用HybridCLR等第三方组件时只能用OptimizeSpeed。");
this.formCheckbox("profilingFuncs", "Profiling Funcs");
this.formCheckbox("profilingMemory", "Profiling Memory");
this.formCheckbox("webgl2", "WebGL2.0(beta)");
this.formCheckbox("iOSPerformancePlus", "iOSPerformancePlus(?)", "是否使用iOS高性能+渲染方案有助于提升渲染兼容性、降低WebContent进程内存");
this.formCheckbox("deleteStreamingAssets", "Clear Streaming Assets");
this.formCheckbox("cleanBuild", "Clean WebGL Build");
// this.formCheckbox("cleanCloudDev", "Clean Cloud Dev");
this.formCheckbox("fbslim", "首包资源优化(?)", "导出时自动清理UnityEditor默认打包但游戏项目从未使用的资源瘦身首包资源体积建议所有游戏启用。" + (this.fbSlimSupport ? "" : "(当前Unity Editor暂不支持该能力)"), !this.fbSlimSupport, (res) =>
{
var fbWin = GetWindow(typeof(WXFbSettingWindow), false, "首包资源优化配置面板", true);
fbWin.minSize = new Vector2(680, 350);
fbWin.Show();
});
this.formCheckbox("showMonitorSuggestModal", "显示优化建议弹窗");
this.formCheckbox("enableProfileStats", "显示性能面板");
this.formCheckbox("enableRenderAnalysis", "显示渲染日志(dev only)");
EditorGUILayout.EndVertical();
}
#if UNITY_INSTANTGAME
foldInstantGame = EditorGUILayout.Foldout(foldInstantGame, "Instant Game - AutoStreaming");
if (foldInstantGame)
{
EditorGUILayout.BeginVertical("frameBox");
this.formInput("bundlePathIdentifier", "Bundle Path Identifier");
this.formInput("dataFileSubPrefix", "Data File Sub Prefix");
EditorGUI.BeginDisabledGroup(true);
this.formCheckbox("autoUploadFirstBundle", "构建后自动上传首包(?)", "仅在开启AutoStreaming生效", true);
EditorGUI.EndDisabledGroup();
GUILayout.BeginHorizontal();
EditorGUILayout.LabelField(string.Empty, GUILayout.Width(10));
GUILayout.Label(new GUIContent("清理AS配置(?)", "如需关闭AutoStreaming选用默认发布方案则需要清理AS配置项目。"), GUILayout.Width(140));
EditorGUI.BeginDisabledGroup(WXConvertCore.IsInstantGameAutoStreaming());
if(GUILayout.Button(new GUIContent("恢复"),GUILayout.Width(60))){
string identifier = config.ProjectConf.bundlePathIdentifier;
string[] identifiers = identifier.Split(";");
string idStr = "";
foreach (string id in identifiers)
{
if (id != "AS" && id != "CUS/CustomAB")
{
idStr += id + ";";
}
}
config.ProjectConf.bundlePathIdentifier = idStr.Trim(';');
if (config.ProjectConf.dataFileSubPrefix == "CUS")
{
config.ProjectConf.dataFileSubPrefix = "";
}
this.loadData();
}
EditorGUI.EndDisabledGroup();
GUILayout.EndHorizontal();
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField(string.Empty);
if (GUILayout.Button(new GUIContent("了解Instant Game AutoStreaming", ""), linkStyle))
{
Application.OpenURL("https://github.com/wechat-miniprogram/minigame-unity-webgl-transform/blob/main/Design/InstantGameGuide.md");
}
EditorGUILayout.EndHorizontal();
EditorGUILayout.EndVertical();
}
#endif
EditorGUILayout.BeginHorizontal();
if (GUILayout.Button(new GUIContent("更多配置项"), GUILayout.Width(100), GUILayout.Height(25)))
{
var config = AssetDatabase.LoadAssetAtPath<UnityEngine.Object>("Assets/WX-WASM-SDK-V2/Editor/MiniGameConfig.asset");
Selection.activeObject = config;
GUIUtility.ExitGUI();
}
if (GUILayout.Button(new GUIContent("WebGL转小游戏(不常用)"), GUILayout.Width(150), GUILayout.Height(25)))
{
this.saveData();
if (WXConvertCore.DoExport(false) == WXConvertCore.WXExportError.SUCCEED)
{
ShowNotification(new GUIContent("转换完成"));
}
GUIUtility.ExitGUI();
}
EditorGUILayout.LabelField(string.Empty, GUILayout.MinWidth(10));
if (GUILayout.Button(new GUIContent("生成并转换"), GUILayout.Width(100), GUILayout.Height(25)))
{
this.saveData();
if (WXConvertCore.DoExport() == WXConvertCore.WXExportError.SUCCEED)
{
if (!WXConvertCore.IsInstantGameAutoStreaming())
ShowNotification(new GUIContent("转换完成"));
else
{
#if (UNITY_WEBGL || PLATFORM_WEIXINMINIGAME) && UNITY_INSTANTGAME
// 上传首包资源
if (!string.IsNullOrEmpty(WXConvertCore.FirstBundlePath) && File.Exists(WXConvertCore.FirstBundlePath))
{
if (Unity.InstantGame.IGBuildPipeline.UploadWeChatDataFile(WXConvertCore.FirstBundlePath))
{
Debug.Log("转换完成并成功上传首包资源");
ShowNotification(new GUIContent("转换完成并成功上传"));
}
else
{
Debug.LogError("首包资源上传失败请检查网络以及Auto Streaming配置是否正确。");
ShowNotification(new GUIContent("上传失败"));
}
}
else
{
Debug.LogError("转换失败");
ShowNotification(new GUIContent("转换失败"));
}
#else
ShowNotification(new GUIContent("转换完成"));
#endif
}
}
GUIUtility.ExitGUI();
}
EditorGUILayout.EndHorizontal();
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField(string.Empty);
if (GUILayout.Button(new GUIContent("了解如何实现自定义构建", ""), linkStyle))
{
Application.OpenURL("https://github.com/wechat-miniprogram/minigame-unity-webgl-transform/blob/main/Design/DevelopmentQAList.md#13%E5%A6%82%E4%BD%95%E8%87%AA%E5%AE%9A%E4%B9%89%E6%8E%A5%E5%85%A5%E6%9E%84%E5%BB%BA%E6%B5%81%E7%A8%8B");
GUIUtility.ExitGUI();
}
EditorGUILayout.EndHorizontal();
EditorGUILayout.EndScrollView();
WXSettingsHelperInterface.helper.OnSettingsGUI(this);
WXSettingsHelperInterface.helper.OnBuildButtonGUI(this);
}
private Vector2 scrollRoot;
private bool foldBaseInfo = true;
private bool foldLoadingConfig = true;
private bool foldSDKOptions = true;
private bool foldDebugOptions = true;
private bool foldInstantGame = false;
private Dictionary<string, string> formInputData = new Dictionary<string, string>();
private Dictionary<string, int> formIntPopupData = new Dictionary<string, int>();
private Dictionary<string, bool> formCheckboxData = new Dictionary<string, bool>();
private string SDKFilePath;
private void addBundlePathIdentifier(string value)
{
string identifier = config.ProjectConf.bundlePathIdentifier;
if (identifier[identifier.Length - 1] != ';')
{
identifier += ";";
}
identifier += value;
config.ProjectConf.bundlePathIdentifier = identifier;
}
private void loadData()
{
// SDKFilePath = Path.Combine(Application.dataPath, "WX-WASM-SDK-V2", "Runtime", "wechat-default", "unity-sdk", "index.js");
SDKFilePath = Path.Combine(UnityUtil.GetWxSDKRootPath(), "Runtime", "wechat-default", "unity-sdk", "index.js");
config = UnityUtil.GetEditorConf();
// Instant Game
if (WXConvertCore.IsInstantGameAutoStreaming())
{
config.ProjectConf.CDN = WXConvertCore.GetInstantGameAutoStreamingCDN();
string identifier = config.ProjectConf.bundlePathIdentifier;
string[] identifiers = identifier.Split(';');
bool AS = false;
bool CUS = false;
foreach (string id in identifiers)
{
if (id == "AS")
{
AS = true;
}
if (id == "CUS/CustomAB")
{
CUS = true;
}
}
if (!AS)
{
this.addBundlePathIdentifier("AS");
}
if (!CUS)
{
this.addBundlePathIdentifier("CUS/CustomAB");
}
if (config.ProjectConf.dataFileSubPrefix != "CUS")
{
config.ProjectConf.dataFileSubPrefix = "CUS";
}
}
this.setData("projectName", config.ProjectConf.projectName);
this.setData("appid", config.ProjectConf.Appid);
this.setData("cdn", config.ProjectConf.CDN);
this.setData("assetLoadType", config.ProjectConf.assetLoadType);
this.setData("compressDataPackage", config.ProjectConf.compressDataPackage);
this.setData("videoUrl", config.ProjectConf.VideoUrl);
this.setData("orientation", (int)config.ProjectConf.Orientation);
this.setData("dst", config.ProjectConf.DST);
this.setData("bundleHashLength", config.ProjectConf.bundleHashLength.ToString());
this.setData("bundlePathIdentifier", config.ProjectConf.bundlePathIdentifier);
this.setData("bundleExcludeExtensions", config.ProjectConf.bundleExcludeExtensions);
this.setData("preloadFiles", config.ProjectConf.preloadFiles);
this.setData("developBuild", config.CompileOptions.DevelopBuild);
this.setData("autoProfile", config.CompileOptions.AutoProfile);
this.setData("scriptOnly", config.CompileOptions.ScriptOnly);
this.setData("il2CppOptimizeSize", config.CompileOptions.Il2CppOptimizeSize);
this.setData("profilingFuncs", config.CompileOptions.profilingFuncs);
this.setData("profilingMemory", config.CompileOptions.ProfilingMemory);
this.setData("deleteStreamingAssets", config.CompileOptions.DeleteStreamingAssets);
this.setData("cleanBuild", config.CompileOptions.CleanBuild);
this.setData("customNodePath", config.CompileOptions.CustomNodePath);
this.setData("webgl2", config.CompileOptions.Webgl2);
this.setData("iOSPerformancePlus", config.CompileOptions.enableIOSPerformancePlus);
this.setData("fbslim", config.CompileOptions.fbslim);
this.setData("useFriendRelation", config.SDKOptions.UseFriendRelation);
this.setData("useMiniGameChat", config.SDKOptions.UseMiniGameChat);
this.setData("preloadWXFont", config.SDKOptions.PreloadWXFont);
this.setData("bgImageSrc", config.ProjectConf.bgImageSrc);
tex = AssetDatabase.LoadAssetAtPath<Texture>(config.ProjectConf.bgImageSrc);
this.setData("memorySize", config.ProjectConf.MemorySize.ToString());
this.setData("hideAfterCallMain", config.ProjectConf.HideAfterCallMain);
this.setData("dataFileSubPrefix", config.ProjectConf.dataFileSubPrefix);
this.setData("maxStorage", config.ProjectConf.maxStorage.ToString());
this.setData("defaultReleaseSize", config.ProjectConf.defaultReleaseSize.ToString());
this.setData("texturesHashLength", config.ProjectConf.texturesHashLength.ToString());
this.setData("texturesPath", config.ProjectConf.texturesPath);
this.setData("needCacheTextures", config.ProjectConf.needCacheTextures);
this.setData("loadingBarWidth", config.ProjectConf.loadingBarWidth.ToString());
this.setData("needCheckUpdate", config.ProjectConf.needCheckUpdate);
this.setData("disableHighPerformanceFallback", config.ProjectConf.disableHighPerformanceFallback);
this.setData("showMonitorSuggestModal", config.CompileOptions.showMonitorSuggestModal);
this.setData("enableProfileStats", config.CompileOptions.enableProfileStats);
this.setData("enableRenderAnalysis", config.CompileOptions.enableRenderAnalysis);
this.setData("autoUploadFirstBundle", true);
}
private void saveData()
{
config.ProjectConf.projectName = this.getDataInput("projectName");
config.ProjectConf.Appid = this.getDataInput("appid");
config.ProjectConf.CDN = this.getDataInput("cdn");
config.ProjectConf.assetLoadType = this.getDataPop("assetLoadType");
config.ProjectConf.compressDataPackage = this.getDataCheckbox("compressDataPackage");
config.ProjectConf.VideoUrl = this.getDataInput("videoUrl");
config.ProjectConf.Orientation = (WXScreenOritation)this.getDataPop("orientation");
config.ProjectConf.DST = this.getDataInput("dst");
config.ProjectConf.bundleHashLength = int.Parse(this.getDataInput("bundleHashLength"));
config.ProjectConf.bundlePathIdentifier = this.getDataInput("bundlePathIdentifier");
config.ProjectConf.bundleExcludeExtensions = this.getDataInput("bundleExcludeExtensions");
config.ProjectConf.preloadFiles = this.getDataInput("preloadFiles");
config.CompileOptions.DevelopBuild = this.getDataCheckbox("developBuild");
config.CompileOptions.AutoProfile = this.getDataCheckbox("autoProfile");
config.CompileOptions.ScriptOnly = this.getDataCheckbox("scriptOnly");
config.CompileOptions.Il2CppOptimizeSize = this.getDataCheckbox("il2CppOptimizeSize");
config.CompileOptions.profilingFuncs = this.getDataCheckbox("profilingFuncs");
config.CompileOptions.ProfilingMemory = this.getDataCheckbox("profilingMemory");
config.CompileOptions.DeleteStreamingAssets = this.getDataCheckbox("deleteStreamingAssets");
config.CompileOptions.CleanBuild = this.getDataCheckbox("cleanBuild");
config.CompileOptions.CustomNodePath = this.getDataInput("customNodePath");
config.CompileOptions.Webgl2 = this.getDataCheckbox("webgl2");
config.CompileOptions.enableIOSPerformancePlus = this.getDataCheckbox("iOSPerformancePlus");
config.CompileOptions.fbslim = this.getDataCheckbox("fbslim");
config.SDKOptions.UseFriendRelation = this.getDataCheckbox("useFriendRelation");
config.SDKOptions.UseMiniGameChat = this.getDataCheckbox("useMiniGameChat");
config.SDKOptions.PreloadWXFont = this.getDataCheckbox("preloadWXFont");
config.ProjectConf.bgImageSrc = this.getDataInput("bgImageSrc");
config.ProjectConf.MemorySize = int.Parse(this.getDataInput("memorySize"));
config.ProjectConf.HideAfterCallMain = this.getDataCheckbox("hideAfterCallMain");
config.ProjectConf.dataFileSubPrefix = this.getDataInput("dataFileSubPrefix");
config.ProjectConf.maxStorage = int.Parse(this.getDataInput("maxStorage"));
config.ProjectConf.defaultReleaseSize = int.Parse(this.getDataInput("defaultReleaseSize"));
config.ProjectConf.texturesHashLength = int.Parse(this.getDataInput("texturesHashLength"));
config.ProjectConf.texturesPath = this.getDataInput("texturesPath");
config.ProjectConf.needCacheTextures = this.getDataCheckbox("needCacheTextures");
config.ProjectConf.loadingBarWidth = int.Parse(this.getDataInput("loadingBarWidth"));
config.ProjectConf.needCheckUpdate = this.getDataCheckbox("needCheckUpdate");
config.ProjectConf.disableHighPerformanceFallback = this.getDataCheckbox("disableHighPerformanceFallback");
config.CompileOptions.showMonitorSuggestModal = this.getDataCheckbox("showMonitorSuggestModal");
config.CompileOptions.enableProfileStats = this.getDataCheckbox("enableProfileStats");
config.CompileOptions.enableRenderAnalysis = this.getDataCheckbox("enableRenderAnalysis");
}
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 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();
}
private void formCheckbox(string target, string label, string help = null, bool disable = false, Action<bool> setting = 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);
formCheckboxData[target] = EditorGUILayout.Toggle(formCheckboxData[target]);
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();
EditorGUILayout.LabelField(string.Empty);
GUILayout.EndHorizontal();
}
}
}

View File

@ -105,6 +105,10 @@ namespace WeChatWASM
// #endif
// return null;
// });
WXExtEnvDef.RegisterAction("WXConvertCore.UseIL2CPP", (args) =>
{
return WXConvertCore.UseIL2CPP;
});
}
}
}

View File

@ -2,7 +2,7 @@
{
public class WXPluginVersion
{
public static string pluginVersion = "202403061052"; // 这一行不要改他,导出的时候会自动替换
public static string pluginVersion = "202403281243"; // 这一行不要改他,导出的时候会自动替换
}
public class WXPluginConf

Binary file not shown.

View File

@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: dbb11f91282684564a2c405c5c485013
guid: 89f24ea7e42cd084ab0d5bbd61fa1e3a
TextScriptImporter:
externalObjects: {}
userData:

View File

@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: cbdd86a6659a24968be342c46c0eeb03
guid: abc091d8a2f753b49a0e435c823e2c91
folderAsset: yes
DefaultImporter:
externalObjects: {}

108
Runtime/Plugins/AES.jslib Normal file
View File

@ -0,0 +1,108 @@
var WebAesLibrary = {
$aesManager: {
instance: null,
mode: 0,
padding: null,
encCallback: null,
decCallback: null,
},
WebAesInitialize: function (
mode,
keyBuffer,
keySize,
ivBuffer,
ivSize,
encCallback,
decCallback,
counter,
segmentSize
) {
if (typeof window === "undefined" || !window.aesjs) {
throw new Error("Fail to initialize WebAES. window.aesjs not found.");
}
var aesjs = window.aesjs;
var key = new Uint8Array(Module.HEAPU8.buffer, keyBuffer, keySize);
var iv = new Uint8Array(Module.HEAPU8.buffer, ivBuffer, ivSize);
switch (mode) {
case 1: // CBC
aesManager.instance = new aesjs.ModeOfOperation.cbc(key, iv);
break;
case 2: // ECB
aesManager.instance = new aesjs.ModeOfOperation.ecb(key);
break;
case 3: // OFB
aesManager.instance = new aesjs.ModeOfOperation.ofb(key, iv);
break;
case 4: // CFB
aesManager.instance = new aesjs.ModeOfOperation.cfb(
key,
iv,
segmentSize
);
break;
case 5:
break; // Reserved for CTS
case 6: // CTR, Not used by interop yet
aesManager.instance = new aesjs.ModeOfOperation.ctr(key, counter);
break;
default:
// Default to ECB
aesManager.instance = new aesjs.ModeOfOperation.ecb(key);
}
aesManager.encCallback = encCallback;
aesManager.decCallback = decCallback;
},
WebAesFinalize: function () {
aesManager = {
instance: null,
mode: 0,
padding: null,
encCallback: null,
decCallback: null,
};
},
WebAesEncrypt: function (plainPtr, size, needPad) {
if (aesManager.instance === null) {
throw new Error("Must call initialize before encrypt");
}
var key = new Uint8Array(Module.HEAPU8.buffer, plainPtr, size);
if (needPad) {
key = aesjs.padding.pkcs7.pad(key);
}
// result is Uint8Array
var result = aesManager.instance.encrypt(key);
var buffer = _malloc(result.length);
HEAPU8.set(result, buffer);
try {
if (aesManager.encCallback) {
Module.dynCall_viii(aesManager.encCallback, 1, buffer, result.length);
}
} finally {
_free(buffer);
}
},
WebAesDecrypt: function (cipherPtr, size, needStrip) {
if (aesManager.instance === null) {
throw new Error("Must call initialize before decrypt");
}
var key = new Uint8Array(Module.HEAPU8.buffer, cipherPtr, size);
// result is Uint8Array
var result = aesManager.instance.decrypt(key, needStrip);
if (needStrip) {
result = aesjs.padding.pkcs7.strip(result);
}
var buffer = _malloc(result.length);
HEAPU8.set(result, buffer);
try {
if (aesManager.decCallback) {
Module.dynCall_viii(aesManager.decCallback, 0, buffer, result.length);
}
} finally {
_free(buffer);
}
},
};
autoAddDeps(WebAesLibrary, "$aesManager");
mergeInto(LibraryManager.library, WebAesLibrary);

View File

@ -0,0 +1,21 @@
fileFormatVersion: 2
guid: a8be7960d079402392e951de6e3978b7
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
defineConstraints: []
isPreloaded: 0
isOverridable: 0
isExplicitlyReferenced: 0
validateReferences: 1
platformData:
- first:
Any:
second:
enabled: 1
settings: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
mergeInto(LibraryManager.library, {
_WebMD5: function(rawData, dataLength, result) {
var data = new Uint8Array(Module.HEAPU8.buffer, rawData, dataLength);
var md5Result = window.md5WASM(data, true); // Assuming this always returns a Uint8Array of 16 bytes
Module.HEAPU8.set(md5Result, result);
}
});

View File

@ -0,0 +1,21 @@
fileFormatVersion: 2
guid: 9250060552104a8388aaff7dc4f4dde7
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
defineConstraints: []
isPreloaded: 0
isOverridable: 0
isExplicitlyReferenced: 0
validateReferences: 1
platformData:
- first:
Any:
second:
enabled: 1
settings: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -508,7 +508,11 @@ mergeInto(LibraryManager.library, {
if (typeof TOTAL_MEMORY !== "undefined") {
return TOTAL_MEMORY
}
return buffer.byteLength;
if (wasmMemory && wasmMemory.buffer) {
return wasmMemory.buffer.byteLength;
}
console.error('Fail to find wasmMemory.buffer, TotalMemorySize is not correct.');
return 0;
},
WXGetTotalStackSize: function() {
return TOTAL_STACK;
@ -783,7 +787,11 @@ mergeInto(LibraryManager.library, {
window.WXWASMSDK.WX_OperateGameRecorderVideo(_WXPointer_stringify_adaptor(option));
},
WXChatCreate: function (option) {
return window.WXWASMSDK.WXChatCreate(_WXPointer_stringify_adaptor(option));
var returnStr = window.WXWASMSDK.WXChatCreate(_WXPointer_stringify_adaptor(option));
var bufferSize = lengthBytesUTF8(returnStr || '') + 1;
var buffer = _malloc(bufferSize);
stringToUTF8(returnStr, buffer, bufferSize);
return buffer;
},
WXChatHide: function () {
window.WXWASMSDK.WXChatHide();

1252
Runtime/Plugins/crypto.jspre Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,21 @@
fileFormatVersion: 2
guid: 71bc98cdcdfd4e059f97a9d2bd913e7f
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
defineConstraints: []
isPreloaded: 0
isOverridable: 0
isExplicitlyReferenced: 0
validateReferences: 1
platformData:
- first:
Any:
second:
enabled: 1
settings: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,4 +1,5 @@
<linker>
<assembly fullname="wx-runtime" preserve="all"/>
<assembly fullname="LitJson" preserve="all"/>
<assembly fullname="Wx" preserve="all"/>
</linker>

View File

@ -1,7 +0,0 @@
fileFormatVersion: 2
guid: 6c79fcf12fc194476b7241375856274b
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 01d4a3ad9ca6f47fc9c50416a5ba3ed2
guid: 7d41c663172388842a5d3b037200a60d
TextScriptImporter:
externalObjects: {}
userData:

Binary file not shown.

View File

@ -1,7 +0,0 @@
fileFormatVersion: 2
guid: 07510318632604babbaaafba80f57ef5
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 0cd7a7a3759f5466bb8fe9ff96ce5227
guid: 7cf82b7695e9a6e4f91fb8e1f00510c7
TextScriptImporter:
externalObjects: {}
userData:

View File

@ -7,9 +7,9 @@ using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Profiling;
using WeChatWASM;
#if UNITY_WEBGL || WEIXINMINIGAME || UNITY_EDITOR
using WeChatWASM;
public class WXProfileStatsScript : MonoBehaviour, WeChatWASM.WXSDKManagerHandler.WXProfileStatsScript
{
private string statsText;

108
Runtime/WebAES.cs Normal file
View File

@ -0,0 +1,108 @@
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Text;
using AOT;
using UnityEditor;
using UnityEngine;
namespace WeChatWASM
{
public class WebAES
{
public static byte[] EncryptedBytes;
public static byte[] DecryptedBytes;
public static bool initialized;
public static System.IntPtr keyPtr, ivPtr;
public delegate void AesCallback(int enc, System.IntPtr resPtr, int resSize);
[DllImport("__Internal")]
public static extern void WebAesInitialize(
int mode,
System.IntPtr keyPtr, int keySize,
System.IntPtr ivPtr, int ivSize,
AesCallback encCallback, AesCallback decCallback,
int counter = 0, int segmentSize = 0);
[DllImport("__Internal")]
public static extern void WebAesFinalize();
[DllImport("__Internal")]
public static extern void WebAesEncrypt(System.IntPtr plainBytesPtr, int size, int needPad);
[DllImport("__Internal")]
public static extern void WebAesDecrypt(System.IntPtr encryptedBytesPtr, int size, int needStrip);
// TODO: consider passing a unique id so that we can initialize multiple instances.
public static void Initialize(System.Security.Cryptography.CipherMode mode, byte[] key,
byte[] iv, int counter = 0, int segmentSize = 0)
{
if (initialized)
{
Debug.Log("Already initialized! Please call Finalize() before initialize again.");
return;
}
if (mode == CipherMode.CTS)
{
Debug.LogError("CTS not supported!");
return;
}
int keySize = key.Length;
keyPtr = Marshal.AllocHGlobal(keySize);
Marshal.Copy(key, 0, keyPtr, keySize);
int ivSize = iv.Length;
ivPtr = Marshal.AllocHGlobal(ivSize);
Marshal.Copy(iv, 0, ivPtr, ivSize);
WebAesInitialize(
(int)mode,
keyPtr, keySize, ivPtr, ivSize,
OnAecCrypto, OnAecCrypto,
counter, segmentSize);
initialized = true;
}
[MonoPInvokeCallback(typeof(AesCallback))]
public static void OnAecCrypto(int enc, System.IntPtr resPtr, int resSize)
{
byte[] res = new byte[resSize];
Marshal.Copy(resPtr, res, 0, resSize);
if (enc == 1)
{
EncryptedBytes = res;
}
else
{
DecryptedBytes = res;
}
}
// TODO: consider passing a unique id so we can destroy by instance.
public static void Finalize()
{
EncryptedBytes = null;
DecryptedBytes = null;
Marshal.FreeHGlobal(keyPtr);
Marshal.FreeHGlobal(ivPtr);
initialized = false;
WebAesFinalize();
}
// Use UTF-8 as it is used as default format for StreamReader
public static byte[] Encode(string plainStr, bool needPad = true)
{
var bytes = Encoding.UTF8.GetBytes(plainStr);
var bytesPtr = Marshal.AllocHGlobal(bytes.Length);
Marshal.Copy(bytes, 0, bytesPtr, bytes.Length);
WebAesEncrypt(bytesPtr, bytes.Length, needPad ? 1 : 0);
Marshal.FreeHGlobal(bytesPtr);
return EncryptedBytes;
}
public static string Decode(byte[] bytes, bool needStrip = true)
{
var bytesPtr = Marshal.AllocHGlobal(bytes.Length);
Marshal.Copy(bytes, 0, bytesPtr, bytes.Length);
WebAesDecrypt(bytesPtr, bytes.Length, needStrip ? 1 : 0);
Marshal.FreeHGlobal(bytesPtr);
return Encoding.UTF8.GetString(DecryptedBytes);
}
}
}

3
Runtime/WebAES.cs.meta Normal file
View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: d9e79ef28f134cfcbafaedb5f052df60
timeCreated: 1707119327

31
Runtime/WebMD5.cs Normal file
View File

@ -0,0 +1,31 @@
using System;
using System.Runtime.InteropServices;
namespace WeChatWASM
{
public class WebMD5
{
[DllImport("__Internal", EntryPoint = "_WebMD5")]
private static extern void getMD5Bytes(IntPtr byteArray, int length, IntPtr result);
public static byte[] GetMD5Bytes(byte[] input)
{
var inputLength = input.Length;
var inputPtr = Marshal.AllocHGlobal(inputLength);
Marshal.Copy(input, 0, inputPtr, inputLength);
var resultPtr = Marshal.AllocHGlobal(16); // MD5 hash is 16 bytes
getMD5Bytes(inputPtr, inputLength, resultPtr);
byte[] result = new byte[16];
Marshal.Copy(resultPtr, result, 0, 16);
Marshal.FreeHGlobal(inputPtr);
Marshal.FreeHGlobal(resultPtr);
return result;
}
}
}

3
Runtime/WebMD5.cs.meta Normal file
View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: ee32301e25b2402fa31faba7435a1367
timeCreated: 1705137445

View File

@ -6,7 +6,7 @@ import './weapp-adapter';
import './events';
import 'texture-config.js';
import unityNamespace from './unity-namespace';
import './$GAME_NAME.wasm.framework.unityweb';
import '.$DOTNET_RUNTIME_FOLD/$GAME_NAME.wasm.framework.unityweb';
import './unity-sdk/index.js';
import checkVersion from './check-version';
import { launchEventType, scaleMode } from './plugin-config';

View File

@ -153,6 +153,10 @@ export class AudioChannelInstance {
gain;
callback = 0;
userData = 0;
loop = false;
loopStart = 0;
loopEnd = 0;
deleyTime = 0;
constructor(callback, userData) {
if (WEBAudio.audioContext) {
this.gain = WEBAudio.audioContext.createGain();
@ -169,6 +173,26 @@ export class AudioChannelInstance {
this.gain.disconnect();
}
}
setLoop(loop) {
this.loop = loop;
if (!this.source || this.source.loop == loop) {
return;
}
this.source.loop = loop;
}
setLoopPoints(loopStart, loopEnd) {
this.loopStart = loopStart;
this.loopEnd = loopEnd;
if (!this.source) {
return;
}
if (this.source.loopStart !== loopStart) {
this.source.loopStart = loopStart;
}
if (this.source.loopEnd !== loopEnd) {
this.source.loopEnd = loopEnd;
}
}
playUrl(startTime, url, startOffset, volume, soundClip) {
try {
this.setup(url);
@ -265,6 +289,8 @@ export class AudioChannelInstance {
}
this.source.canPlayFnList.push(fn);
this.source.mediaElement.onCanplay(fn);
this.source.mediaElement.loop = this.loop;
this.deleyTime = startTime;
this.source.start(startTime, startOffset);
this.source.playbackStartTime = startTime - startOffset / this.source.playbackRateValue;
}
@ -298,6 +324,9 @@ export class AudioChannelInstance {
this.gain.gain.value = volume;
}
}
this.source.loop = this.loop;
this.source.loopStart = this.loopStart;
this.source.loopEnd = this.loopEnd;
this.source.start(startTime, startOffset);
this.source.playbackStartTime = startTime - startOffset / this.source.playbackRateValue;
}
@ -355,7 +384,7 @@ export class AudioChannelInstance {
return true;
}
if (this.source.mediaElement) {
return (this.source.mediaElement.paused || this.source.pauseRequested) ?? true;
return (!this.source.isPlaying || this.source.pauseRequested) ?? true;
}
return false;
}
@ -373,9 +402,9 @@ export class AudioChannelInstance {
}
const pausedSource = {
isPausedMockNode: true,
loop: source.loop,
loopStart: source.loopStart,
loopEnd: source.loopEnd,
loop: this.loop,
loopStart: this.loopStart,
loopEnd: this.loopEnd,
buffer: source.buffer,
playbackRate: source.playbackRateValue,
playbackPausedAtPosition: source.estimatePlaybackPosition(),
@ -396,7 +425,8 @@ export class AudioChannelInstance {
return;
}
if (this.source.mediaElement) {
this.source.start();
this.source.start(this.deleyTime);
delete this.deleyTime;
return;
}
const pausedSource = this.source;
@ -1043,7 +1073,7 @@ export default {
if (!channel.source) {
return;
}
channel.source.loop = loop > 0;
channel.setLoop(loop > 0);
},
_JS_Sound_SetLoopPoints(channelInstance, loopStart, loopEnd) {
if (WEBAudio.audioWebEnabled === 0) {
@ -1056,8 +1086,7 @@ export default {
if (!channel.source) {
return;
}
channel.source.loopStart = loopStart;
channel.source.loopEnd = loopEnd;
channel.setLoopPoints(loopStart, loopEnd);
},
_JS_Sound_SetPaused(channelInstance, paused) {
if (WEBAudio.audioWebEnabled === 0) {

View File

@ -24,32 +24,32 @@ function runMethod(method, option, callbackId, isString = false) {
cacheArrayBuffer(callbackId, res.arrayBuffer);
returnRes = JSON.stringify({
bytesRead: res.bytesRead,
arrayBufferLength: res.arrayBuffer.byteLength,
arrayBufferLength: res.arrayBuffer?.byteLength ?? 0,
});
}
else if (method === 'readCompressedFile') {
cacheArrayBuffer(callbackId, res.data);
returnRes = JSON.stringify({
arrayBufferLength: res.data.byteLength,
arrayBufferLength: res.data?.byteLength ?? 0,
});
}
else if (method === 'readFile') {
if (config.encoding) {
returnRes = JSON.stringify({
stringData: res.data,
stringData: res.data || '',
});
}
else {
cacheArrayBuffer(callbackId, res.data);
returnRes = JSON.stringify({
arrayBufferLength: res.data.byteLength,
arrayBufferLength: res.data?.byteLength ?? 0,
});
}
}
else {
returnRes = JSON.stringify(res);
}
// console.log(`fs.${method} success:`, res);
moduleHelper.send('FileSystemManagerCallback', JSON.stringify({
callbackId, type: 'success', res: returnRes, method: isString ? `${method}_string` : method,
}));
@ -400,7 +400,7 @@ export default {
cacheArrayBuffer(callbackId, res.arrayBuffer);
return JSON.stringify({
bytesRead: res.bytesRead,
arrayBufferLength: res.arrayBuffer.byteLength,
arrayBufferLength: res.arrayBuffer?.byteLength ?? 0,
});
},
WX_FileSystemManagerFstatSync(option) {

View File

@ -225,6 +225,9 @@ function convertBase64ToData(input) {
return input;
}
export function cacheArrayBuffer(callbackId, data) {
if (!callbackId || !data) {
return;
}
tempCacheObj[callbackId] = data;
}
export function setArrayBuffer(buffer, offset, callbackId) {

View File

@ -1 +1 @@
{"name":"com.qq.weixin.minigame","displayName":"WXSDK","description":"WeChat Mini Game Tuanjie Engine Adapter SDK Package.","version":"0.1.2","unity":"2019.4","unityRelease":"29f1","keywords":["Tuanjie","WX"],"dependencies":{}}
{"name":"com.qq.weixin.minigame","displayName":"WXSDK","description":"WeChat Mini Game Tuanjie Engine Adapter SDK Package.","version":"0.1.3","unity":"2019.4","unityRelease":"29f1","keywords":["Tuanjie","WX"],"dependencies":{}}