Unity WebGL性能优化实战:内存管理、WASM调优与Shader变体精简

news2026/5/24 1:48:00
1. 这不是“把游戏搬上网”那么简单为什么《疯狂特技赛车2》的Web化是Unity引擎能力边界的试金石你肯定见过那种“Unity WebGL导出一键搞定”的教程点几下Build Settings勾上WebGL等十分钟编译完拖进浏览器——然后卡在Loading界面不动了控制台刷满RuntimeError: memory access out of bounds或者刚跑两秒就内存爆掉、帧率跌到5fps。我第一次接手《疯狂特技赛车2》源码时也以为就是这么个流程。结果呢整整三周团队卡在“能跑”和“能玩”之间动弹不得。这不是一个普通3D赛车游戏它有实时物理碰撞检测基于NVIDIA PhysX的定制化简化版、动态光影烘焙实时光影混合系统、粒子系统驱动的烟尘/火花/油渍四层叠加特效、以及一套用C#写的轻量级网络同步逻辑——所有这些在Unity Editor里丝滑如德芙在WebGL里却像在沼泽里开F1。关键词“Unity引擎的Web化实践”背后藏着三个被绝大多数人忽略的硬核事实第一WebGL不是“另一个平台”它是沙盒里的沙盒——没有文件系统直读、没有原生线程、内存上限被浏览器硬性掐死在2GB以内第二“源码解析”不是看.cs文件里写了什么而是要逆向工程Unity底层如何把C# IL代码喂给Mono AOT编译器再转成WASM字节码最后被JS胶水代码调度第三《疯狂特技赛车2》这类中型项目其Web化成败不取决于美术资源压缩技巧而取决于内存生命周期管理策略是否重构——比如它的赛道网格体在Editor里是单个MeshRenderer挂载到了WebGL必须拆成8块LOD分片按视距动态加载/卸载否则光一个10MB的.fbx就吃掉近三分之一可用内存。适合谁来读这篇如果你正面临类似困境项目已用Unity开发完成但老板/客户突然要求“必须上线网页版”而你翻遍Unity官方文档只看到“WebGL支持有限”这句正确的废话或者你是技术美术发现粒子系统在WebGL里表现诡异却查不到Shader变体为何失效又或者你是前端工程师被拉去配合Unity团队调试__ZN6il2cpp2os10ThreadImpl10SetPriorityE19Il2CppThreadPriority这种符号报错一脸懵。这篇文章不讲虚的它是一份从崩溃日志反推引擎机制、从内存快照定位GC风暴、从WASM反汇编验证AOT优化效果的实战手记。接下来每一节都是我们踩过坑、改过源码、重编过il2cpp后的真实路径。2. 源码结构深挖剥离表层C#逻辑看清Unity WebGL构建链路的真实断点拿到《疯狂特技赛车2》源码包第一反应往往是打开Assets/Scripts目录扫一遍——但这恰恰是最大的误区。WebGL的瓶颈从来不在你的GameController.cs里而在Unity构建管线如何把这段C#变成能在浏览器里执行的WASM模块。我们必须先建立一个认知Unity WebGL构建不是“编译”而是“三段式翻译”C# → IL → Cil2cpp→ WASM JS胶水。而《疯狂特技赛车2》的源码里真正决定WebGL成败的是那些藏在Editor目录、Plugins目录、甚至ProjectSettings里的配置文件。2.1 Assets/Plugins/WebGL目录被忽视的“WebGL专属运行时”入口很多人以为Plugins目录只放第三方SDK但在《疯狂特技赛车2》里这个目录下藏着一个名为WebGLSupport的子文件夹里面有两个关键文件WebGLInputHandler.jslib和WebGLAudioBridge.cpp。前者是JS胶水代码的扩展后者是C插件。重点看WebGLInputHandler.jslibmergeInto(LibraryManager.library, { // 重写Unity默认的键盘事件捕获逻辑 _WebGL_HandleKeyDown: function(keyCode) { // 原始Unity会把所有keycode转成KeyCode枚举值 // 但WebGL中某些游戏手柄按键如PS4 L2/R2无法映射 // 此处直接透传原始event.code字符串 var code UTF8ToString(keyCode); if (code ArrowUp || code KeyW) { Module._SetInputAxis(Vertical, 1.0); } }, // 关键绕过Unity默认的AudioSource.Play()调用栈 // 直接调用Web Audio API降低延迟 _WebGL_PlayAudioClip: function(clipPtr, volume) { var audioContext window.AudioContext || window.webkitAudioContext; var ctx new audioContext(); // 此处省略音频解码逻辑重点是它避开了Unity Audio Mixer的JS桥接开销 } });这段代码的存在解释了为什么游戏在WebGL里方向盘响应比Editor里快12ms——它用原生Web Audio API替代了Unity Audio系统的JS桥接层。但问题也出在这里WebGL_PlayAudioClip函数在il2cpp生成的C代码中被声明为extern C而Unity 2021.3.15f1版本的il2cpp有个bug当C插件函数名含下划线且参数含float时WASM符号表会丢失类型信息导致调用时传入的volume参数永远是0。我们最终的修复方案是在WebGLAudioBridge.cpp顶部强制添加类型声明// 必须显式声明否则il2cpp生成的WASM符号无类型信息 extern C { void _WebGL_PlayAudioClip(intptr_t clipPtr, float volume); }提示Unity官方文档从不提这个细节因为这是il2cpp内部实现的副作用。遇到WebGL音频无声先检查C插件函数声明是否完整比查AudioSource组件设置更有效。2.2 ProjectSettings/PlayerSettings.assetWebGL专用参数的隐性战场打开这个二进制asset文件用YAML序列化工具可读你会发现几个关键字段被修改过字段名Editor默认值《疯狂特技赛车2》值影响说明webglCompressionFormatDisabledBrotliBrotli比Gzip压缩率高22%但要求服务器启用br编码否则406错误webglExceptionSupportNoneExplicitly Thrown Exceptions启用后WASM堆栈可追溯但包体增大1.8MB且Chrome 110需手动开启--js-flags--expose-gc才能触发webglMemorySize2561024内存初始分配值单位MB。设太小如256会导致频繁GC设太大如2048则Chrome直接拒绝加载最致命的是webglDataCaching字段。默认为true意味着Unity会把AssetBundle缓存到IndexedDB。但在《疯狂特技赛车2》里它被设为false——因为游戏采用“赛道即场景”的设计每次切换赛道都要加载全新AssetBundle而IndexedDB在iOS Safari上对单个文件50MB有写入失败率导致部分用户卡在“正在加载赛道3”。我们最终方案是保留webglDataCachingtrue但重写AssetBundle.LoadFromFileAsync()为分块加载每块≤30MB并在JS端用createObjectURL()临时托管。2.3 Assets/Editor/BuildPipeline/目录自定义构建脚本暴露的引擎真相这里有一个WebGLBuilder.cs它重写了Unity的IPreprocessBuildWithReport接口。关键逻辑在OnPreprocessBuild方法里public void OnPreprocessBuild(BuildReport report) { // 强制关闭所有非必要调试符号 PlayerSettings.SetPropertyString(webglDebugSymbols, false); // 关键动态替换Shader变体剔除规则 // Unity默认用Graphics.Blit作为剔除锚点但本项目大量使用CommandBuffer.DrawMeshInstanced Shader.globalRenderPipeline CustomRacePipeline; // 手动注入预编译宏让Shader知道当前是WebGL环境 string[] defines PlayerSettings.GetScriptingDefineSymbolsForGroup(BuildTargetGroup.WebGL); Liststring defineList new Liststring(defines); defineList.Add(WEBGL_OPTIMIZED); // 这个宏在Shader里控制分支 PlayerSettings.SetScriptingDefineSymbolsForGroup(BuildTargetGroup.WebGL, string.Join(;, defineList.ToArray())); }这个脚本揭示了一个残酷现实Unity的Shader变体剔除Shader Variant Collection在WebGL下完全失效。因为Unity不知道CommandBuffer.DrawMeshInstanced会触发哪些变体所以默认剔除规则会漏掉至少47%的必要变体导致WebGL里出现“黑模型”。解决方案不是关掉剔除那会让包体暴涨300%而是像上面代码那样用Shader.globalRenderPipeline强制指定渲染管线并在Shader里用#ifdef WEBGL_OPTIMIZED包裹所有WebGL专用分支确保变体可控。3. WebGL性能生死线内存、GC、WASM三重风暴的定位与根治《疯狂特技赛车2》WebGL版初期帧率稳定在8fpsProfiler显示CPU时间集中在GarbageCollector::Collect但Managed Heap只有120MB——这明显不合理。我们花了两天时间用Chrome DevTools的Memory面板做三次快照对比终于定位到真凶Unity的WebGL GC策略与浏览器V8引擎的内存管理存在根本性冲突。3.1 Managed Heap假象为什么Profiler显示的内存远低于实际占用Unity Profiler的Managed Heap数值只统计C#对象引用的内存不包括WASM线性内存Linear Memory。而WebGL的WASM模块拥有独立的4GB地址空间实际由浏览器限制在2GB其中__heap_base到__data_end静态数据区约15MB__heap_base向上动态堆由malloc管理峰值达1.2GBstack区域线程栈每个协程独占64KB问题在于Unity的GC.Collect()调用只清理Managed Heap而WASM堆里的Texture2D、Mesh数据、甚至Physics Scene的PhysX内存全靠浏览器GC自动回收——但V8的GC时机不可控且对大内存块100MB有惰性回收策略。我们用window.performance.memory监控发现页面JS堆仅200MB但totalJSHeapSize持续增长到1.8GB最终触发Chrome OOM Killer。解决方案是双轨内存管理在C#层主动释放WASM资源。例如赛车模型的轮胎烟尘粒子系统原本用ParticleSystem.Emit()生成改为// 旧写法依赖Unity自动管理 particleSystem.Emit(100); // 新写法手动控制WASM内存生命周期 private IntPtr smokeBuffer; // 指向WASM堆的指针 private void AllocateSmokeBuffer() { // 调用il2cpp提供的内存分配API smokeBuffer il2cpp_codegen_array_new_specific( il2cpp_defaults.byte_class, (uint32_t)(1024 * 1024 * 10) // 10MB预分配 ); } private void ReleaseSmokeBuffer() { if (smokeBuffer ! IntPtr.Zero) { il2cpp_codegen_free(smokeBuffer); // 主动归还WASM堆 smokeBuffer IntPtr.Zero; } }注意il2cpp_codegen_free是il2cpp内部API未公开文档但头文件il2cpp-api.h里有声明。调用前必须确保该指针确由il2cpp_codegen_array_new_specific分配否则WASM会崩溃。3.2 WASM函数调用开销为什么Physics.Raycast比Editor慢17倍在WebGL里每次C#调用Physics.Raycast实际执行路径是C# → il2cpp生成的WASM函数 → JS胶水代码调用Module._PhysicsRaycast→ 浏览器调用WebAssembly.Instance.exports.PhysicsRaycast → 最终进入PhysX WASM模块。我们用Chrome的Performance面板录制发现单次Raycast耗时4.2ms其中JS胶水层占3.1ms。根本原因是Unity的WebGL PhysX绑定把整个Raycast参数打包成一个struct再通过memcpy复制到WASM堆而struct含8个float和3个int共44字节——每次调用都触发一次内存拷贝。优化方案是批处理Raycast// 定义WASM端批处理函数需修改PhysX绑定C代码 [DllImport(__Internal)] private static extern void _BatchRaycast( IntPtr origins, // float3数组指针 IntPtr directions, // float3数组指针 IntPtr distances, // float数组指针输出 int count // 批次数 ); // C#端调用 public void BatchRaycast(Vector3[] origins, Vector3[] directions, float[] distances) { // 将数组Pin到WASM堆固定地址 GCHandle originHandle GCHandle.Alloc(origins, GCHandleType.Pinned); GCHandle dirHandle GCHandle.Alloc(directions, GCHandleType.Pinned); GCHandle distHandle GCHandle.Alloc(distances, GCHandleType.Pinned); try { _BatchRaycast( originHandle.AddrOfPinnedObject(), dirHandle.AddrOfPinnedObject(), distHandle.AddrOfPinnedObject(), origins.Length ); } finally { originHandle.Free(); dirHandle.Free(); distHandle.Free(); } }实测效果100次Raycast从420ms降至23ms性能提升18倍。代价是内存占用增加约2MB用于Pin住数组但相比GC风暴这是值得的。3.3 Texture内存黑洞为什么一张4K贴图吃掉320MB显存WebGL没有真正的GPU显存概念所有Texture数据都存于WASM线性内存浏览器纹理缓存。《疯狂特技赛车2》的赛道贴图是4096x4096的ASTC_4x4格式但Unity WebGL导出时会自动解压为RGBA32每个像素4字节导致单张贴图内存占用 4096×4096×4 67MB。而游戏同时加载5张同类贴图赛道、护栏、广告牌、天空盒、UI背景理论内存达335MB——这还没算Mipmap链。我们用chrome://gpu页面确认浏览器实际分配的纹理内存是理论值的4.7倍。原因在于WebGL规范要求纹理上传时浏览器必须预留额外内存用于纹理压缩/解压缩缓冲区。解决方案是强制禁用Mipmap并改用ETC2压缩// 在TextureImporter脚本中 TextureImporter importer AssetImporter.GetAtPath(path) as TextureImporter; importer.textureType TextureImporterType.Default; importer.mipmapEnabled false; // 关键禁用Mipmap importer.npotScale TextureImporterNPOTScale.None; // WebGL平台专用设置 importer.SetPlatformTextureSettings(WebGL, new TextureImporterPlatformSettings() { overridden true, format TextureImporterFormat.ETC2_RGBA8, // ETC2比ASTC在WebGL兼容性更好 maxTextureSize 2048, // 降分辨率4K→2K节省75%内存 resizeAlgorithm TextureResizeAlgorithm.Bilinear });实测单张贴图内存从67MB降至12MB5张总内存从335MB压到60MB帧率从8fps升至32fps。代价是远处纹理略模糊但赛车速度极快玩家根本察觉不到。4. 实战排错链路从Chrome控制台一行报错到定位il2cpp源码级缺陷某天测试组发来截图Chrome控制台红字报错Uncaught RuntimeError: abort(CompileError: WebAssembly.instantiate(): expected magic word 00 61 73 6d, found 3c 21 2d 2d 0)。这行错误看似简单实则是WebGL构建链路彻底断裂的信号。我们用了整整36小时走完一条从现象到根因的完整排查链路。4.1 第一层确认是构建产物损坏而非运行时错误首先排除常见误操作检查Build Settings里Target Platform是否真为WebGL曾有人误选Android后导出确认index.html是否被Nginx错误配置为text/html而非application/wasm用file命令检查build.wasm文件头$ file build.wasm build.wasm: HTML document, ASCII text, with very long lines发现文件头是!DOCTYPE html——这说明WASM文件被当作HTML返回了立刻检查Nginx配置发现遗漏了location ~* \.wasm$ { add_header Content-Type application/wasm; }。但加上后问题依旧file build.wasm仍显示HTML。用curl -I http://localhost/build.wasm发现响应头Content-Encoding: gzip而WASM文件本身已是压缩格式双重压缩导致损坏。解决方案在Nginx中禁用WASM文件的gziplocation ~* \.wasm$ { add_header Content-Type application/wasm; gzip off; # 关键 }4.2 第二层WASM符号缺失导致的undefined symbol连锁崩溃修复Nginx后新报错出现Uncaught RuntimeError: abort(undefined symbol: _Z17MyCustomFunctionv)。这是典型的C符号未导出问题。_Z17MyCustomFunctionv是MyCustomFunction()的mangled name。我们用wabt工具反编译WASM$ wasm-decompile build.wasm | grep MyCustomFunction # 无输出说明该函数根本没进WASM模块。检查Assets/Plugins/WebGL/MyPlugin.cpp发现函数声明为void MyCustomFunction() { // 实现 }缺了extern CC编译器会对函数名做mangling而WASM链接器只认C风格符号。修复为extern C { void MyCustomFunction() { // 实现 } }但重新构建后wasm-decompile仍找不到符号。用nm build.wasm检查符号表发现MyCustomFunction在符号表里但类型是UNDundefined。继续深挖发现Unity的il2cpp在处理extern C函数时要求必须在C#层有对应的DllImport声明否则会跳过链接。我们在C#脚本里补上[DllImport(__Internal)] private static extern void MyCustomFunction();这次wasm-decompile终于输出了函数体。但运行时仍崩溃控制台新报错Uncaught RuntimeError: abort(undefined symbol: __cxa_atexit)。这是C全局对象析构函数的符号WebGL默认不链接libcxx。解决方案在PlayerSettings Other Settings Configuration Scripting Backend中将Api Compatibility Level从.NET Standard 2.1降为.NET Framework并勾选Use Il2Cpp Code Generation下的Enable Exception Handling。4.3 第三层il2cpp源码级缺陷——ListT.Sort()在WebGL的无限循环最棘手的问题出现在赛车AI路径规划模块。AI用ListVector3.Sort((a,b) Vector3.Distance(a, carPos).CompareTo(Vector3.Distance(b, carPos)))排序路点Editor里正常WebGL里浏览器直接卡死。用Chrome Performance录制发现mono_wasm_invoke_method函数调用栈无限递归。我们下载Unity 2021.3.15f1的il2cpp源码GitHub可得搜索List_1_Sort定位到libil2cpp/icalls/mscorlib/System.Collections.Generic/List_1.cpp。关键代码段// il2cpp源码第123行 if (comparer NULL) { comparer il2cpp_defaults.generic_comparer; } // 问题在这里WebGL下il2cpp_defaults.generic_comparer为NULL // 导致comparer-Compare()调用空指针根本原因是Unity WebGL的il2cpp默认不编译泛型比较器认为WebGL不需要。修复方案是强制在link.xml中保留linker assembly fullnamemscorlib type fullnameSystem.Collections.Generic.GenericComparer1 preserveall/ /assembly /linker但link.xml对il2cpp无效。最终方案在Assets/Editor/il2cpp_fixes.cs中添加// 强制触发GenericComparerT的AOT编译 public class Il2CppFixes { [RuntimeInitializeOnLoadMethod] static void Init() { // 触发Vector3的GenericComparer编译 var dummy ComparerVector3.Default; } }踩坑心得Unity WebGL的任何“看起来应该有”的功能大概率需要手动触发AOT编译。不要相信“Unity会自动处理”它只会处理你明确用到的泛型实例。5. 可复用的WebGL优化清单从构建配置到运行时策略的21条硬核经验基于《疯狂特技赛车2》的实战我整理了一份可直接抄作业的WebGL优化清单。每一条都经过生产环境验证附带原理说明和生效验证方式。5.1 构建阶段必做7项强制启用Brotli压缩操作PlayerSettings Publishing Settings Compression Format Brotli原理Brotli比Gzip平均压缩率高22%对WASM文本段.text效果尤其显著验证构建后检查build.wasm文件大小应比Gzip小15%以上禁用WebGL调试符号操作PlayerSettings Other Settings Debug Symbols false原理调试符号使WASM文件增大300%且Chrome 110需额外flag才启用验证build.wasm体积应减少1.2MB以上设置合理的内存初始值操作PlayerSettings Publishing Settings Memory Size 1024原理256MB是Chrome最低要求但会导致频繁内存扩容每次扩容耗时80ms验证Chrome Task Manager中JavaScript Memory初始值应≈1024MB关闭Autoconnect Profiler操作PlayerSettings Other Settings Autoconnect Profiler false原理Profiler连接会启动WebSocket长连接WebGL下占用额外15MB内存验证Network面板无ws://连接强制指定Graphics API为WebGL 2.0操作PlayerSettings Other Settings Auto Graphics API false只勾选WebGL 2.0原理WebGL 1.0不支持instancing导致Draw Call暴增10倍验证gl.getParameter(gl.VERSION)返回WebGL 2.0禁用Application.runInBackground操作代码中Application.runInBackground false原理后台运行时Unity仍每帧更新物理/动画WebGL下CPU占用飙升验证切到其他标签页CPU占用应降至5%以下Shader变体精简至最小集操作创建ShaderVariantCollection只包含实际用到的变体用Frame Debugger确认原理默认Shader变体超2000个WebGL只加载其中5%验证build.json中shaderVariants字段应2005.2 运行时必调14项Texture内存分级管理操作对4K贴图设maxTextureSize20482K贴图设1024UI贴图设512原理纹理内存宽×高×4字节降分辨率是性价比最高的内存削减手段验证chrome://gpu中Texture memory应300MBMesh分块LOD加载操作用Mesh.CombineMeshes()将赛道拆为8块按视距动态Instantiate/Destroy原理单个Mesh超10万顶点时WebGL GPU上传耗时200ms验证Frame Debugger中Draw Mesh调用次数应50粒子系统改用GPU Instancing操作ParticleSystemRenderer勾选Enable GPU InstancingShader用Particles/Standard Unlit原理CPU Instancing每粒子1次Draw CallGPU Instancing 1000粒子1次Draw Call验证Stats面板Batches数应200物理系统降频操作Time.fixedDeltaTime 0.04f25HzPhysics.autoSimulation false手动Physics.Simulate()原理WebGL Physics计算耗时是Editor的3倍降频可保帧率验证Profiler中Physics.Process耗时应8ms/frame禁用所有Reflection操作删除所有GetType().GetMethod()、Activator.CreateInstance()调用原理WebGL下Reflection API需AOT编译全部类型包体暴涨验证build.json中reflectionUsage字段应为noneCoroutine改用Update轮询操作将yield return new WaitForSeconds(1)改为if (Time.time nextFire) { ... nextFire Time.time 1; }原理Coroutine在WebGL下有额外栈管理开销Update轮询更轻量验证MonoManager内存占用下降40%字体图集预生成操作TextMeshPro组件勾选Enable Atlas PackingFont Asset设Atlas Resolution1024原理动态字体图集生成会触发GC预生成后内存恒定验证TMP_FontAsset内存占用应5MBAudioSource改用Web Audio API直连操作用WebGLAudioBridge.cpp封装Web AudioC#层调用_WebGL_PlayAudioClip原理Unity Audio Mixer JS桥接延迟50msWeb Audio API延迟10ms验证chrome://tracing中音频播放延迟应12ms禁用所有Editor-only代码操作所有#if UNITY_EDITOR代码块内添加#if UNITY_WEBGL UNITY_EDITOR双条件原理Unity有时会错误包含Editor代码到WebGL构建中验证build.wasm中搜索UnityEditor应无结果Canvas RenderMode设为Screen Space - Camera操作UI Canvas组件Render Mode Screen Space - Camera指定主相机原理Overlay模式强制每帧重建UI网格Camera模式可复用验证Canvas.SendWillRenderCanvases耗时应1ms禁用Animator IK操作Animator.avatar设为null改用Transform.Rotate()实现转向原理IK求解在WebGL下耗时是Editor的8倍验证Animator.Update耗时应3msScripting Runtime Version设为.NET 4.x操作PlayerSettings Configuration Scripting Runtime Version .NET 4.x原理.NET Standard 2.1在WebGL下有已知GC bug.NET 4.x更稳定验证build.json中runtimeVersion应为4.0禁用所有AssetBundle Manifest依赖操作AssetBundle.Unload(false)后立即Resources.UnloadUnusedAssets()原理Manifest对象在WebGL下不被GC长期驻留内存验证Resources.UnloadUnusedAssets()后内存下降应50MB强制Texture2D.Apply()异步化操作Texture2D.Apply()前加yield return null确保不在主线程阻塞原理WebGL下Apply()会触发GPU同步阻塞主线程长达200ms验证Texture2D.Apply调用不再出现在主线程耗时热点中我在实际项目中发现只要严格执行这21条中型Unity项目WebGL版帧率能稳定在45fps以上内存占用压到800MB内。最关键的是第11条物理降频和第19条.NET 4.x它们解决的是Unity WebGL最隐蔽的性能地雷。最后分享一个小技巧每次构建后用wabt工具链检查WASM文件健康度——wasm-validate build.wasm应无错误wasm2wat build.wasm | wc -l行数应50万行超50万说明AOT编译过度。这些不是玄学而是我们一行行日志、一次次崩溃、一帧帧Profiler里抠出来的硬道理。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2635778.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SpringBoot-17-MyBatis动态SQL标签之常用标签

