基于最优缩放比例适配Div至屏幕的游戏实体位置偏移问题
云游戏固定帧适配窗口时实体位置偏移问题
背景
我开发的云游戏有固定尺寸的游戏帧:
// 关卡帧原始尺寸 const frameWidth = 1140; const frameHeight = 518;
我试过多款2D渲染库,但都因Bug或功能不足放弃。由于游戏是云原生架构,客户端仅负责收发世界数据包并渲染场景,因此选择浏览器原生HTML渲染方案。
我希望将游戏帧适配至窗口,采用能完整放入屏幕的最小缩放比例并居中显示,为此编写了以下函数:
import ScreenSize from '@/app/util/ScreenSize'; export default function optimalFitRatio(originalSize: ScreenSize, fitToSize: ScreenSize): number { const horizontalRatio = fitToSize.width / originalSize.width; const verticalRatio = fitToSize.height / originalSize.height; return Math.min(horizontalRatio, verticalRatio); }
问题
当前遇到的核心问题:游戏内世界实体的位置会随窗口分辨率变化而偏移,此前使用2D渲染库时无此问题。
最小复现代码
function optimalFitRatio(originalSize, fitToSize) { const horizontalRatio = fitToSize.width / originalSize.width; const verticalRatio = fitToSize.height / originalSize.height; return Math.min(horizontalRatio, verticalRatio); } // 关卡帧原始尺寸 const frameWidth = 1140; const frameHeight = 518; class LevelView { container1 = undefined; container2 = undefined; resizeListener = undefined; attached = false; scale = 0; width = 0; height = 0; attach() { this.attached = true; // 创建第一个容器 this.newContainer(); // 监听窗口 resize this.resizeListener = this.resizeFrame.bind(this); window.addEventListener('resize', this.resizeListener); this.resizeFrame(); } detach() { window.removeEventListener('resize', this.resizeListener); this.container1?.remove(); this.attached = false; } renderLevel(world) { this.container1?.remove(); this.newContainer(); this.orientContainer(); // 按Z轴排序实体 // sortEntitiesByZ(world.entities); for (const entity of world.entities) { if (entity.type == "Entity") { this.renderEntity(entity); } } } renderEntity(entity) { // 设置元素样式 const orient = shape => { shape.style.position = "fixed"; shape.style.left = `${entity.x}px`; shape.style.top = `${entity.y}px`; shape.style.transform = `rotate(${entity.rotationRadians}rad)`; shape.style.transformOrigin = "center"; shape.style.userSelect = "none"; }; // 创建实体图片 const bitmap = document.createElement("img"); bitmap.src = "https://i.imgur.com/Dx200x9.png"; orient(bitmap); this.container2.appendChild(bitmap); } resizeFrame() { if (!this.attached) { return; } // 计算适配比例 const ratio = optimalFitRatio( { width: frameWidth, height: frameHeight }, { width: window.innerWidth, height: window.innerHeight }, ); // 缓存尺寸和比例 this.width = frameWidth * ratio; this.height = frameHeight * ratio; this.scale = ratio; // 更新容器样式 this.orientContainer(); } orientContainer() { // 设置外层容器尺寸 const [w, h] = [this.width, this.height]; this.container1.style.width = `${w}px`; this.container1.style.height = `${h}px`; // 缩放内层容器 this.container2.style.transform = `scale(${this.scale})`; // 外层容器居中 this.container1.style.left = `${window.innerWidth / 2 - w / 2}px`; this.container1.style.top = `${window.innerHeight / 2 - h / 2}px`; } newContainer() { const c0 = document.body; const c1 = document.createElement('div'); c1.style.display = "inline-block"; c1.style.background = "#000"; c1.style.overflow = "hidden"; c1.style.position = "fixed"; c1.style.userSelect = "none"; c0.appendChild(c1); this.container1 = c1; const c2 = document.createElement('div'); c1.appendChild(c2); this.container2 = c2; } } const levelView = new LevelView(); levelView.attach(); levelView.renderLevel({ entities: [{ type: "Entity", x: 100, y: 100, rotationRadians: 0, }], });
(更新说明:此前遗漏了{ type: "Entity", ...p },现在可复现问题)
修复方案
问题根源在于实体的定位逻辑:你给实体设置了position: fixed,使其相对于视口定位,而非缩放后的容器container2;同时container2未设置参考定位,导致缩放时实体坐标无法同步适配。
修改步骤
- 给
container2添加相对定位,作为实体的定位参考,并设置其尺寸为游戏帧原始大小:
// 在newContainer方法中修改c2的创建逻辑 const c2 = document.createElement('div'); c2.style.position = "relative"; c2.style.width = `${frameWidth}px`; c2.style.height = `${frameHeight}px`; c1.appendChild(c2); this.container2 = c2;
- 将实体的
position从fixed改为absolute,使其相对于container2定位:
// 修改renderEntity中的orient函数 const orient = shape => { shape.style.position = "absolute"; shape.style.left = `${entity.x}px`; shape.style.top = `${entity.y}px`; shape.style.transform = `rotate(${entity.rotationRadians}rad)`; shape.style.transformOrigin = "center"; shape.style.userSelect = "none"; };
修改后,实体将基于原始游戏帧尺寸的container2定位,而container2会随外层容器整体缩放、居中,窗口resize时实体位置将自动同步适配,不会再出现偏移。
内容的提问来源于stack exchange,提问作者Hydroper
相关产品推荐
相关产品推荐

