Turbulenz Engine 资产流水线实战指南:转换工具链、Mapping Table 与资源服务优化

📅 发布时间:2026/10/14 16:27:09
Turbulenz Engine 资产流水线实战指南:转换工具链、Mapping Table 与资源服务优化
游戏开发图形学【免费下载链接】turbulenz_engineTurbulenz is a modular 3D and 2D game framework for making HTML5 powered games for browsers, desktops and mobile devices.项目地址https://gitcode.com/gh_mirrors/tu/turbulenz_engine点击查看免费下载本文基于 Turbulenz Engine 官方文档 assets_guide.rst 整理完整覆盖资产转换工具的使用方式dae2json、cgfx2json、json2tar 等、基于回调的资产加载流程、静态资源缓存策略以及如何创建和调试 Mapping Table。读完本文后你可以把.dae模型与.cgfx着色器转换为引擎可加载的 JSON 资产搭建带内容哈希命名的staticmax/缓存目录并通过 Mapping Table 实现资产版本管理与缓存失效控制。一、资产工具链总览Turbulenz SDK 内置一套面向开发者的工具集绝大多数工具使用 Python 编写跨平台运行。文档列出的工具及用途如下工具用途dae2json将建模软件导出的 Collada.dae文件转换为 JSON 格式cgfx2json将 CgFX 格式着色器转换为 JSON 格式原生可执行文件material2json将 .material 文件转换为 JSON 格式effect2json将 .effect 文件转换为 JSON 格式json2tar为 JSON 资产引用的二进制资源生成 TAR 归档json2json合并 JSON 资产文件maketzjs从 JavaScript 源码生成 .tzjs 或 .js 文件可选优化输出makehtml为 maketzjs 构建的代码生成 HTML 页面可传入自定义 HTML 覆盖默认模板xml2json将 XML 资产转换为结构化 JSON 资产json2stats统计并报告 JSON 资产的指标json2txt从 JSON 资产生成纯文本或 HTML 输出注意cgfx2json是原生可执行文件运行方式与 Python 工具不同见下文第四节。运行任一 Python 工具的标准步骤点击开始菜单中的 Run environment 快捷方式启动虚拟环境执行*TOOLNAME* *ARGS*其中TOOLNAME替换为上表任一工具名ARGS为该工具的参数不带参数直接运行工具时会打印该工具支持的全部命令行选项。文档约定后续示例均在*SDKINSTALLDIR*下执行其中*GAMEDIR*为*SDKINSTALLDIR*/apps/sampleapp。资产转换工具的通用选项各 Python 转换工具共享一组 Standard Asset Tool Options常用的有--verbose, -v 详细输出 --silent, -s 静默运行 --metrics, -m 输出资产指标 --json_indentSIZE, -j JSON 输出缩进空格数默认 0 --include-typeTYPE, -I 仅导出指定类别的对象 --exclude-typeTYPE, -E 排除指定类别的对象 --assetsPATH, -a 资产根目录路径 --urlURL, -u 为所有资产引用添加的 URL 前缀 --inputINPUT, -i 输入文件 --outputOUTPUT, -o 输出文件其中-I/-E支持的类别包括geometries、nodes、animations、images、effects、materials、lights、physicsmaterials、physicsmodels、physicsnodes。使用这些选项可能产生不完整的资产需要在运行时修补。二、资产查看与反汇编Asset Viewer 与 DisassemblerAsset ViewerAsset Viewer 用于快速查看游戏资产并附带一组调试选项。通过本地开发服务器的 View Asset 面板即可打开某个资产。更详细的功能见 Viewer 工具文档其访问形式为http://x.x.x.x:8070/viewer/game/asset.jsonViewer 支持线框渲染Wireframe、调试着色器选择、灯光范围绘制、骨骼/插值器绘制、场景节点层级、物理几何体与范围着色运动学蓝色、动态绿色、静态红色、法线/切线/副法线可视化、动画速度缩放等。注意Asset Viewer 需要已安装 Turbulenz Engine 才能使用且它与 SDK 内置的引擎版本兼容。Asset DisassemblerDisassembler 用于查看并浏览资产的 JSON 树结构入口是本地服务器 Metrics 列表中请求过的 JSON 资产点击其中任意一项即可进入。访问形式为见 Disassembler 工具文档http://x.x.x.x:8070/disassemble/game/asset.json渲染出的 JSON 树可通过DEPTH树渲染深度默认 2、LIST列表裁剪默认 5、DICT字典裁剪默认 5三个参数控制。当当前资产属性中引用了其他 JSON 资产时树上会提供超链接点击即可在 Disassembler 中继续查看。三、将 Collada 模型转换为 JSON以 SDK 自带的 assets/models/duck.dae 为例用dae2json完成转换按前述方式启动虚拟环境执行dae2json -i assets/models/duck.dae -o apps/sampleapp/staticmax/duck.dae.json生成文件名为duck.dae.json即转换后的 JSON 模型资产Turbulenz Engine 可以直接加载。提示如需以人类可读的缩进格式输出 JSON 便于调试添加-j SIZE选项SIZE 为缩进空格数dae2json -j 4 -i assets/models/duck.dae -o apps/sampleapp/staticmax/duck.dae.json很多.dae资产除了几何数据外还包含动画、物理等数据dae2json提供-I选项提取指定类别。例如从 assets/models/Seymour.dae 中仅提取动画数据dae2json -I animations -i assets/models/Seymour.dae -o apps/sampleapp/staticmax/Seymour_animation_only.dae.json结果文件只包含动画。加载场景与资产的完整示例可参考load model样本见 samples/templates/load_model.html或使用 Protolib 提供的资产加载与场景管理框架快速上手。四、将 CgFX 着色器转换为 JSONcgfx2json与其他工具最大的区别是它是原生可执行文件。以 SDK 自带的 assets/shaders/generic3D.cgfx 为例tools/bin/*PLATFORM*/cgfx2json -i assets/shaders/generic3D.cgfx -o apps/sampleapp/staticmax/generic3D.cgfx.json转换后即可被引擎加载使用。推荐的开发流程是修改样本中使用的着色器源码 → 用 cgfx2json 构建 → 用样本代码验证结果。该工具完整的选项说明见 cgfx2json 文档。从构建系统源码 scripts/buildassets.py 可以看到两个实现细节构建时记录cgfx2json的版本check_version写入cgfx2json.version版本变化会触发全部着色器资产重编译Cgfx2JsonTool.check_external_deps会执行cgfx2json -i src -M列出着色器的外部依赖如.cgh头文件只要任一依赖的修改时间晚于输出文件该着色器就会被重新编译——这解释了为什么修改common.cgh之类的公共头文件后引用它的着色器都会自动重建。五、将二进制资产打包进归档json2tar对文本类格式各转换工具通常提供压缩或优化选项对于二进制文件则可选用json2tar它会扫描 JSON 文件中的引用把对应的二进制文件归档到 TAR 中。这主要起定位资源并聚合的作用同时可能带来压缩收益。仍以duck.dae为例启动虚拟环境仅在需要时重建模型dae2json -i assets/models/duck.dae -o apps/sampleapp/staticmax/duck.dae.json打包json2tar -i apps/sampleapp/staticmax/duck.dae.json -o apps/sampleapp/staticmax/duck.tar -a assets注意本例中-a指向资产根目录是因为duck.dae内部的资源引用相对于该目录例如textures/duck.png。其他文件中的引用可能相对于资产文件自身位置。该工具足够灵活你可以根据自己的资产生成方式调整路径。六、使用回调加载资产浏览器游戏中从服务器请求资产与数据时回调是最核心的机制。本节示例创建两个文件asset_loading.js与asset_loading.html若项目目录缺少jslib需先从安装目录复制过来。asset_loading.js的骨架如下/*{{ javascript(jslib/utilities.js) }}*/ /*{{ javascript(jslib/camera.js) }}*/ /*{{ javascript(jslib/drawprimitives.js) }}*/ TurbulenzEngine.onload function onloadFn() { if (!TurbulenzEngine.version) { // No version is available, terminating early return; } // Create the GraphicsDevice interface with no parameters var graphicsDeviceParameters { }; var graphicsDevice TurbulenzEngine.createGraphicsDevice(graphicsDeviceParameters); // Create the MathDevice interfaces with no parameters var mathDeviceParameters { }; var mathDevice TurbulenzEngine.createMathDevice(mathDeviceParameters); // The draw variables var draw null; var camera Camera.create(mathDevice); var clearColor [1.0, 1.0, 0.0, 1.0]; // // INSERT CODE TO REQUEST ASSETS HERE // var intervalID; function loadingLoopFn() { // // INSERT CODE TO WAIT FOR ASSETS TO FINISH LOADING HERE // } // Call the loadingLoopFn, 10 fps intervalID TurbulenzEngine.setInterval(loadingLoopFn, 1000 / 10); function destroyFn() { // Clear the interval TurbulenzEngine.clearInterval(intervalID); camera null; draw null; clearColor null; // // INSERT CODE TO DESTROY HERE // TurbulenzEngine.flush(); } TurbulenzEngine.onunload destroyFn; };在REQUEST ASSETS HERE注释后填入请求代码。注意两条加载路径的回调风格不同着色器走 Native Engine 接口的request函数回调收到 JSON 字符串纹理走GraphicsDevice.createTextureonload作为参数传入// This will be our counter to detect when our assets have been loaded var totalAssetsRemaining 0; // Create empty arrays to insert the assets into var textures {}; var shaders {}; // List of material parameters var materialParams []; // Function called when a shader is loaded function shaderLoadedCallback(jsonData) { // Shader data passed to function as a JSON object if (jsonData) { var shaderParameters JSON.parse(jsonData); shaders[shaderParameters.name] graphicsDevice.createShader(shaderParameters); totalAssetsRemaining - 1; } } // Create the parameters to load a texture. // In this case the onload callback is passed as a parameter var crateTextureParameters { src : textures/crate.jpg, mipmaps : true, onload : function (texture) { textures[this.src] texture; totalAssetsRemaining - 1; } }; // stonesTextureParameters、brickTextureParameters 与上面结构相同 // 分别对应 textures/stones.jpg 与 textures/brick.png // 2x Shaders, 3x Textures totalAssetsRemaining 5; // Start texture load graphicsDevice.createTexture(crateTextureParameters); graphicsDevice.createTexture(stonesTextureParameters); graphicsDevice.createTexture(brickTextureParameters); // Start shader load TurbulenzEngine.request(shaders/generic2D.cgfx.json, shaderLoadedCallback); TurbulenzEngine.request(shaders/generic3D.cgfx.json, shaderLoadedCallback);在destroyFn的DESTROY HERE注释后补充销毁代码textures null; shaders null; materialParams null; crateTextureParameters null; stonesTextureParameters null; brickTextureParameters null;等待期间绘制彩色背景并轮询totalAssetsRemaining归零后打印资产清单、初始化绘制并切换到新的绘制函数。loadingLoopFn中加入if (graphicsDevice.beginFrame()) { graphicsDevice.clear(clearColor, 1.0, 0.0); graphicsDevice.endFrame(); } if (totalAssetsRemaining 0) { TurbulenzEngine.clearInterval(intervalID); // The technique we will use to draw the textures var technique2DName textured2D; var technique2D null; var shader2D null; var resultString Loaded:brdldtShaders/dtddul; for(var s in shaders) { if (shaders.hasOwnProperty(s)) { var shader shaders[s]; var technique shader.getTechnique(technique2DName); if (technique) { technique2D technique; shader2D shader; } resultString li s /li; } } resultString /ul/dddtTextures/dtddul; for(var t in textures) { if (textures.hasOwnProperty(t)) { if (shader2D) { materialParams[materialParams.length] { clipSpace: [2.0 / graphicsDevice.width, -2.0 / graphicsDevice.height, -1.0, 1.0], diffuse: textures[t]}; } resultString li t /li; } } resultString /ul/dd/dl; if (shader2D) { // Draw the textures if a 2D shader exists draw DrawPrimitives.create(graphicsDevice, shaders/); draw.setTechnique(technique2D, true); } var outputElem document.getElementById(output); if (outputElem) { outputElem.innerHTML resultString; } // Draw at 30 fps TurbulenzEngine.setInterval(drawTexturesFn, 1000 / 30); }为了让加载结果输出到页面需要自定义 HTML 模板asset_loading.html/*{% extends default %}*/ /*{% block tz_app_title %}*/ Asset Loading /*{% endblock %}*/ /*{% block tz_app_title_name %}*/ Asset Loading /*{% endblock %}*/ /*{% block tz_app_html_controls %}*/ div idoutput/div /*{% endblock %}*/此时直接运行只会看到黄色背景——因为资产尚未就位。加载完成后用刚加载的着色器绘制纹理在loadingLoopFn上方定义function drawTexturesFn() { if (graphicsDevice.beginFrame()) { graphicsDevice.clear([0.0, 0.0, 0.0, 1.0], 1.0, 0.0); if (draw) { var length materialParams.length; var dim graphicsDevice.height / length; var posA [0, 0]; var posB [dim, dim]; for(var i 0; i length; i 1) { posA[1] (dim * i); posB[1] (dim * (i 1)); draw.update2DTex(posA, posB); draw.updateParameters(materialParams[i]); draw.dispatch(camera); } } graphicsDevice.endFrame(); } }最后把需要的资产复制到工程目录textures从*SDKINSTALLDIR*/assets/textures复制 crate.jpg、stones.jpg、brick.png 到textures/目录shaders用cgfx2json将 generic3D.cgfx 和 generic2D.cgfx 编译到shaders/generic3D.cgfx.json与shaders/generic2D.cgfx.json。注意Turbulenz JavaScript Library 提供了纹理、着色器、资源的管理器类 API如TextureManager、ShaderManager可以避免像本例这样手动请求资产加载场景的完整方法可参考scene loading样本见 samples/templates/scene_loading.html。七、资产服务Asset Serving的关键考量浏览器游戏的资源访问方式与本地运行差异巨大资产组织不当会造成低效。请求文件时应关注以下方面缓存控制浏览器缓存可避免对未变更文件的重复传输。服务器端应用通常有staticmax目录存放很少变更的静态文件服务器会尽量让浏览器长期缓存这些文件。但你对用户浏览器缓存的控制有限缓存随时可能被清空届时所有数据需重新请求。资产分组可以按单文件、合并文件或打包二进制等不同粒度请求。例如把模型与其纹理组合成完整资产合并文件或二进制包保证其他资源变更不影响它更高一层的分组可以用 mapping table 指定启动某关卡所需的资产列表。资产流水线化细粒度请求允许并行发起多个请求甚至来自多个服务器浏览器可并发处理资产处理可以在全部数据下载完成前开始处理中发现的依赖也可以并行请求。资产更新与新增内容通过mapping tables更新资产——mapping table 把源资产名关联到具体转换后的文件修改表即可指向更新后的资产而无需改源码。新增内容只需上传新资产并由更新后的代码请求。可选的延迟加载若某资产只有游戏进行到某时才需要可编写代码在合适时机如按游戏进度再下载。可用缓存总量不同浏览器的缓存大小不一可能导致加载性能差异应在多个浏览器上测试一致性不要依赖缓存功能来实现游戏逻辑。缩短启动时间结构上保证只下载最小数据集即可开始运行游戏结合以上技术改善用户体验。八、创建 Mapping TableTurbulenz 引擎中mapping table 用于把对命名资产的请求重定向到实际可用的文件。它的用途包括作为实质相同的实体的资产版本化系统通过源资产名访问转换后的文件如源名duck.dae转换后资产为duck.dae.json;实现缓存控制更新 mapping table 可使不同的替代资产被载入缓存。部署游戏时 mapping table 是必需的Turbulenz Services 用它确定需要部署哪些资产部署后Services 会修改 mapping table 指向已部署的资产。mapping table JSON 文件示例{ version: 1.0, urnmapping: { textures/crate.jpg: rOHfqp7mY3khNQCy245e1Jw.jpg, models/cube.dae: r3QnJu7UdYPC5LIy52dxSgw.json, shaders/generic3D.cgfx: rjxN8UefxjzRnqj85E-ylLA.json, default.effects: rQvPPmnvenV63V6xXpLK26Q.json, shaders/standard.cgfx: rkuK-8VukqwJ58gbxarF2fg.json, default.materials: rX2FUZffIpiNCu-aN2Hm6gQ.json, shaders/generic2D.cgfx: rgR4TUjrppkd8T3fxgQTIgw.json }, }它本质上是一组源资产名 → 处理后资产名的键值对version表示 mapping table 格式版本号。游戏代码中请求该文件时它会被转换为 JavaScript 对象。样本代码中的等价写法// Create a mapping so that were loading the processed assets var mapping { shaders/defaultrendering.cgfx: shaders/defaultrendering.cgfx.json, shaders/standard.cgfx: shaders/standard.cgfx.json, shaders/debug.cgfx: shaders/debug.cgfx.json, models/duck.dae: models/duck.dae.json }; textureManager.setPathRemapping(mapping, ); shaderManager.setPathRemapping(mapping, ); sceneLoader.setPathRemapping(mapping, );为某个管理器如 shader manager设置 mapping table 后任何对路径shaders/standard.cgfx的着色器请求都会被重定向到shaders/standard.cgfx.json。警告生成唯一的资产名是 mapping table 的关键步骤。若两个资产同名但内容不同请求会命中浏览器缓存里的旧文件而不是服务器上的新文件用户必须手动清缓存这不可取。正确做法是基于文件内容哈希保留唯一标识。注意mapping table 中目的端处理后资产名必须URL 安全因为本地服务器会按该名称请求文件在 Turbulenz Hub 上映射会在托管文件前被处理。若使用 base64 编码生成文件哈希务必确保其 URL 安全如 Python 的base64.urlsafe_b64encode。MappingTables 还支持通过可选overrides属性覆盖默认映射表详见 MappingTable API 文档。为自己的应用创建 mapping table 的完整步骤确定使用的资产。例如 Sample App 使用debug.cgfx、defaultrendering.cgfx、standard.cgfx、duck.png、duck.dae。注意这些文件的请求方式并不相同着色器通过shaders/standard.cgfx路径请求而textures/duck.png则从 textures 目录请求。mapping table 可以统一映射到不同的目录结构或扁平结构。按游戏代码实际请求的名字列出清单shaders/debug.cgfx shaders/defaultrendering.cgfx shaders/standard.cgfx textures/duck.png models/duck.dae映射到staticmax/目录。仿照 apps/sampleapp 中的用法把希望浏览器尽量长期缓存的文件映射到staticmax/目录目录名即尽量长缓存的约定标记。浏览器缓存的控制是有限的没有保证缓存文件下次请求时仍然存在缓存可能被填满、条目被移除、被自动/手动清空默认行为是先使用缓存文件未命中才回落到文件服务器。为文件生成唯一名并复制到staticmax/。命名方式由你的构建流程决定但合理的时机是在把资产转换为 Turbulenz 兼容格式时同步生成。例如shaders/debug.cgfx.json - staticmax/2Hohp_autOW0WbutP_NSUw.json shaders/defaultrendering.cgfx.json - staticmax/4HdTZBhuheSPYHe1vmygYA.json shaders/standard.cgfx.json - staticmax/5Yhd75LjDeV3WEvRsKnGSQ.json textures/duck.png - staticmax/f1Ay_x_BbiiUGm_qQdfSWQ.png models/duck.dae.json - staticmax/grhvty7RHO1eksdUf0wlAw.json注意二进制.png保留扩展名这样 mime-type 得以保留、文件类型能被正确处理也让你能在本地服务器中查看和反汇编资产。这一规则在构建脚本 scripts/buildassets.py 中有直接体现get_file_hash用md5摘要后以urlsafe_b64encode(...).rstrip()生成 URL 安全的 20 位哈希串install()按逻辑名_哈希.扩展名的物理名如E91_Ym3MMRjHxq4mvC7qQQ.png复制文件并构建mapping字典最终把{urnmapping: mapping}写入mapping_table.json。在*GAMEDIR*目录创建mapping_table.json{ urnmapping: { shaders/debug.cgfx: 2Hohp_autOW0WbutP_NSUw.json, shaders/defaultrendering.cgfx: 4HdTZBhuheSPYHe1vmygYA.json, shaders/standard.cgfx: 5Yhd75LjDeV3WEvRsKnGSQ.json, textures/duck.png: f1Ay_x_BbiiUGm_qQdfSWQ.png, models/duck.dae: grhvty7RHO1eksdUf0wlAw.json }, version: 1.0 }注意不需要手动在资产名前加staticmax/前缀mapping table API 会代劳。加载并读取映射表。样本中类似代码如下在请求任何资产之前必须先等这个loadAssets被调用var mappingTable; var mappingTableReceived function mappingTableReceivedFn(mappingTable) { textureManager.setPathRemapping(mappingTable.urlMapping, mappingTable.assetPrefix); shaderManager.setPathRemapping(mappingTable.urlMapping, mappingTable.assetPrefix); soundManager.setPathRemapping(mappingTable.urlMapping, mappingTable.assetPrefix); sceneLoader.setPathRemapping(mappingTable.urlMapping, mappingTable.assetPrefix); loadAssets(); }; var gameSessionCreated function gameSessionCreatedFn(gameSession) { mappingTable TurbulenzServices.createMappingTable(gameSession, mappingTableReceived); }; var gameSession TurbulenzServices.createGameSession(gameSessionCreated);一个完整的loadAssets示例var renderer; var scene Scene.create(mathDevice); var sceneLoader SceneLoader.create(); var loadAssets function loadAssetsFn() { // Start loading assets // Renderer for the scene. renderer DefaultRendering.create(graphicsDevice, mathDevice, shaderManager, effectManager); renderer.setGlobalLightPosition(mathDevice.v3Build(0.5, 100.0, 0.5)); renderer.setAmbientColor(mathDevice.v3Build(0.3, 0.3, 0.4)); // Load objects into the scene using a scene loader sceneLoader.load({ scene : scene, assetPath : models/duck.dae, graphicsDevice : graphicsDevice, textureManager : textureManager, effectManager : effectManager, shaderManager : shaderManager, requestHandler: requestHandler, append : false, dynamic : true }); };构建并运行开发版本构建方法见 getting started 指南。把游戏加入本地服务器打开 metrics 页面验证应当看到请求的文件确实来自staticmax/目录。mapping table 与staticmax目录结合让你在代码不感知服务器文件结构的前提下自由选择缓存哪些资产。游戏中可以使用多张 mapping table例如按关卡分组或提供多套文件别名。源码印证MappingTable 的实现仓库中的 tslib/services/mappingtable.ts 即MappingTable的 TypeScript 源。要点包括getURL(assetPath, missingCallbackFn)按当前 profile 的 overrides沿parent链递归→ 默认urnmapping→ 兜底assetPrefix assetPath的顺序解析 URL未命中时调用missingCallbackFn并返回兜底 URL支持map、alias、setProfile、getCurrentProfile等方法创建时默认 profile 取自TurbulenzEngine.getSystemInfo().platformProfile引擎根据平台将其设为desktop、tablet或smartphone见 tslib/webgl/turbulenzengine.ts参数mappingTablePrefix会前缀到所有物理路径assetPrefix只加到表中未找到的路径上。apps/sampleapp/scripts/sampleappmain.js 展示了真实应用的接法var gameSessionCreated function gameSessionCreatedFn(gameSession) { var defaultMappingSettings { mappingTablePrefix: staticmax/, assetPrefix: missing/, mappingTableURL: mapping_table.json }; TurbulenzServices.createMappingTable(requestHandler, gameSession, mappingTableReceived, defaultMappingSettings); };这里mappingTablePrefix: staticmax/正是文档所说API 会自动加前缀的来源assetPrefix: missing/则让未映射的资产请求落到missing/目录便于在本地服务器日志中一眼发现缺失资产。应用清单 apps/sampleapp/manifest.yaml 中deploy_files与staticmax/*一起被部署并声明mapping_table: mapping_table.json与上述机制相互印证。用 maptool 为 mapping table 添加 Profilemaptool 命令可在资产构建过程中操作mapping_table.json、添加 profiles。它面向主构建生成默认资产、次构建在特定条件下替代默认集的场景。例如主构建生成output/ mapping_table.json staticmax/ texture1_ae7646ef.png (for asset texture1.png) texture2_e6a67fe4.png (for asset texture2.png) ...而面向高分辨率显示的次构建生成output/ mapping_table_hires.json staticmax/ texture1_hires_d64eb8a0.png (for asset texture1.png) texture2_hires_37df4f8e.png (for asset texture2.png) ...执行maptool -o mapping_table_final.json --profile hires,mapping_table_hires.json得到mapping_table_final.json默认 profile 指向texture1_ae7646ef.png与texture2_e6a67fe4.pnghiresprofile 指向texture1_hires_d64eb8a0.png与texture2_hires_37df4f8e.png。--profile name,maptable.json[,parent]可多次使用parent指定继承的已有 profile。对应的 JSON 结构overrides段在 MappingTable API 文档 中有完整定义每个 profile 的urnmapping只需列出要覆盖的路径查不到时沿parent递归回退无parent则落到默认表。运行时可用mappingTable.setProfile(hires)切换 profile。九、调试 Mapping Table生成 mapping table 后资产在游戏中不出现时可尝试以下方法同样适用于代码中手工构造的 mapping如 Sample App 的mapping变量。关键是查看实际发出的资产请求确认目标资产存在——有些加载代码会尝试加载文件中不存在或未使用的引用。方法一传入自定义 request 函数。若干 JavaScript API 允许传入自定义request函数包括 jslib 的resourceloader和样本脚本中的sceneloader。重载它即可追踪每次资产请求及其重定向目标var urlMapping { shaders/debug.cgfx : 2Hohp_autOW0WbutP_NSUw.json, shaders/defaultrendering.cgfx : 4HdTZBhuheSPYHe1vmygYA.json, shaders/standard.cgfx : 5Yhd75LjDeV3WEvRsKnGSQ.json }; var missingMapping []; function addMissingMappingFn(assetName) { missingMapping[missingMapping.length] assetName; } var request function requestFn(assetName, onload) { return TurbulenzEngine.request(mappingTable.getURL(assetName, addMissingMappingFn), onload); };随后可打印missingMapping数组或分阶段调试并在监视列表中查看变量。这正是getURL第二参数missingCallbackFn的用途——文档明确它用于调试release 构建不应出现缺失资产。注意并非所有基于TurbulenzEngine.request的 API 都支持传入自定义 request 函数这些情况需要手工调试。方法二查看本地服务器的文件请求日志。运行本地开发服务器时控制台会输出包括文件 HTTP 请求及其状态码在内的日志200OK404Not Found304Not Modified这些结果能帮你区分哪些文件服务失败、哪些缺失、哪些已存在。若清空浏览器缓存后重跑游戏就能看到全部请求被重新发出。十、小结Turbulenz Engine 的资产流水线由四部分构成Python 工具链dae2json、material2json 等负责把源资产转换为引擎可加载的 JSONcgfx2json 这一原生可执行文件负责着色器编译json2tar 负责二进制资源归档mapping table配合staticmax/与内容哈希命名负责缓存控制与资产版本化。理解这套机制后你可以把资产构建自动化参考 scripts/buildassets.py 与 apps/sampleapp/deps.yaml并按 assets_guide.rst 中的步骤调试加载问题。赞分享游戏开发图形学【免费下载链接】turbulenz_engineTurbulenz is a modular 3D and 2D game framework for making HTML5 powered games for browsers, desktops and mobile devices.项目地址https://gitcode.com/gh_mirrors/tu/turbulenz_engine点击查看免费下载相关推荐Turbulenz Engine 资产管线与 JSON 资产格式dae2json、cgfx2json 与场景 JSON 深度解析Turbulenz Engine 资产管线与 JSON 资产格式dae2json、cgfx2json 与场景 JSON 深度解析 本文基于仓库中的 Asset游戏开发图形学RePKGWallpaper Engine资源提取与转换工具完整指南RePKGWallpaper Engine资源提取与转换工具完整指南 RePKG是一款专为Wallpaper Engine设计的免费开源工具能够轻松提取PKCLI开发工具逆向工程RePKGWallpaper Engine资源提取与转换工具完全指南RePKGWallpaper Engine资源提取与转换工具完全指南 工具概述与核心价值 RePKG是一款专为Wallpaper Engine设计的开源资源处CLI开发工具逆向工程创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考