文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…

wordpress后台更新后 前端没变化的解决方法

使用siteground主机的wordpress网站,会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后,网站没有变化的情况。 不熟悉siteground主机的新手,遇到这个问题,就很抓狂,明明是哪都没操作错误&#x…

网络编程(Modbus进阶)

思维导图 Modbus RTU(先学一点理论) 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议,由 Modicon 公司(现施耐德电气)于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…

UE5 学习系列(二)用户操作界面及介绍

这篇博客是 UE5 学习系列博客的第二篇,在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下: 【Note】:如果你已经完成安装等操作,可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作,重…

IDEA运行Tomcat出现乱码问题解决汇总

最近正值期末周,有很多同学在写期末Java web作业时,运行tomcat出现乱码问题,经过多次解决与研究,我做了如下整理: 原因: IDEA本身编码与tomcat的编码与Windows编码不同导致,Windows 系统控制台…

利用最小二乘法找圆心和半径

#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …

使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式

一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明&#xff1a;假设每台服务器已…

XML Group端口详解

在XML数据映射过程中&#xff0c;经常需要对数据进行分组聚合操作。例如&#xff0c;当处理包含多个物料明细的XML文件时&#xff0c;可能需要将相同物料号的明细归为一组&#xff0c;或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码&#xff0c;增加了开…

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造&#xff0c;完美适配AGV和无人叉车。同时&#xff0c;集成以太网与语音合成技术&#xff0c;为各类高级系统&#xff08;如MES、调度系统、库位管理、立库等&#xff09;提供高效便捷的语音交互体验。 L…

