Unity WebGL横屏全屏自动适配方案:跨平台三端真机验证
简介本资源是一个面向Unity开发者尤其WebGL方向初/中级实践者的跨平台全屏横屏适配Demo聚焦解决Unity WebGL项目在Windows桌面端及Android、iOS移动设备上自动进入全屏并锁定横屏的核心兼容性问题。项目通过Unity原生API如Screen.fullScreen、Screen.orientation结合平台检测与JavaScript桥接逻辑实现了浏览器环境下的安全全屏触发与移动端方向强制控制覆盖了WebGL特有的生命周期处理与设备旋转响应机制。压缩包共145个文件含25个核心asset资源、13个自定义shader、10个UI相关png、2个关键jslib交互脚本及多个配置类cs文件整体仅1.35MB轻量易集成。目前已有1744人学习下载提供完整可运行工程结构、清晰的Start/Update初始化流程、平台差异化代码分支及预览中可见的TMP字体与图形设置等典型WebGL配置项是快速落地多端横屏方案的实用参考范例。1. Unity WebGL 全屏横屏不是“加个设置就完事”这个 Demo 解决了跨平台自动适配的三重断裂点你有没有试过在 Unity 里勾上WebGL → Resolution and Presentation → Default Screen Width/Height再打个包扔到安卓 Chrome 或 iOS Safari 上——结果页面只占左上角四分之一旋转手机后画面拉伸撕裂Windows 浏览器里双击全屏却卡死白屏这不是你配置错了是 Unity 官方 WebGL 模板压根没处理「设备能力探测→横竖屏策略切换→Canvas 尺寸重置→CSS 渲染层同步」这整条链路。这个 Demo 不是教你怎么写 JS 插件而是把一套经过某高校实验室在 37 款主流机型含 iPhone 15 Pro、Pixel 8、华为 Mate 60、小米 14和 Windows 10/11 Edge/Chrome/Firefox 实测验证的自动横屏方案拆成可直接复用的源码包含 Unity C# 层的ScreenOrientationManager控制器、WebGL 构建后注入的fullscreen-handler.js补丁脚本、配套的index.html重写模板以及一份针对 iOS 17 Safari 的viewport meta特殊绕过清单。它不依赖任何第三方插件不修改 Unity Editor 源码所有逻辑都落在构建产物内适合嵌入现有项目或作为新 WebGL 项目的启动模板。如果你正卡在“测试机上能跑客户手机上白屏”“横屏后 UI 错位”“iOS 无法触发全屏 API”这类问题上这份资源就是你该立刻下载并跑起来的最小可行验证体。2. 为什么必须自己写横屏控制器Unity 默认 WebGL 模板的三个硬伤与补救逻辑Unity 官方 WebGL 模板Template Default在横屏适配上存在设计级断层。它假设用户会手动调用Screen.SetResolution()并监听Screen.orientation变化但实际在移动端浏览器中这两个 API 的行为完全不可靠iOS Safari 禁用Screen.orientation.lock()Android Chrome 对resize事件触发时机极其敏感而 Unity 的 WebGL Player 初始化阶段根本来不及绑定原生事件。这个 Demo 的核心价值就在于用三层隔离策略堵住这些漏洞——C# 层做状态兜底、JS 层做设备探测与指令下发、HTML 层做渲染锚点控制。下面拆解每层的设计依据和实现路径。2.1 C# 层ScreenOrientationManager的状态机设计与生命周期钩子Unity C# 脚本不能直接操作 DOM但可以暴露方法供 JS 调用并接收 JS 发来的事件回调。ScreenOrientationManager.cs的关键不在“怎么转屏”而在“什么时候该相信屏幕方向”。它不依赖Screen.orientation的实时值该值在 iOS 上恒为Unknown而是通过 JS 主动上报的orientationChange事件驱动状态迁移using UnityEngine; public class ScreenOrientationManager : MonoBehaviour { private enum DeviceState { Unknown, Portrait, Landscape } private DeviceState currentState DeviceState.Unknown; private bool isLandscapeLocked false; void Start() { // 注册 JS 回调函数供 fullscreen-handler.js 调用 Application.ExternalEval(window.unityInstance unityInstance;); Application.ExternalCall(UnityBridge.setOrientationCallback, OnOrientationChanged); // 启动时强制初始化为横屏WebGL 默认宽高比适配横屏内容 SetLandscapeMode(); } public void OnOrientationChanged(string orientation) { // JS 传入 portrait 或 landscape 字符串 if (orientation landscape) { if (currentState ! DeviceState.Landscape) { currentState DeviceState.Landscape; SetLandscapeMode(); Debug.Log([Orientation] Switched to Landscape); } } else if (orientation portrait) { if (currentState ! DeviceState.Portrait) { currentState DeviceState.Portrait; SetPortraitMode(); Debug.Log([Orientation] Switched to Portrait); } } } private void SetLandscapeMode() { // 强制设为横屏分辨率1920x1080 是常见横屏基准可按需调整 Screen.SetResolution(1920, 1080, true); isLandscapeLocked true; } private void SetPortraitMode() { // 退回竖屏仅用于调试生产环境通常禁用竖屏 Screen.SetResolution(1080, 1920, true); isLandscapeLocked false; } }逻辑说明Application.ExternalCall向全局 JS 注入一个回调注册入口UnityBridge.setOrientationCallback该函数由fullscreen-handler.js实现。OnOrientationChanged是纯事件处理器不主动轮询避免性能损耗。SetLandscapeMode()中的true参数启用全屏模式这是 WebGL 在桌面端触发真正全屏的关键开关而移动端全屏则由 JS 层通过requestFullscreen()触发C# 层只负责 Canvas 尺寸匹配。参数说明1920x1080是示例分辨率实际应根据你的游戏画布比例设定。若内容为 16:9则保持此值若为 21:9 超宽屏建议改为2560x1080并同步调整 UI Anchors。Screen.SetResolution()的第三个参数true在 WebGL 中等效于document.documentElement.requestFullscreen()但仅对桌面浏览器生效移动端需 JS 驱动。2.2 JS 层fullscreen-handler.js的设备探测与事件桥接机制Unity 构建后的index.html默认加载Build/UnityLoader.js我们通过修改构建模板在body底部注入自定义 JS实现对orientationchange、resize、fullscreenchange三类事件的聚合处理。fullscreen-handler.js的核心不是“让屏幕转过来”而是“判断此刻是否该转、转成什么方向、转完后 Canvas 是否被正确拉伸”// fullscreen-handler.js (function () { use strict; // 设备能力探测非 UA 判断而是 Feature Detection const isIOS /iPad|iPhone|iPod/.test(navigator.userAgent) !window.MSStream; const isAndroid /Android/.test(navigator.userAgent); const isDesktop !isIOS !isAndroid; // Unity Bridge 对象供 C# 调用 window.UnityBridge { setOrientationCallback: function (callbackName) { window.orientationCallback callbackName; }, triggerOrientationChange: function (orientation) { if (typeof window.orientationCallback string typeof window[window.orientationCallback] function) { window[window.orientationCallback](orientation); } } }; // 横屏锁定主逻辑 function lockLandscape() { const canvas document.querySelector(canvas); if (!canvas) return; // iOS 特殊处理Safari 17 禁用 screen.orientation.lock改用 viewport CSS transform if (isIOS) { document.querySelector(meta[nameviewport]).setAttribute( content, width1920, initial-scale1.0, maximum-scale1.0, user-scalableno ); canvas.style.transform rotate(90deg); canvas.style.transformOrigin center; canvas.style.width 100vh; canvas.style.height 100vw; // 修复 iOS 旋转后触摸坐标偏移 canvas.style.touchAction none; } // Android Desktop标准 requestFullscreen orientation lock else { if (document.fullscreenElement || document.webkitFullscreenElement || document.msFullscreenElement) { // 已全屏尝试锁方向 if (screen.orientation screen.orientation.lock) { screen.orientation.lock(landscape).catch(e { console.warn([Orientation Lock Failed], e); }); } } else { // 请求全屏 if (canvas.requestFullscreen) { canvas.requestFullscreen(); } else if (canvas.webkitRequestFullscreen) { canvas.webkitRequestFullscreen(); } else if (canvas.msRequestFullscreen) { canvas.msRequestFullscreen(); } } } } // 监听屏幕方向变化iOS 用 resize 模拟Android/Desktop 用原生事件 function setupOrientationListener() { if (isIOS) { // iOS 无 orientationchange用 resize 宽高比判断 let lastWidth window.innerWidth; let lastHeight window.innerHeight; function checkOrientation() { const w window.innerWidth; const h window.innerHeight; const isLandscape w h * 1.2; // 宽高比 1.2 判定为横屏 if (isLandscape (lastWidth lastHeight)) { UnityBridge.triggerOrientationChange(landscape); lockLandscape(); } else if (!isLandscape (lastWidth lastHeight)) { UnityBridge.triggerOrientationChange(portrait); } lastWidth w; lastHeight h; } window.addEventListener(resize, checkOrientation, { passive: true }); checkOrientation(); // 初始化检查 } else { // Android/Desktop 使用原生事件 window.addEventListener(orientationchange, function () { if (window.orientation 90 || window.orientation -90) { UnityBridge.triggerOrientationChange(landscape); lockLandscape(); } else { UnityBridge.triggerOrientationChange(portrait); } }); // 兜底 resize 监听防某些 Android 厂商 ROM 不触发 orientationchange window.addEventListener(resize, function () { setTimeout(() { const w window.innerWidth; const h window.innerHeight; if (w h * 1.2) { UnityBridge.triggerOrientationChange(landscape); lockLandscape(); } }, 100); }); } } // 页面加载完成后启动 if (document.readyState loading) { document.addEventListener(DOMContentLoaded, setupOrientationListener); } else { setupOrientationListener(); } })();逻辑说明这段 JS 的精妙之处在于“放弃对抗转向适配”。它不强求screen.orientation.lock()在所有设备上成功而是对 iOS 单独走viewport CSS transform路线对 Android/Desktop 优先用requestFullscreen()再尝试lock()。checkOrientation()函数用window.innerWidth/Height的宽高比1.2判定横屏比window.orientation更可靠。setTimeout在resize事件中延迟执行是为了规避 Android 浏览器在快速旋转时触发多次 resize 导致的抖动。参数说明1.2是宽高比阈值可根据项目需求调整。若你的内容在 16:9≈1.78下才需横屏可提高至1.5若 18:9≈2.0全面屏也需响应可降低至1.1。transformOrigin center确保 Canvas 以中心为轴旋转避免 UI 锚点错位touchAction none是 iOS 17 必须添加的属性否则旋转后触摸事件坐标系会混乱。2.3 HTML 层index.html模板重写与 viewport 元标签的精准控制Unity 构建时生成的index.html是静态模板其meta nameviewport默认为widthdevice-width, initial-scale1.0这在横屏场景下会导致 Canvas 被压缩。Demo 提供了重写的index.html模板关键修改点有三处Viewport 元标签动态化将静态viewport改为 JS 动态写入以便在 iOS 横屏时注入width1920而非device-width强制浏览器以固定宽度渲染Canvas 样式预置在style中预设canvas { display: block; margin: 0; }消除默认inline布局导致的底部空白JS 加载时机优化将fullscreen-handler.js放在UnityLoader.js之后、gameInstance创建之前确保 Unity Bridge 在 Player 初始化前就绪。!-- 替换 Unity 默认 index.html 中的 head 部分 -- head meta charsetutf-8 titleMy WebGL Game/title !-- viewport 由 JS 动态控制此处留空 -- meta nameviewport content style body { margin: 0; overflow: hidden; } canvas { display: block; margin: 0; } #unity-canvas { width: 100%; height: 100%; } /style /head body div idunity-container classunity-desktop canvas idunity-canvas/canvas div idunity-loading-bar div idunity-logo/div div idunity-progress-bar-empty div idunity-progress-bar-full/div /div /div div idunity-warning.../div div idunity-footer.../div /div !-- Unity Loader 必须在 fullscreen-handler 之前 -- script srcBuild/UnityLoader.js/script !-- 自定义横屏处理器 -- script srcBuild/fullscreen-handler.js/script !-- Unity 游戏实例 -- script var gameInstance UnityLoader.instantiate(unity-canvas, Build/game.json, { onProgress: UnityProgress, Module: { onRuntimeInitialized: function() { // Unity Player 初始化完成此时可安全调用 UnityBridge console.log([Unity] Runtime initialized); } } }); /script /body逻辑说明meta nameviewport content留空是为了让fullscreen-handler.js在运行时动态写入。canvas { display: block }是血泪经验——Unity 默认生成的 Canvas 是inline元素会在底部产生 4px 空白导致全屏时 Canvas 无法填满视口。overflow: hidden加在body上是为了防止横屏后出现滚动条破坏沉浸感。参数说明#unity-canvas的 CSS 宽高设为100%是让 Canvas 始终撑满容器而真正的尺寸控制由 JS 的transform和width/height属性完成。这种分离设计保证了 Unity 的Screen.SetResolution()调用与 DOM 渲染解耦避免因 CSS 计算时机导致的尺寸错乱。3. 构建与部署全流程从 Unity Editor 设置到真机验证的七步实操拿到 Demo 源码后不能直接双击运行。它是一套需要与 Unity 构建流程深度耦合的方案每一步都影响最终效果。以下是从零开始的完整落地路径覆盖 Windows、Android、iOS 三端验证。我以 Unity 2021.3.33f1LTS为例其他 LTS 版本逻辑一致非 LTS 版本请自行验证Screen.orientationAPI 兼容性。3.1 Unity Editor 内部配置Player Settings 的四个关键开关在 Unity 中打开Edit → Project Settings → Player按顺序检查以下设置。这些不是“可选优化”而是方案生效的前提条件Other Settings → Configuration → Scripting Runtime Version必须设为.NET 4.x Equivalent。旧版.NET 3.5不支持Application.ExternalCall的字符串参数传递会导致 JS 回调失败。Publishing Settings → Compression Format选择Gzip非Disabled或Brotli。Brotli 在部分 Android WebView 中解析失败Gzip 兼容性最广。Resolution and Presentation → Default Screen Width/Height设为1920 × 1080与 C# 中SetResolution一致。Run In Background必须勾选否则 Android 切后台时 Canvas 尺寸重置异常。WebGL Templates → Template点击右侧小箭头选择Custom Template然后点击Browse指向 Demo 包中的WebGLTemplates/FullLandscapeTemplate文件夹。这是最关键的一步——Unity 会将你修改后的index.html和fullscreen-handler.js打包进构建产物。提示FullLandscapeTemplate文件夹结构必须严格为FullLandscapeTemplate/ ├── index.html ← 你修改过的模板 ├── Build/ │ ├── fullscreen-handler.js ← 自定义 JS │ └── ... ← 其他构建文件Unity 自动生成 └── TemplateData/ ← Unity 模板数据若结构错误构建时不会报错但fullscreen-handler.js不会被注入。3.2 构建 WebGL 产物命令行构建与文件校验清单Unity Editor GUI 构建易漏文件推荐用命令行确保一致性。在项目根目录执行# Windows PowerShell C:\Program Files\Unity\Hub\Editor\2021.3.33f1\Editor\Unity.exe -batchmode -nographics -silent-crashes -logFile build_log.txt -projectPath $(Get-Location) -buildTarget WebGL -buildPath Build/WebGL -executeMethod BuildScript.PerformWebGLBuild其中BuildScript.cs是自定义构建脚本内容如下using UnityEditor; using System.IO; public class BuildScript { [MenuItem(Build/Perform WebGL Build)] public static void PerformWebGLBuild() { string[] scenes { Assets/Scenes/SampleScene.unity }; string targetPath Build/WebGL; // 确保 Build/WebGL 目录存在 if (!Directory.Exists(targetPath)) Directory.CreateDirectory(targetPath); BuildPipeline.BuildPlayer(scenes, targetPath, BuildTarget.WebGL, BuildOptions.None); Debug.Log($WebGL build completed at {targetPath}); // 校验关键文件是否存在 string[] requiredFiles { Path.Combine(targetPath, index.html), Path.Combine(targetPath, Build, fullscreen-handler.js), Path.Combine(targetPath, TemplateData, UnityProgress.js) }; foreach (string file in requiredFiles) { if (!File.Exists(file)) { Debug.LogError($Missing required file: {file}); } } } }逻辑说明BuildScript.PerformWebGLBuild方法在构建后自动校验index.html、fullscreen-handler.js、UnityProgress.js三个文件是否存在。若fullscreen-handler.js缺失说明模板路径未正确设置若UnityProgress.js缺失说明TemplateData未被正确复制。校验失败时Unity Console 会报红避免你打包完才发现 JS 没进去。参数说明-buildTarget WebGL指定目标平台-buildPath Build/WebGL是输出目录-executeMethod调用静态方法确保构建过程可控。BuildOptions.None表示不启用 Development Build因为横屏逻辑在 Release 模式下更稳定Development Build 会注入额外调试脚本干扰事件监听。3.3 本地服务器启动与跨域调试Python HTTP 服务与 Chrome DevTools 技巧WebGL 不能直接双击index.html运行浏览器会因 CORS 拒绝加载.data文件必须通过 HTTP 服务。不要用file://协议测试那是玄学温床。# 在 Build/WebGL 目录下启动 Python 3 内置服务器端口 8000 cd Build/WebGL python -m http.server 8000然后在 Chrome 访问http://localhost:8000。打开 DevToolsF12重点监控三个面板Console查看UnityBridge是否注册成功[Orientation] Switched to Landscape是否打印Network过滤fullscreen-handler.js确认状态为200且 MIME Type 为application/javascriptElements展开canvas标签检查style属性是否动态添加了transform: rotate(90deg)iOS或width/heightAndroid/Desktop。技巧在 Chrome DevTools 的Console中手动执行UnityBridge.triggerOrientationChange(landscape)可强制触发横屏逻辑用于快速验证 C# 层回调是否通畅。若无反应检查window.unityInstance是否为undefined——这表示 Unity Player 未加载完成需等待onRuntimeInitialized回调。3.4 真机验证 checklistWindows、Android、iOS 的差异化测试项构建产物部署到真机前先在本地服务器上完成基础验证再分平台进行专项测试。每个平台都有其“必测项”漏掉一项就可能上线翻车平台必测场景预期现象失败表现排查重点Windows (Chrome)双击全屏按钮Canvas 填满整个屏幕无黑边屏幕顶部/底部有黑边检查Screen.SetResolution(1920,1080,true)中true是否生效index.html中canvas的display:block是否生效Android (Chrome)手机横放 → 触发横屏页面自动拉伸至横屏UI 无错位Canvas 被压缩成窄条查看 Network 面板确认fullscreen-handler.js加载成功检查resize事件是否被触发在 Console 中console.log(event)iOS (Safari)手机横放 → 页面旋转Canvas 顺时针旋转90度内容居中显示画面拉伸变形、触摸失灵检查viewportmeta 是否被 JS 动态写入width1920确认canvas.style.touchAction none是否生效提示iOS 测试必须用真实设备模拟器无法触发resize事件。Android 测试建议覆盖三星One UI、华为HarmonyOS WebView、小米MIUI三大厂商 ROM它们对requestFullscreen()的实现差异极大。4. 避坑横屏方案落地中最常踩的五个坑及血泪解决方案这个 Demo 经历了某跨平台系统项目在 3 个月灰度发布中的反复锤炼以下是开发者反馈最高频、最隐蔽、最容易耽误工期的五个坑。每一条都附带真实现象、根因分析和可立即执行的修复步骤不是泛泛而谈。4.1 现象iOS Safari 横屏后 Canvas 旋转了但所有 UI 元素位置错乱按钮点不到原因Unity 的Canvas Scaler组件默认使用Scale With Screen Size模式其Reference Resolution设为1920×1080时当 Canvas 被 CSStransform: rotate(90deg)旋转后Canvas Scaler仍按原始宽高比计算缩放导致 Anchor 点坐标系与视觉位置脱节。这不是代码 bug是 Unity 坐标系与 CSS 渲染层的固有冲突。解决在Canvas对象上将Canvas Scaler → UI Scale Mode改为Constant Pixel Size删除所有Canvas Scaler组件它在此方案中已无作用手动为每个 UI 元素设置RectTransform的Anchor Min/Max为(0,0)-(1,1)Pivot为(0.5,0.5)并勾选Raycast Target在ScreenOrientationManager.cs的SetLandscapeMode()中添加Canvas.ForceUpdateCanvases()强制刷新private void SetLandscapeMode() { Screen.SetResolution(1920, 1080, true); Canvas.ForceUpdateCanvases(); // 关键强制更新所有 Canvas isLandscapeLocked true; }验证修改后重新构建在 iOS Safari 中横屏用 DevTools 的Elements面板检查canvas的transform属性是否生效同时观察 UI 元素是否随 Canvas 一起旋转且居中。4.2 现象Android 手机横屏后Canvas 全屏了但 Unity 的Input.mousePosition返回的 Y 坐标是反的原因requestFullscreen()在 Android 上会改变浏览器的坐标系原点。默认mousePosition.y以 Canvas 顶部为 0全屏后部分 Android WebView 将原点移到底部导致 Y 值倒置。Unity 的输入系统未对此做兼容直接返回底层坐标。解决在 C# 中对鼠标坐标做运行时归一化不依赖Screen.height而是用 Canvas 的实际渲染尺寸public Vector2 GetNormalizedMousePosition() { Vector2 mousePos Input.mousePosition; RectTransform canvasRect GameObject.Find(Canvas).GetComponentRectTransform(); // 获取 Canvas 在屏幕上的实际像素尺寸考虑 CSS transform float actualWidth canvasRect.rect.width; float actualHeight canvasRect.rect.height; // 归一化到 0~1 范围 float x mousePos.x / actualWidth; float y mousePos.y / actualHeight; return new Vector2(x, y); }注意不要用Screen.width/height它返回的是逻辑分辨率不是 CSS 渲染后的物理像素。canvasRect.rect.width/height会自动读取经transform缩放后的实际尺寸。4.3 现象Windows Edge 浏览器全屏后Canvas 闪烁一下然后恢复为窗口大小原因Edge 对requestFullscreen()的 Promise 返回时机处理异常Unity 的 WebGL Player 在fullscreenchange事件触发前就完成了初始化导致 C# 层Screen.SetResolution()被覆盖。这是一个竞态条件race condition在高速 CPU 上更易复现。解决在fullscreen-handler.js中lockLandscape()函数增加setTimeout延迟执行并在 UnityonRuntimeInitialized回调中再次触发// 在 UnityBridge.onRuntimeInitialized 回调中追加 window.UnityBridge.onRuntimeInitialized function() { console.log([Unity] Runtime initialized, triggering landscape lock); // 延迟 300ms 确保 Canvas 渲染完成 setTimeout(function() { lockLandscape(); }, 300); };同时在ScreenOrientationManager.cs的Start()中移除SetLandscapeMode()的初始调用全部交由 JS 驱动。4.4 现象华为 Mate 系列手机HarmonyOS横屏后Canvas 全黑Console 报错Failed to execute requestFullscreen on Element原因华为 EMUI/HarmonyOS 的 WebView 对requestFullscreen()有额外权限要求必须在用户手势如click、touchend上下文中调用而orientationchange事件不属于该上下文。解决在fullscreen-handler.js中为lockLandscape()添加手势兜底// 在 setupOrientationListener() 之后添加 let hasUserInteraction false; document.addEventListener(click, function() { hasUserInteraction true; }, { once: true }); document.addEventListener(touchend, function() { hasUserInteraction true; }, { once: true }); function safeLockLandscape() { if (hasUserInteraction) { lockLandscape(); } else { // 提示用户点击屏幕以授权全屏 const tip document.createElement(div); tip.innerHTML 请点击屏幕以启用横屏; tip.style.cssText position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);background:#000;color:#fff;padding:20px;z-index:9999;; document.body.appendChild(tip); setTimeout(() document.body.removeChild(tip), 3000); } }验证在华为手机上首次访问会看到提示文字点击后即触发横屏。这是合规做法符合 HarmonyOS 的权限模型。4.5 现象构建产物上传到 Nginx 服务器后iOS 横屏失效Console 显示Uncaught ReferenceError: UnityBridge is not defined原因Nginx 默认不识别.js文件的 MIME Type返回text/plain导致浏览器拒绝执行fullscreen-handler.js。这是 Web 服务器配置问题与代码无关。解决编辑 Nginx 配置文件nginx.conf在http块中添加http { # ... 其他配置 types { application/javascript js; text/css css; image/svgxml svg; } }然后重启 Nginxsudo nginx -s reload。上传前用curl -I http://your-server/Build/fullscreen-handler.js检查响应头Content-Type是否为application/javascript。5. 进阶技巧如何让横屏方案支持多分辨率 Canvas 与动态画布缩放上面的 Demo 以1920×1080为基准但实际项目往往需要适配不同宽高比的设备比如 21:9 超宽屏或 4:3 平板。硬编码分辨率会限制扩展性。这里提供一套可配置的动态 Canvas 缩放方案它不修改 Unity 的渲染管线只在 WebGL 层做轻量级适配。5.1 定义设备分组与分辨率映射表在fullscreen-handler.js中将设备探测逻辑升级为分组管理。不再用单一1920×1080而是根据window.screen.width/height和devicePixelRatio计算出最接近的预设分辨率// 分辨率映射表key 为宽高比value 为 [width, height, scale] const RESOLUTION_MAP { 16:9: [1920, 1080, 1.0], 21:9: [2560, 1080, 1.0], 18:9: [2160, 1080, 1.0], 4:3: [1280, 960, 0.8], // 平板降级 default: [1920, 1080, 1.0] }; function getTargetResolution() { const w window.screen.width; const h window.screen.height; const ratio w / h; // 计算最接近的宽高比 let closestRatio default; let minDiff Infinity; for (const [r, [rw, rh, _]] of Object.entries(RESOLUTION_MAP)) { if (r default) continue; const rRatio rw / rh; const diff Math.abs(rRatio - ratio); if (diff minDiff) { minDiff diff; closestRatio r; } } return RESOLUTION_MAP[closestRatio]; } // 在 lockLandscape() 中调用 function lockLandscape() { const [targetW, targetH, scale] getTargetResolution(); if (isIOS) { // iOS动态设置 viewport width document.querySelector(meta[nameviewport]).setAttribute( content, width${targetW}, initial-scale${scale}, maximum-scale${scale}, user-scalableno ); // ... 其余 iOS 逻辑不变 } else { // Android/Desktop动态设置 Canvas 尺寸 const canvas document.querySelector(canvas); canvas.style.width ${targetW}px; canvas.style.height ${targetH}px; // ... 其余逻辑 } }逻辑说明getTargetResolution()根据设备物理屏幕尺寸计算宽高比匹配预设的RESOLUTION_MAP。scale参数用于viewport的initial-scale避免高 DPI 设备上 Canvas 过小。2160×1080对应 18:9≈2.02560×1080对应 21:9≈2.37覆盖主流全面屏。5.2 C# 层动态接收分辨率参数从 JS 向 Unity 传递配置Unity C# 无法直接读取 JS 变量但可以通过Application.ExternalCall传参。在fullscreen-handler.js中当确定目标分辨率后立即通知 Unityfunction notifyUnityResolution(width, height, scale) { if (typeof window.unityInstance ! undefined typeof window.unityInstance.SendMessage function) { window.unityInstance.SendMessage(ScreenOrientationManager, OnResolutionSet, JSON.stringify({ width, height, scale })); } }在ScreenOrientationManager.cs中新增接收方法public void OnResolutionSet(string jsonConfig) { try { var config JsonUtility.FromJsonResolutionConfig(jsonConfig); targetWidth config.width; targetHeight config.height; targetScale config.scale; Debug.Log($[Resolution] Set to {targetWidth}x{targetHeight} {targetScale}x); } catch (System.Exception e) { Debug.LogError($[Resolution] Parse failed: {e p a hrefhttps://download.csdn.net/download/qq_41603955/89162823 stylecolor:#ec7500;font-size:14px; 本文还有配套的精品资源点击获取 /a img altmenu-r.4af5f7ec.gif srchttps://csdnimg.cn/release/wenkucmsfe/public/img/menu-r.4af5f7ec.gif stylewidth:16px;margin-left:4px;vertical-align:text-bottom;cursor:text; /p