{
  "schemaVersion": "0.11.0",
  "canonical": "https://www.pystone.net/notes/unity3d-webgl-overview/",
  "atlas": "https://www.pystone.net/?node=unity3d-webgl-overview#knowledge-atlas",
  "markdown": "https://www.pystone.net/notes/unity3d-webgl-overview.md",
  "context": "https://www.pystone.net/notes/unity3d-webgl-overview.context.json",
  "knowledgeVersion": "224c990773de.5fa8af6e39fa",
  "build": {
    "siteCommit": "224c990773de166d23a886306577dd90379529ce",
    "notesCommit": "5fa8af6e39fa3891d1b9b4832bfa6c4e0ecaaf0a",
    "builtAt": "1970-01-01T00:00:00.000Z",
    "version": "224c990773de.5fa8af6e39fa"
  },
  "id": "note:unity3d-webgl-overview",
  "slug": "unity3d-webgl-overview",
  "title": "P3 Unity3D WebGL",
  "type": "note",
  "visibility": "public",
  "idStability": "rename-stable",
  "author": {
    "name": "Perrin Yong",
    "profile": "https://www.pystone.net/profile/"
  },
  "publisher": {
    "name": "Perrin Yong",
    "profile": "https://www.pystone.net/profile/"
  },
  "aliases": [],
  "summary": "P3 Unity3D WebGL 基本结构",
  "contentRole": "unspecified",
  "isMoc": false,
  "mocRecognition": "none",
  "generated": false,
  "attribution": "unspecified",
  "domain": "10-计算机、信息技术与工程",
  "tags": [],
  "mocs": [],
  "contentHash": "6a49ecb470bcc4e4533d8e7af6df68179dc01bb4235dfa9ebc14e15bb10aec86",
  "assets": [
    {
      "reference": "assets/image-20250516000357725.png",
      "url": "/media/c062ffba7ed884f56f19.png",
      "mediaType": "image/png",
      "contentHash": "c062ffba7ed884f56f19afa00dc740ec13a9b9cf8ad9d5a2daeed93ba403bbc4",
      "byteLength": 85973,
      "width": 600,
      "height": 721
    },
    {
      "reference": "assets/image-20250519154647442.png",
      "url": "/media/8b28820888fd4e612294.png",
      "mediaType": "image/png",
      "contentHash": "8b28820888fd4e612294ddf293493b2f555cd80eeee53a0963cdba68bdedde7f",
      "byteLength": 31605,
      "width": 723,
      "height": 380
    },
    {
      "reference": "assets/image-20250519162928466.png",
      "url": "/media/15007e29ac88ccfa5a16.png",
      "mediaType": "image/png",
      "contentHash": "15007e29ac88ccfa5a16d6377be498125a791924c7b7088a5821ca465b9e226b",
      "byteLength": 61860,
      "width": 1467,
      "height": 923
    },
    {
      "reference": "assets/image-20250519162957254.png",
      "url": "/media/4052330b706366faa06b.png",
      "mediaType": "image/png",
      "contentHash": "4052330b706366faa06b2f125ff7e94410694c2109e877d2eb88cb6585ff0530",
      "byteLength": 29154,
      "width": 593,
      "height": 197
    }
  ],
  "headings": [
    {
      "depth": 1,
      "text": "P3 Unity3D WebGL",
      "anchor": "p3-unity3d-webgl",
      "citation": "https://www.pystone.net/notes/unity3d-webgl-overview/#p3-unity3d-webgl"
    },
    {
      "depth": 2,
      "text": "基本结构",
      "anchor": "基本结构",
      "citation": "https://www.pystone.net/notes/unity3d-webgl-overview/#%E5%9F%BA%E6%9C%AC%E7%BB%93%E6%9E%84"
    },
    {
      "depth": 3,
      "text": "1. 主要文件",
      "anchor": "1-主要文件",
      "citation": "https://www.pystone.net/notes/unity3d-webgl-overview/#1-%E4%B8%BB%E8%A6%81%E6%96%87%E4%BB%B6"
    },
    {
      "depth": 3,
      "text": "2. Build 目录核心文件",
      "anchor": "2-build-目录核心文件",
      "citation": "https://www.pystone.net/notes/unity3d-webgl-overview/#2-build-%E7%9B%AE%E5%BD%95%E6%A0%B8%E5%BF%83%E6%96%87%E4%BB%B6"
    },
    {
      "depth": 3,
      "text": "3. 加载机制",
      "anchor": "3-加载机制",
      "citation": "https://www.pystone.net/notes/unity3d-webgl-overview/#3-%E5%8A%A0%E8%BD%BD%E6%9C%BA%E5%88%B6"
    },
    {
      "depth": 3,
      "text": "4. 用户界面组件",
      "anchor": "4-用户界面组件",
      "citation": "https://www.pystone.net/notes/unity3d-webgl-overview/#4-%E7%94%A8%E6%88%B7%E7%95%8C%E9%9D%A2%E7%BB%84%E4%BB%B6"
    },
    {
      "depth": 2,
      "text": "框架解读",
      "anchor": "框架解读",
      "citation": "https://www.pystone.net/notes/unity3d-webgl-overview/#%E6%A1%86%E6%9E%B6%E8%A7%A3%E8%AF%BB"
    },
    {
      "depth": 3,
      "text": "加载流程时序图",
      "anchor": "加载流程时序图",
      "citation": "https://www.pystone.net/notes/unity3d-webgl-overview/#%E5%8A%A0%E8%BD%BD%E6%B5%81%E7%A8%8B%E6%97%B6%E5%BA%8F%E5%9B%BE"
    },
    {
      "depth": 2,
      "text": "代码解读",
      "anchor": "代码解读",
      "citation": "https://www.pystone.net/notes/unity3d-webgl-overview/#%E4%BB%A3%E7%A0%81%E8%A7%A3%E8%AF%BB"
    },
    {
      "depth": 3,
      "text": "index.html 文件",
      "anchor": "indexhtml-文件",
      "citation": "https://www.pystone.net/notes/unity3d-webgl-overview/#indexhtml-%E6%96%87%E4%BB%B6"
    },
    {
      "depth": 3,
      "text": "BuildWebGL.loader.js 文件",
      "anchor": "buildwebglloaderjs-文件",
      "citation": "https://www.pystone.net/notes/unity3d-webgl-overview/#buildwebglloaderjs-%E6%96%87%E4%BB%B6"
    },
    {
      "depth": 4,
      "text": "createUnityInstance（位于 loader.js）",
      "anchor": "createunityinstance位于-loaderjs",
      "citation": "https://www.pystone.net/notes/unity3d-webgl-overview/#createunityinstance%E4%BD%8D%E4%BA%8E-loaderjs"
    },
    {
      "depth": 3,
      "text": "framework.js 关键片段",
      "anchor": "frameworkjs-关键片段",
      "citation": "https://www.pystone.net/notes/unity3d-webgl-overview/#frameworkjs-%E5%85%B3%E9%94%AE%E7%89%87%E6%AE%B5"
    },
    {
      "depth": 2,
      "text": "Unity构建微信小程序",
      "anchor": "unity构建微信小程序",
      "citation": "https://www.pystone.net/notes/unity3d-webgl-overview/#unity%E6%9E%84%E5%BB%BA%E5%BE%AE%E4%BF%A1%E5%B0%8F%E7%A8%8B%E5%BA%8F"
    },
    {
      "depth": 3,
      "text": "构建流程",
      "anchor": "构建流程",
      "citation": "https://www.pystone.net/notes/unity3d-webgl-overview/#%E6%9E%84%E5%BB%BA%E6%B5%81%E7%A8%8B"
    },
    {
      "depth": 2,
      "text": "CDN 配置",
      "anchor": "cdn-配置",
      "citation": "https://www.pystone.net/notes/unity3d-webgl-overview/#cdn-%E9%85%8D%E7%BD%AE"
    }
  ],
  "claims": [],
  "outgoing": [],
  "incoming": [
    {
      "id": "note:game-graphics-and-runtime",
      "title": "游戏图形与运行时",
      "url": "https://www.pystone.net/notes/game-graphics-and-runtime/",
      "atlas": "https://www.pystone.net/?node=game-graphics-and-runtime#knowledge-atlas",
      "label": "游戏图形与运行时",
      "origin": "explicit",
      "humanReviewed": true,
      "context": "WebGL中的“P3 Unity3D WebGL”导航项",
      "citation": "https://www.pystone.net/notes/game-graphics-and-runtime/#webgl"
    }
  ],
  "contentMarkdown": "# P3 Unity3D WebGL\n## 基本结构\n\n### 1. 主要文件\n- index.html: 主入口页面，负责加载和运行游戏\n- Build/目录: 包含编译后的WebGL游戏核心文件\n- StreamingAssets/目录: Unity流式资产\n- TemplateData/目录: UI元素和样式资源\n\n### 2. Build 目录核心文件\n- **BuildWebGL.data**: 包含游戏资源数据(模型、贴图、音频等)\n- **BuildWebGL.framework.js**: Unity运行时框架，处理游戏逻辑\n- **BuildWebGL.loader.js**: Boot 脚本负责加载游戏并初始化Unity实例\n- **BuildWebGL.wasm**: WebAssembly二进制文件，包含编译后的C#、C++代码，提供高性能执行\n- **BuildWebGL.symbols.json**: 调试符号信息，用于错误追踪\n- index.html                   页面模板 (调用 createUnityInstance)\n\n> 旧版 Unity 只有 `UnityLoader.js`；2020+ 拆成 *loader + framework* 便于缓存与模板自定义。\n\n### 3. 加载机制\n从代码中可以看出，游戏通过以下流程加载：\n- JavaScript初始化UI元素(容器、画布、加载条等)\n- 配置Unity WebGL加载器\n- 加载器加载.wasm和资源文件\n- 显示进度条指示加载进度\n- 完成加载后启动游戏\n\n### 4. 用户界面组件\n- 进度条: 显示资源加载进度\n- 全屏按钮: 控制游戏全屏显示\n- 警告横幅: 显示错误或警告信息\n\n## 框架解读\n\n### 加载流程时序图\n\n```html\nindex.html\n   │ create <script src=\"*.loader.js\">\n   ▼\nloader.js\n   ├─ ① 解析 config (data / wasm / framework 路径)\n   ├─ ② 动态 <script> 载入 framework.js\n   ├─ ③ fetch .data   ─┐\n   ├─ ④ fetch .wasm   ─┤ 并行下载 → onProgress()\n   ├─ ⑤ fetch StreamingAssets -┘\n   │\nframework.js\n   ├─ ⑥ 构造 Module = { preInit, instantiateWasm, onRuntimeInitialized … }\n   ├─ ⑦ 调 WebAssembly.instantiateStreaming(.wasm, importObj)\n   └─ ⑧ 运行时触发 Module.onRuntimeInitialized()\n           │\n           ▼\nreturn Promise<unityInstance> 给用户\n```\n\n```php\n┌────────────────────────────────────────────────────────────────────┐\n│  L0  ──  Host Shell                                              │\n│  (index.html + CDN/小程序容器)                                    │\n│  - 静态 UI（canvas, progress div, full-screen btn …）            │\n│  - <script src=\"BuildWebGL.loader.js\">                           │\n└────────────────────────────────────────────────────────────────────┘\n            │  createUnityInstance(canvas, userConfig, onProgress)\n            ▼\n┌────────────────────────────────────────────────────────────────────┐\n│  L1  ──  Boot / Loader 层      <BuildWebGL.loader.js>            │\n│                                                                    │\n│  ① 组装 **Module 原型**  (变量 c)                                 │\n│     - 默认字段：canvas / webglContextAttributes / print / abort…  │\n│     - 混入 userConfig (dataUrl / codeUrl / companyName …)          │\n│                                                                    │\n│  ② “环境钩子”                                                     │\n│     - 禁用右键拖拽  (disabledCanvasEvents)                         │\n│     - 全局 try-catch           → 函数 **t(event)**                 │\n│     - 全屏尺寸同步             → webkitfullscreenchange            │\n│     - 统一清理栈              → c.deinitializers[]                │\n│                                                                    │\n│  ③ 进度聚合器 **b(name, progEvt)**                                │\n│     - 维护 c.downloadProgress[name]                                │\n│     - 计算整体进度 → onProgress(0–0.9)                             │\n│                                                                    │\n│  ④ 动态拉取 **framework.js**   → 函数 **w()**                     │\n│     - <script> 注入 + MIME 诊断                                    │\n│     - 加载完执行： unityFramework(c)                               │\n│                                                                    │\n│  **向下接口** : 调用 w() 之后，控制权交给 framework.js            │\n└────────────────────────────────────────────────────────────────────┘\n            ▼\n┌────────────────────────────────────────────────────────────────────┐\n│  L2  ──  Glue / Framework 层   <BuildWebGL.framework.js>          │\n│                                                                    │\n│  ①  完整构造 **Module** (仍是同一个对象 c)                        │\n│      • locateFile   → 把 “build.wasm” 重定向到 c.codeUrl          │\n│      • preRun / postRun 数组                                       │\n│      • instantiateWasm(imports, cb) ↴                              │\n│          fetch .wasm  → WebAssembly.instantiate   (可自定义)       │\n│                                                                    │\n│  ②  网络包装 **fetchWithProgress(url, opts)**                     │\n│      • readBodyWithProgress → onProgress(chunk)                    │\n│      • 支持 enableStreamingDownload (边下边解包 .data)            │\n│                                                                    │\n│  ③  下载 & 解包 **.data**                                         │\n│      • 推入 Module.preRun：                                        │\n│           - fetch dataUrl                                          │\n│           - 解析 UnityFS header                                    │\n│           - FS_createDataFile  写入 MEMFS                         │\n│                                                                    │\n│  ④  ✱**SystemInfo**✱ 运行前检测                                   │\n│      hasWebGL / hasWasm / hasThreads / GPU Vendor …                │\n│                                                                    │\n│  ⑤  **Error 路由链**                                              │\n│      abort()  ─►  Module.abortHandler → m() → g() →  UI/alert      │\n│      JS Error ─►  window.onerror          (同上)                   │\n│                                                                    │\n│  **向下接口** : 当 Wasm + 资源全部 OK → 触发                       │\n│       Module.onRuntimeInitialized()                                │\n│                                                                    │\n│  此时 loader 的 Promise resolve(unityInstance)                    │\n└────────────────────────────────────────────────────────────────────┘\n            ▼\n┌────────────────────────────────────────────────────────────────────┐\n│  L3  ──  Runtime / Wasm 层     <BuildWebGL.wasm>                  │\n│                                                                    │\n│  - IL2CPP 转 C++ 转 wasm32 指令                                   │\n│  - Unity Runtime (渲染/物理/Audio…)                               │\n│  - _start() → UnityMain()                                         │\n│  - 导出：  SendMessage / SetFullscreen / quit / _getMemInfo       │\n│                                                                    │\n│  · 通过 import 表调用 glue 中的                                   │\n│      • glBindBuffer / wasmFS / pthread…                           │\n│  · 如果抛 fatal → `abort()` → L2 错误链                           │\n└────────────────────────────────────────────────────────────────────┘\n```\n\n## 代码解读\n### index.html 文件\n\n这是主入口文件，包含初始化UI元素和启动游戏的核心代码：\n\n```html\n// ...页面HTML结构...\n\n  <canvas id=\"unity-canvas\" width=\"960\" height=\"600\"></canvas>\n\n\n\n\n\n\n\n\n\n\n\n```\n\n核心JavaScript初始化代码（通常位于index.html底部）：\n\n```javascript\n// 定义配置参数\nvar container = document.querySelector(\"#unity-container\");\nvar canvas = document.querySelector(\"#unity-canvas\");\nvar loadingBar = document.querySelector(\"#unity-loading-bar\");\nvar progressBarFull = document.querySelector(\"#unity-progress-bar-full\");\nvar fullscreenButton = document.querySelector(\"#unity-fullscreen-button\");\nvar warningBanner = document.querySelector(\"#unity-warning\");\n\n// 移动设备检测和配置\nif (/iPhone|iPad|iPod|Android/i.test(navigator.userAgent)) {\n  // 移动设备配置...\n}\n\n// 创建Unity实例配置\nvar buildUrl = \"Build\";\nvar loaderUrl = buildUrl + \"/BuildWebGL.loader.js\";\nvar config = {\n  dataUrl: buildUrl + \"/BuildWebGL.data\",\n  frameworkUrl: buildUrl + \"/BuildWebGL.framework.js\",\n  codeUrl: buildUrl + \"/BuildWebGL.wasm\",\n  streamingAssetsUrl: \"StreamingAssets\",\n  companyName: \"DefaultCompany\",\n  productName: \"Stick Guys TD\",\n  productVersion: \"1.0\",\n  showBanner: unityShowBanner,\n};\n\n// 加载脚本和启动Unity\nvar script = document.createElement(\"script\");\nscript.src = loaderUrl;\nscript.onload = () => {\n  createUnityInstance(canvas, config, (progress) => {\n    progressBarFull.style.width = 100 * progress + \"%\";\n  }).then((unityInstance) => {\n    loadingBar.style.display = \"none\";\n    fullscreenButton.onclick = () => {\n      unityInstance.SetFullscreen(1);\n    };\n  }).catch((message) => {\n    alert(message);\n  });\n};\ndocument.body.appendChild(script);\n```\n\n### BuildWebGL.loader.js 文件\n\n这个文件包含Unity WebGL加载器的核心代码：\n\n```javascript\n// 定义全局UnityLoader对象\nvar UnityLoader = {\n  // 初始化相关配置和方法\n  Compression: {\n    // 解压缩方法定义\n  },\n\n  UnityCache: {\n    // 缓存管理相关代码\n  },\n\n  // 加载和解析.wasm文件的核心方法\n  SystemInfo: function() {\n    // 检测浏览器兼容性\n  },\n\n  // 内存管理相关方法\n  cachedFetch: function() {\n    // 资源加载和缓存\n  },\n\n  // 创建Unity实例的方法\n  instantiateModule: function(binary, moduleOptions) {\n    // 实例化WebAssembly模块\n  }\n}\n\n// createUnityInstance函数 - 最关键的函数，负责:\nfunction createUnityInstance(canvas, config, onProgress) {\n  // 1. 设置进度回调函数\n  // 2. 根据配置加载BuildWebGL.data（游戏资源）\n  // 3. 加载BuildWebGL.framework.js（Unity运行时框架）\n  // 4. 加载BuildWebGL.wasm（WebAssembly二进制代码）\n  // 5. 初始化Unity实例并返回Promise\n}\n```\n\n\n#### `createUnityInstance`（位于 loader.js）\n\n```javascript\nexport function createUnityInstance(canvas, cfg, onProgress) {\n  const { frameworkUrl, dataUrl, codeUrl } = cfg;\n\n  // (1) 注入样式 && 预加载条\n  let pr = 0;\n  const setProgress = p => { pr = p; onProgress?.(p); };\n\n  // (2) 下载 Framework (await)\n  await import(/* webpackIgnore: true */ frameworkUrl);\n\n  // (3) 构造 Module 对象 (由 framework 定义全局变量)\n  const Module = window.Module || {};\n  Module.canvas = canvas;\n  Module.preInit = [() => setProgress(0.1)];\n  Module.onRuntimeInitialized = () => setProgress(1.0);\n\n  // (4) 设置文件路径，Framework 内 fetch() 时会用\n  Module.dataUrl = dataUrl;\n  Module.codeUrl = codeUrl;\n\n  // (5) 调用 createUnityInstanceImpl (framework.js 注入的函数)\n  return window.createUnityInstanceImpl(Module, setProgress);\n}\n```\n\n**要点**\n* 进度条纯 JS 计数，无 GPU；你可 override `setProgress` 做自定义动画。\n* Framework 和 Wasm 并行下载，通过 `instantiateStreaming` 边下边编译。\n\n---\n\n### `framework.js` 关键片段\n\n```javascript\nvar Module = {\n  preRun:   [],\n  postRun:  [],\n  preInit:  [],\n  wasmBinaryFile:  config.codeUrl,\n  locateFile: (path) => {\n    if (path.endsWith('.data')) return config.dataUrl;\n    return path;\n  },\n  instantiateWasm: (imports, successCb) => {\n    fetch(Module.wasmBinaryFile)\n      .then(r => r.arrayBuffer())\n      .then(bin => WebAssembly.instantiate(bin, imports))\n      .then(out => successCb(out.instance, out.module));\n    return {};           // Emscripten stub\n  }\n};\n```\n\n* **`instantiateWasm`** 可被你改写为自行缓存 .wasm 或使用 `WebAssembly.compileStreaming`。\n* **`asmLibraryArg`**：Emscripten 生成，包含所有导入（`memory`, `table`, `glBindBuffer`…）。\n\n## Unity构建微信小程序\n\n微信小游戏开发指南:\nhttps://developers.weixin.qq.com/minigame/dev/guide/develop/start.html\n\nUnity转微信小游戏:\nhttps://github.com/wechat-miniprogram/minigame-unity-webgl-transform\n\n### 构建流程\nhttps://wechat-miniprogram.github.io/minigame-unity-webgl-transform/Design/Transform.html\n\n微信小程序申请测试号, 即可生成测试用的AppID\n\n生成后, 可将webgl 目录 作为资源包, 上传到CDN服务器. Github Pages 可以作为CDN服务器.\n\n![](assets/image-20250516000357725.png)\n\n\n## CDN 配置\n阿里云OSS 上传webgl 文件夹\n\n![](assets/image-20250519154647442.png)\n\n小程序测试号需要配置合法域名\n\n![](assets/image-20250519162928466.png)\n\n导出配置CDN和AppID\n\n![](assets/image-20250519162957254.png)\n"
}