(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)

题目&#xff1a;3442. 奇偶频次间的最大差值 I 思路 &#xff1a;哈希&#xff0c;时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况&#xff0c;哈希表这里用数组即可实现。 C版本&#xff1a; class Solution { public:int maxDifference(string s) {int a[26]…

【大模型RAG】拍照搜题技术架构速览:三层管道、两级检索、兜底大模型

摘要 拍照搜题系统采用“三层管道&#xff08;多模态 OCR → 语义检索 → 答案渲染&#xff09;、两级检索&#xff08;倒排 BM25 向量 HNSW&#xff09;并以大语言模型兜底”的整体框架&#xff1a; 多模态 OCR 层 将题目图片经过超分、去噪、倾斜校正后&#xff0c;分别用…

【Axure高保真原型】引导弹窗

今天和大家中分享引导弹窗的原型模板&#xff0c;载入页面后&#xff0c;会显示引导弹窗&#xff0c;适用于引导用户使用页面&#xff0c;点击完成后&#xff0c;会显示下一个引导弹窗&#xff0c;直至最后一个引导弹窗完成后进入首页。具体效果可以点击下方视频观看或打开下方…

接口测试中缓存处理策略

在接口测试中&#xff0c;缓存处理策略是一个关键环节&#xff0c;直接影响测试结果的准确性和可靠性。合理的缓存处理策略能够确保测试环境的一致性&#xff0c;避免因缓存数据导致的测试偏差。以下是接口测试中常见的缓存处理策略及其详细说明&#xff1a; 一、缓存处理的核…

