name: galacean-mobile description: Galacean Engine 移动端游戏开发指南,包含适配、优化和最佳实践
Galacean 移动端游戏开发
1. 视口配置(关键)
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover">
完整 HTML 头部配置:
<head>
<!-- 基础视口 -->
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover">
<!-- 禁止自动识别 -->
<meta name="format-detection" content="telephone=no, date=no, address=no">
<!-- iOS全屏 -->
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
<!-- 主题色 -->
<meta name="theme-color" content="#1a1a2e">
</head>
2. CSS 移动端适配
* {
/* 禁止触摸高亮 */
-webkit-tap-highlight-color: transparent;
touch-action: none;
/* 禁止文字选中 */
-webkit-user-select: none;
user-select: none;
-webkit-touch-callout: none;
}
html, body {
/* 禁止橡皮筋效果 */
overscroll-behavior: none;
-webkit-overflow-scrolling: none;
/* 全屏显示 */
width: 100%;
height: 100%;
overflow: hidden;
}
/* 安全区域适配(刘海屏) */
#ui {
padding-top: env(safe-area-inset-top);
padding-bottom: env(safe-area-inset-bottom);
}
/* 响应式字体 */
.responsive-text {
font-size: clamp(14px, 4vw, 18px);
}
3. 移动端检测
// 检测移动端
const isMobile = /iPhone|iPad|iPod|Android|webOS|BlackBerry|Windows Phone/i.test(navigator.userAgent);
// 检测触摸设备
const isTouchDevice = 'ontouchstart' in window || navigator.maxTouchPoints > 0;
// 检测iOS
const isIOS = /iPad|iPhone|iPod/.test(navigator.userAgent);
// 检测横竖屏
const isPortrait = window.innerHeight > window.innerWidth;
4. 相机适配
基础适配
function setupMobileCamera(camera: Camera): void {
const aspect = window.innerWidth / window.innerHeight;
if (aspect < 1) {
// 竖屏:缩小视野以适应宽度
camera.orthographicSize = 9;
} else {
// 横屏:根据宽度调整
camera.orthographicSize = 9 / aspect;
}
camera.aspectRatio = aspect;
}
// 监听方向变化
window.addEventListener('orientationchange', () => {
setTimeout(() => {
setupMobileCamera(camera);
}, 100);
});
基于内容尺寸的精确适配(推荐)
根据游戏内容实际尺寸和视窗大小动态计算相机视野,确保内容完整显示:
// 游戏内容配置(根据实际游戏设置)
const BOARD_WIDTH = 6; // 内容宽度(世界单位)
const BOARD_HEIGHT = 6; // 内容高度(世界单位)
const PADDING = 0.9; // 边距系数(0-1),越大留白越多
/**
* 根据视窗尺寸计算相机正交大小
* 确保游戏内容能完整显示在屏幕内
*/
function calculateOrthoSize(): number {
const aspect = window.innerWidth / window.innerHeight;
if (aspect < 1) {
// 竖屏:检查宽度和高度,取较大值确保完整显示
const heightBasedSize = BOARD_HEIGHT / 2 / PADDING;
const widthBasedSize = BOARD_WIDTH / 2 / aspect / PADDING;
return Math.max(heightBasedSize, widthBasedSize);
} else {
// 横屏:以高度为基准
return BOARD_HEIGHT / 2 / PADDING;
}
}
// 相机初始化
const camera = cameraEntity.addComponent(Camera);
camera.isOrthographic = true; // 必须启用正交模式!
camera.orthographicSize = calculateOrthoSize();
camera.aspectRatio = window.innerWidth / window.innerHeight;
// 响应式更新
window.addEventListener('resize', () => {
camera.orthographicSize = calculateOrthoSize();
camera.aspectRatio = window.innerWidth / window.innerHeight;
});
关键点:
- 必须设置
camera.isOrthographic = true,否则orthographicSize不会生效 - 竖屏时窄屏幕设备(如手机)需要同时检查宽高,防止内容被截断
PADDING系数控制四周留白,建议 0.85-0.95
5. 触摸事件处理
禁止默认行为
// 禁止页面滚动
document.addEventListener('touchmove', (e) => {
e.preventDefault();
}, { passive: false });
// 禁止双击缩放
let lastTouchEnd = 0;
document.addEventListener('touchend', (e) => {
const now = Date.now();
if (now - lastTouchEnd <= 300) {
e.preventDefault();
}
lastTouchEnd = now;
}, { passive: false });
// 禁止长按菜单
document.addEventListener('contextmenu', (e) => e.preventDefault());
触摸反馈
class MobileButton extends Script {
private onTouchStart(): void {
// 按下效果:缩小
this.entity.transform.setScale(0.95, 0.95, 1);
}
private onTouchEnd(): void {
// 松开效果:恢复
this.entity.transform.setScale(1, 1, 1);
}
onAwake(): void {
const canvas = document.getElementById('canvas');
canvas?.addEventListener('touchstart', () => this.onTouchStart(), { passive: true });
canvas?.addEventListener('touchend', () => this.onTouchEnd(), { passive: true });
}
}
6. 性能优化
DPR 限制
// 限制DPR避免性能问题
const dpr = Math.min(window.devicePixelRatio || 1, 2);
engine.canvas.resizeByClientSize(dpr);
动画优化
// 移动端使用更短的动画时间
const animationDuration = isMobile ? 0.2 : 0.3;
// 减少同时进行的动画数量
const MAX_CONCURRENT_ANIMATIONS = isMobile ? 10 : 20;
7. 横竖屏适配
CSS 媒体查询
/* 竖屏 */
@media screen and (orientation: portrait) {
#game-container {
width: 100vw;
height: 100vh;
}
}
/* 横屏 */
@media screen and (orientation: landscape) {
#game-container {
width: 100vw;
height: 100vh;
}
}
/* 强制竖屏提示(小屏幕横屏时) */
@media screen and (orientation: landscape) and (max-height: 500px) {
#orientation-hint {
display: flex;
}
#game-container {
display: none;
}
}
横竖屏切换处理
class OrientationManager extends Script {
onAwake(): void {
this.checkOrientation();
window.addEventListener('orientationchange', () => {
setTimeout(() => this.checkOrientation(), 100);
});
}
private checkOrientation(): void {
const isPortrait = window.innerHeight > window.innerWidth;
const hint = document.getElementById('orientation-hint');
if (!isPortrait && window.innerHeight < 500) {
// 小屏幕横屏,显示提示
hint?.style.setProperty('display', 'flex');
} else {
hint?.style.setProperty('display', 'none');
}
}
}
8. UI 适配
响应式布局
// 根据屏幕尺寸调整UI
function adaptUIForMobile(): void {
const width = window.innerWidth;
const isMobile = width < 768;
const scoreElement = document.getElementById('score');
if (scoreElement) {
scoreElement.style.fontSize = isMobile ? '16px' : '22px';
scoreElement.style.padding = isMobile ? '8px 16px' : '10px 30px';
}
}
触摸友好的按钮
.mobile-button {
min-width: 44px; /* iOS推荐最小触摸尺寸 */
min-height: 44px;
padding: 12px 24px;
/* 防止误触 */
margin: 8px;
/* 触摸反馈 */
transition: transform 0.1s;
}
.mobile-button:active {
transform: scale(0.95);
}
9. 常见移动端问题
问题1:点击延迟
解决:设置正确的 viewport
<meta name="viewport" content="width=device-width, user-scalable=no">
问题2:iOS 橡皮筋效果
解决:
body {
overflow: hidden;
position: fixed;
width: 100%;
height: 100%;
}
问题3:刘海屏适配
解决:
#ui {
padding-top: max(10px, env(safe-area-inset-top));
padding-bottom: max(10px, env(safe-area-inset-bottom));
}
问题4:虚拟键盘
解决:
const initialHeight = window.innerHeight;
window.addEventListener('resize', () => {
const keyboardHeight = initialHeight - window.innerHeight;
if (keyboardHeight > 150) {
// 键盘弹出,调整布局
}
});
10. 测试清单
- iOS Safari 正常显示
- Android Chrome 正常显示
- 横竖屏切换正常
- 触摸点击响应正常
- 无双击缩放
- 无页面滚动
- 刘海屏适配正常
- 性能流畅(60fps)
渲染系统职责分离
问题: 混用Canvas和HTML渲染UI导致定位混乱
原则: Canvas渲染游戏,HTML渲染UI,避免跨系统
// ❌ 混用:Canvas渲染预览,HTML渲染HUD
private nextPreviewEntity: Entity; // Canvas
private hudElement: HTMLElement; // HTML
// ✅ 分离:Canvas仅游戏,HTML仅UI
private board: Entity[][]; // Canvas游戏层
private nextCells: HTMLDivElement[]; // HTML UI层
updatePreview() { cell.style.background = color; } // DOM操作
触摸控制布局
推荐: 左侧方向键,右侧功能键,分组间距24px防误触
#controls { display: flex; justify-content: space-between; gap: 24px; }
.control-group { display: grid; gap: 8px; }