插件格式
有2种方式
以源码方式引用,就是直接放入 cpp 文件,以及相关的头文件,cpp 文件才要放在unity中,头文件可以放在别的地方,只要能引用到就行编译时本身就会把C#通过IL2CPP反编译成cpp,然后再同插件中的cpp一起使用emscripten编译成最后的.wasm编译成静态库
2021.2版本之前,是编译成 .wasm 和 .js 格式
2021.2版本开始,是编译成 .a 和 .jslib 格式
.jslib 就是把 .js 扩展名改一下,都是为了把c++函数导出给js用的,如果你都是在c#中通过 [DllImport(“__Internal”)] 方式导入,那么可以不要这个文件
.wasm 和 .a 就是库文件,只不过需要用 emscripten 工具来编译而不是 gcc
官方示例NativeRenderingPlugin编译
git: https://github.com/Unity-Technologies/NativeRenderingPlugin.git
目录结构
PluginSource c++插件工程UnityProject unity工程入口
UseRenderingPlugin.cs 调用c++插件进行额外的渲染
编译web库
该示例本身是通过 cpp 源码方式直接做为web插件的,跟 PluginSource 中的头文件是有引用关系的,发布时最好编译成 .a库
由于这里不需要把接口导出给js用,因此不需要编译 .jslib ,只需要编译出 .a
把下面代码放在 PluginSource/build_web_lib.bat 中运行即可
:: 把webgl的cpp打包成.a库 :: 把emscriptenDir改成对应unity版本的路径,或直接使用 gaussiansplattingweb 内置的 Tools/BuildWebRenderPlugin 工具进行编译 set emscriptenDir=C:\Program Files\Unity\Hub\Editor\2021.3.25f1c1\Editor\Data\PlaybackEngines\WebGLSupport\BuildTools\Emscripten\ set srcDir=..\UnityProject\Packages\mg.gaussiansplattingweb\Runtime\Plugins\WebGL\ set emcc=%emscriptenDir%emscripten\emcc.bat set emar=%emscriptenDir%emscripten\emar.bat set EMSDK_PYTHON=%emscriptenDir%python\python.exe set EM_CONFIG=%emscriptenDir%.emscripten set cppPath=%srcDir%RenderingPlugin.cpp set objPath=RenderingPlugin.o set libPath=%srcDir%RenderingPlugin.a call "%emcc%" -c "%cppPath%" -o "%objPath%" call "%emar%" rcs "%libPath%" "%objPath%" del "%objPath%" pause
通过unity编辑器脚本编译加入编辑器脚本 BuildWebRenderPlugin.cs 在插件的 Editor 目录下
// 把webgl的cpp打包成.a库 // 对应 source 分支下 PluginSource/build_web_lib.bat 实现的功能 // 使用内置脚本有个好处,直接使用匹配的emscripten进行编译,不需要手动干预 public class BuildWebRenderPlugin: ScriptableObject { [MenuItem("Tools/BuildWebRenderPlugin")] public static void RunEmscriptenBuild() { // 获取 Unity 安装路径 string unityPath = EditorApplication.applicationPath; string unityRoot = Path.GetDirectoryName(unityPath); // 获取当前脚本的 MonoScript 实例 MonoScript script = MonoScript.FromScriptableObject(ScriptableObject.CreateInstance<BuildWebRenderPlugin>()); // 获取当前脚本的路径 string scriptPath = Path.GetDirectoryName(AssetDatabase.GetAssetPath(script)); // 构建文件路径 string cppFilePath = Path.Combine(scriptPath, "..", "Runtime", "Plugins", "WebGL", "RenderingPlugin.cpp"); string outputObjectFilePath = "RenderingPlugin.o"; string outputLibraryFilePath = Path.Combine(scriptPath, "..", "Runtime", "Plugins", "WebGL", "RenderingPlugin.a"); UnityEngine.Debug.Log($"RenderingPlugin.cpp Path={cppFilePath}"); // 拼接 emcc 和 emar 路径 string emccPath = Path.Combine(unityRoot, "Data", "PlaybackEngines", "WebGLSupport", "BuildTools", "Emscripten", "emscripten", "emcc.bat"); string emarPath = Path.Combine(unityRoot, "Data", "PlaybackEngines", "WebGLSupport", "BuildTools", "Emscripten", "emscripten", "emar.bat"); string pythonPath = Path.Combine(unityRoot, "Data", "PlaybackEngines", "WebGLSupport", "BuildTools", "Emscripten", "python", "python.exe"); string emConfigPath = Path.Combine(unityRoot, "Data", "PlaybackEngines", "WebGLSupport", "BuildTools", "Emscripten", ".emscripten"); UnityEngine.Debug.Log($"emcc.bat Path={emccPath}"); // 确保路径存在 if (!File.Exists(emccPath) || !File.Exists(emarPath) || !File.Exists(pythonPath) || !File.Exists(emConfigPath)) { UnityEngine.Debug.LogError("Emscripten tools not found. Please check your Unity installation."); return; } if (!File.Exists(cppFilePath)) { UnityEngine.Debug.LogError($"C++ source file not found: {cppFilePath}"); return; } // 设置环境变量 var envVariables = new System.Collections.Generic.Dictionary<string, string> { { "EMSDK_PYTHON", pythonPath }, { "EM_CONFIG", emConfigPath } }; // 执行 emcc 命令 ExecuteCommand(emccPath, $"-c \"{cppFilePath}\" -o \"{outputObjectFilePath}\"", envVariables); // 执行 emar 命令 ExecuteCommand(emarPath, $"rcs \"{outputLibraryFilePath}\" \"{outputObjectFilePath}\"", envVariables); // 删除 .o 文件 File.Delete(outputObjectFilePath); AssetDatabase.Refresh(); UnityEngine.Debug.Log($"RenderingPlugin.a 生成成功"); } private static void ExecuteCommand(string command, string arguments, System.Collections.Generic.Dictionary<string, string> envVariables) { UnityEngine.Debug.Log($"ExecuteCommand cmd={command} args={arguments}"); ProcessStartInfo startInfo = new ProcessStartInfo { FileName = command, Arguments = arguments, RedirectStandardOutput = true, RedirectStandardError = true, UseShellExecute = false, CreateNoWindow = true }; // 设置环境变量 foreach (var envVar in envVariables) { startInfo.EnvironmentVariables[envVar.Key] = envVar.Value; } using (Process process = new Process { StartInfo = startInfo }) { process.Start(); string output = process.StandardOutput.ReadToEnd(); string error = process.StandardError.ReadToEnd(); process.WaitForExit(); // 输出日志 UnityEngine.Debug.Log($"output:{output}"); if (!string.IsNullOrEmpty(error)) { UnityEngine.Debug.LogError($"error:{error}"); } } } }