بنقرة واحدة
galacean-mobile
Galacean Engine 移动端游戏开发指南,包含适配、优化和最佳实践
التثبيت باستخدام Codex أو Claude انسخ هذا Prompt والصقه في Codex أو Claude أو مساعد آخر ليراجع صفحة Skill ويثبّتها لك.
القائمة
Galacean Engine 移动端游戏开发指南,包含适配、优化和最佳实践
التثبيت باستخدام Codex أو Claude انسخ هذا Prompt والصقه في Codex أو Claude أو مساعد آخر ليراجع صفحة Skill ويثبّتها لك.
استنادا إلى تصنيف SOC المهني
此 Skill 约定了 Galacean Engine 中自定义着色器的编写方式,包含 Shader.create、BaseMaterial 参数绑定、动画驱动、透明混合与常见排错。
此 Skill 包含了使用 Galacean Engine 物理引擎的注意事项、距离检测替代方案以及常见问题解决方案
此 Skill 包含了在 Galacean Engine 中通过 Script 脚本处理物体交互(如点击)的核心模式,基于 onPointerClick 实现
此 Skill 包含了在 Galacean Engine 中创建物理碰撞体的核心模式,涵盖动态碰撞体 (DynamicCollider) 和 静态碰撞体 (StaticCollider) 的实现
使用 Galacean Engine 开发2D游戏的核心模式,包括真2D(Sprite)和伪2D(3D正交投影)实现
此 Skill 包含了 Galacean Engine 相机的配置方法,包括透视/正交投影设置、俯视视角配置、窗口适配等
| name | galacean-mobile |
| description | Galacean Engine 移动端游戏开发指南,包含适配、优化和最佳实践 |
<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>
* {
/* 禁止触摸高亮 */
-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);
}
// 检测移动端
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;
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// 禁止页面滚动
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 });
}
}
// 限制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;
/* 竖屏 */
@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');
}
}
}
// 根据屏幕尺寸调整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);
}
解决:设置正确的 viewport
<meta name="viewport" content="width=device-width, user-scalable=no">
解决:
body {
overflow: hidden;
position: fixed;
width: 100%;
height: 100%;
}
解决:
#ui {
padding-top: max(10px, env(safe-area-inset-top));
padding-bottom: max(10px, env(safe-area-inset-bottom));
}
解决:
const initialHeight = window.innerHeight;
window.addEventListener('resize', () => {
const keyboardHeight = initialHeight - window.innerHeight;
if (keyboardHeight > 150) {
// 键盘弹出,调整布局
}
});
问题: 混用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; }