龙虎榜——20250610

上证指数放量收阴线&#xff0c;个股多数下跌&#xff0c;盘中受消息影响大幅波动。 深证指数放量收阴线形成顶分型&#xff0c;指数短线有调整的需求&#xff0c;大概需要一两天。 2025年6月10日龙虎榜行业方向分析 1. 金融科技 代表标的&#xff1a;御银股份、雄帝科技 驱动…

观成科技:隐蔽隧道工具Ligolo-ng加密流量分析

1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具&#xff0c;该工具基于TUN接口实现其功能&#xff0c;利用反向TCP/TLS连接建立一条隐蔽的通信信道&#xff0c;支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式&#xff0c;适应复杂网…

铭豹扩展坞 USB转网口 突然无法识别解决方法

当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…

未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?

编辑&#xff1a;陈萍萍的公主一点人工一点智能 未来机器人的大脑&#xff1a;如何用神经网络模拟器实现更智能的决策&#xff1f;RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战&#xff0c;在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…

Linux应用开发之网络套接字编程(实例篇)

服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …

华为云AI开发平台ModelArts

华为云ModelArts&#xff1a;重塑AI开发流程的“智能引擎”与“创新加速器”&#xff01; 在人工智能浪潮席卷全球的2025年&#xff0c;企业拥抱AI的意愿空前高涨&#xff0c;但技术门槛高、流程复杂、资源投入巨大的现实&#xff0c;却让许多创新构想止步于实验室。数据科学家…

深度学习在微纳光子学中的应用

深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向&#xff1a; 逆向设计 通过神经网络快速预测微纳结构的光学响应&#xff0c;替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…