Version: 1.0.31
让用户用一句话生成可独立打开的单文件 HTML 应用或文档,并落库到 fe-service。
本 skill 不主动调用 LLM,所有 HTML / app_name / features 由调用本 skill 的 LLM agent 直接产出后传入工具。
> ⚠️ 最高优先级规则(违反即为严重错误):
> 1. 游戏类应用 绝对禁止 包含任何形式的 AI 对手、人机对战、电脑自动下棋/出牌。棋类/对战类游戏 只允许 双人同屏轮流操作,不实现任何计算机决策逻辑。
> 2. 所有应用 绝对禁止 出现"导出"、"打印"、"保存"按钮或相关功能代码。
当用户需求匹配以下任一类别时,本 skill 参与:
关键词:应用、app、小游戏、游戏、网页、页面、工具、计算器、时钟、日历、翻译器、转换器
示例:
关键词:文档、文件、word、ppt、演示文稿、slides、pdf、excel、xlsx、表格、报告、简历、周报、日报、总结、方案、计划书
示例:
用户意图是"产出一个可独立打开、保存下来反复使用的文件或应用"时触发本 skill。
如果用户只是想让你"说一段话"或"回答问题",不要触发。
以下前缀仍可直接触发,不需要语义判断:
[create_app]<描述> → 创建新应用/文件[update_app:app_id=]<描述> → 更新已有应用/文件[install_check:session=] → 安装自检(仅调 myapp_ping)最长等待 300s。若 300s 内不能完成 HTML 生成与 myapp_register 调用,直接回复"应用创建失败,请稍后重试"。
app_name:应用/文件名,≤30 字features:JSON 字符串数组,3~8 条,每条 ≤30 字,描述具体功能/内容要点html_content:完整可独立打开的单文件 HTML(按类型选择模板风格) / 目标运行环境 — Android 8.1 WebView(Chrome/85):
所有 HTML 最终运行在搭载 Android 8.1 的智能屏 WebView 中,无鼠标/硬键盘,仅支持触摸和左右滑动。
生成代码时必须遵守以下兼容规则:
JS / Web API 兼容(feature detection 优先,禁止仅凭 UA 判断):
typeof IntersectionObserver !== 'undefined' 等特性检测,不要直接调用可能不存在的 APICSS.registerProperty、@property、content-visibility、aspect-ratio(CSS)、ResizeObserver(需检测)、structuredClone(用 JSON.parse(JSON.stringify()) 替代)??=、||=、&&=、import()动态导入、BigInt、globalThis(用 window 替代)ctx.roundRect()——这是 Canvas 2D 的新方法,Chrome 99 才加入,Chrome 85 WebView 直接抛异常导致整个 draw 函数崩溃。必须用以下自定义 helper 替代:```js
function rr(ctx, x, y, w, h, r) {
ctx.beginPath();
ctx.moveTo(x + r, y);
ctx.lineTo(x + w - r, y); ctx.arcTo(x + w, y, x + w, y + r, r);
ctx.lineTo(x + w, y + h - r); ctx.arcTo(x + w, y + h, x + w - r, y + h, r);
ctx.lineTo(x + r, y + h); ctx.arcTo(x, y + h, x, y + h - r, r);
ctx.lineTo(x, y + r); ctx.arcTo(x, y, x + r, y, r);
ctx.closePath();
}
// 用法:rr(ctx, x, y, w, h, radius); ctx.fill();
```
Promise、async/await、fetch、CSS Grid、Flexbox、CSS Variables、IntersectionObserver(需检测)、requestAnimationFramelocalStorage / sessionStorage 在 WebView 中可用,但不要依赖跨页面持久化CSS 兼容:
100vh 在 Android WebView 中等于视口高度(无地址栏问题),可直接使用position: sticky 在 Chrome 85 中支持,但父容器不能有 overflow: hidden,否则失效position: fixed 在 WebView 中软键盘弹出时可能错位,避免在输入框场景依赖 fixed 定位inset 属性(inset: 0 等简写)——Chrome 87 才支持,Chrome 85 不识别,会导致遮罩层无法撑满、内容偏移到左上角。替换写法:top:0; left:0; right:0; bottom:0(注意:box-shadow 中的 inset 关键字不受此限制,可以正常使用)backdrop-filter(Chrome 85 需前缀,WebView 不保证支持)clamp() 在 Chrome 85 中已支持,可正常使用-webkit-overflow-scrolling: touch 保证惯性滚动触摸与交互:
touchstart/touchmove/touchend 处理手势;click 在 WebView 中有 300ms 延迟,交互敏感场景改用 touch 事件 中加 user-scalable=no 或在 CSS 中加 touch-action: manipulationhover 状态(无鼠标),交互反馈改用 :active 或 JS touch 事件touchstart/touchend 计算 deltaX 实现,不要用第三方手势库(CDN 可能不可达)keydown/keyup)作为唯一控制方式,必须提供触摸按钮软键盘与输入框:
height: 100vh 做全屏布局的唯一约束fixed 元素可能被遮挡,底部操作栏改用 position: sticky 或监听 visualViewport.resizewindow.visualViewport 在 Chrome 61+ 可用,Chrome 85 支持弹窗 / Modal:
position: fixed; top:0; left:0; width:100%; height:100% 覆盖全屏overflow-y: auto; -webkit-overflow-scrolling: touchalert()/confirm()/prompt()(WebView 中可能被宿主拦截)| 设备 | 视口宽×高比例 |
|------|-----------|
| 普通智能屏 | 960×600 |
| mini智能屏 | 960×480 |
| 闺蜜机 | 1280×720 |
| 秋月 | 1280×800 |
| pad | 1920×1080 |
| pad17 | 2560×1600 |
布局以横屏为前提(宽 > 高),使用 vw/vh/百分比等响应式单位,禁止固定像素宽度导致小屏溢出或大屏留白。
字体大小建议用 clamp() 适配,如 font-size: clamp(14px, 2vw, 18px)。
应用类 — 交互式:
width:100vw; height:100vh; overflow:hidden 撑满全屏vmin 或百分比,确保 960px 小屏到 2560px 大屏均可用游戏类强制横版左右布局(所有游戏类应用必须遵守):
> ⚠️ 强制要求:所有游戏页面必须是横版布局,整体分为左右两栏,禁止竖版堆叠布局。
布局结构如下:
```
┌─────────────────────────────┬──────────────────┐
│ │ 状态(必展示) │
│ │ 关卡/得分/生命 │
│ 游戏区域(左栏) ├──────────────────┤
│ 占总宽约 65%~70% │ 下一块/预览 │
│ 高度撑满 100vh ├──────────────────┤
│ │ ◀ II ▶ 方向键 │
└─────────────────────────────┴──────────────────┘
```
右栏布局规则(状态区和方向键始终可见):
#status(关卡、难度、得分/目标分、生命):flex:0 0 auto,必须展示,不可压缩#game-preview(下一块/预览区):flex:0 0 auto,有预览需求的游戏必须展示,不可压缩#leaderboard(排行榜)和 #instructions(操作说明):当游戏有 #game-preview 时,不展示排行榜和操作说明(右栏空间有限,预览区优先)。仅在无预览需求的游戏中,才用 flex:1 1 0; min-height:0 + 标题 h3 固定 + .sb-inner 内容区 overflow-y:auto 独立滚动的方式展示#controls(方向键+暂停):flex:0 0 auto,固定在右栏底部,始终可见,不参与滚动CSS 骨架模板(必须使用):
```html
```
Canvas 尺寸初始化(JS 中必须这样写,确保 Android 8.1 WebView 中 canvas 像素与显示尺寸一致):
> ⚠️ Android 8.1 WebView 已知问题:页面刚加载时 clientWidth/clientHeight 可能为 0,导致 Canvas 尺寸变成 0×0、什么都画不出来。必须用以下模板,通过 window.load + 双层 requestAnimationFrame 延迟读取尺寸,并用 offsetWidth 做兜底。
Canvas CSS(必须用 position:absolute 撑满,不依赖 flex 布局尺寸):
```html
```
Canvas 尺寸初始化(必须在 window.load + 双层 requestAnimationFrame 后执行):
```js
function initCanvas() {
const gameArea = document.getElementById('game-area');
const canvas = document.getElementById('canvas');
// offsetWidth 兜底:Android 8.1 WebView 刚加载时 clientWidth 可能为 0
const w = gameArea.clientWidth || gameArea.offsetWidth || window.innerWidth * 0.7;
const h = gameArea.clientHeight || gameArea.offsetHeight || window.innerHeight;
canvas.width = w;
canvas.height = h;
// 格子大小根据 canvas 尺寸动态计算
const COLS = 30, ROWS = Math.floor(h / (w / COLS));
const cellW = w / COLS, cellH = h / ROWS;
// ... 其余游戏初始化逻辑
}
// 必须在 window.load 后、双层 rAF 确保布局渲染完毕再读尺寸
window.addEventListener('load', function() {
requestAnimationFrame(function() {
requestAnimationFrame(function() {
initCanvas();
});
});
});
```
游戏类 WebView 触摸适配要点(Android 8.1 WebView 无键盘/鼠标):
> ⚠️ 强制要求(主控方式):所有游戏的主要控制方式必须是触摸坐标直接映射——玩家手指按在画布哪里,角色/目标就跟到哪里(或朝那个方向移动),不需要抬手再点方向按钮。右下角的 ↑↓←→ 方向键和暂停键保留作为辅助控制,不是唯一控制入口。暂停按钮必须位于方向键 3×3 grid 的正中心格。
触摸坐标映射实现模板(贴在 Canvas/游戏容器上):
```js
// 主控:手指位置直接映射到游戏坐标
canvas.addEventListener('touchstart', onTouch, {passive: false});
canvas.addEventListener('touchmove', onTouch, {passive: false});
function onTouch(e) {
e.preventDefault();
const rect = canvas.getBoundingClientRect();
const touch = e.touches[0];
// 将屏幕像素坐标换算为 canvas 内部坐标
const cx = (touch.clientX - rect.left) * (canvas.width / rect.width);
const cy = (touch.clientY - rect.top) * (canvas.height / rect.height);
movePlayerTo(cx, cy); // 替换为游戏实际函数:直接设置目标坐标
}
```
touchstart 记录起点,touchend 计算 deltaX/deltaY,阈值 20px 内忽略,超过则取绝对值较大的轴作为方向touchmove 实时跟随手指坐标飞机大战类游戏专项规则:
setInterval 或游戏主循环中每隔固定帧数发射),禁止要求玩家手动点击射击俄罗斯方块/消除类游戏专项规则:
lock() → spawn(),下一块在同一帧内出现。禁止 LOCK_DELAY 缓冲期spawnPiece() 必须将新方块赋值给全局变量(如 cur = shape),否则 drawGrid() 中 if(cur != null) 判断为 false 导致方块不显示interval = Math.max(83, 800 - (level - 1) 5 (1000/60))(每关减约 5 帧,60fps 基准)#game-preview 中展示,有预览时不展示排行榜和操作说明 方向键 + 暂停按钮模板(放在右栏 #controls 内,作为辅助控制):
布局规则:暂停按钮放在方向键 3×3 grid 的正中心格,不单独放在键盘上方。整体视觉要炫酷:方向键用半透明玻璃质感,暂停键用蓝色渐变 + 光晕,:active 有明显按压反馈。
```html
/ 方向键:半透明玻璃质感 /
.dpad-btn {
background: linear-gradient(145deg, rgba(255,255,255,.22), rgba(255,255,255,.06));
border: 1.5px solid rgba(255,255,255,.35);
border-radius: 50%;
color: #fff;
font-size: 4.5vmin;
touch-action: manipulation;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 2px 8px rgba(0,0,0,.4), inset 0 1px 0 rgba(255,255,255,.25);
cursor: pointer;
transition: transform .08s;
}
.dpad-btn:active {
background: linear-gradient(145deg, rgba(255,255,255,.45), rgba(255,255,255,.15));
transform: scale(.88);
box-shadow: 0 1px 4px rgba(0,0,0,.5), inset 0 1px 0 rgba(255,255,255,.3);
}
/ 暂停键与方向键样式完全一致,仅字号加粗 + 细边框区分;用 II 代替 ⏸ 符号(WebView 渲染兼容) /
#btn-pause {
font-size: 4.5vmin;
font-weight: bold;
border-color: rgba(255,255,255,.6);
}
document.getElementById('btn-up').addEventListener('touchstart', e=>{e.preventDefault();changeDir(0,-1);},{passive:false});
document.getElementById('btn-down').addEventListener('touchstart', e=>{e.preventDefault();changeDir(0, 1);},{passive:false});
document.getElementById('btn-left').addEventListener('touchstart', e=>{e.preventDefault();changeDir(-1,0);},{passive:false});
document.getElementById('btn-right').addEventListener('touchstart', e=>{e.preventDefault();changeDir( 1,0);},{passive:false});
document.getElementById('btn-pause').addEventListener('touchstart', e=>{e.preventDefault();togglePause();},{passive:false});
// 桌面调试保留键盘
document.addEventListener('keydown', e=>{
if(e.key==='ArrowUp') changeDir(0,-1);
if(e.key==='ArrowDown') changeDir(0, 1);
if(e.key==='ArrowLeft') changeDir(-1,0);
if(e.key==='ArrowRight') changeDir( 1,0);
if(e.key===' ') togglePause();
});
```
changeDir(dx, dy) 和 togglePause() 替换为游戏实际函数名;坐标映射类游戏中方向键可改为触发"持续向该方向移动"逻辑vmin 单位,960px 小屏到 2560px 大屏均可用游戏角色视觉规范(卡通可爱风格,禁止只画纯色圆点/方块):
```js
function drawHead(ctx, x, y, w, h, dir) {
ctx.save();
ctx.translate(x + w/2, y + h/2);
const angle = {right:0, down:Math.PI/2, left:Math.PI, up:-Math.PI/2}[dir] || 0;
ctx.rotate(angle);
// 头部圆角矩形
ctx.fillStyle = '#2ecc71';
roundRect(ctx, -w/2, -h/2, w, h, w*0.3);
ctx.fill();
// 眼睛
[[-w0.15, -h0.15],[w0.15, -h0.15]].forEach(([ex,ey])=>{
ctx.fillStyle='#fff'; ctx.beginPath(); ctx.arc(ex,ey,w0.12,0,Math.PI2); ctx.fill();
ctx.fillStyle='#222'; ctx.beginPath(); ctx.arc(ex+w0.03,ey,w0.06,0,Math.PI*2); ctx.fill();
});
ctx.restore();
}
function roundRect(ctx,x,y,w,h,r){
ctx.beginPath();ctx.moveTo(x+r,y);ctx.lineTo(x+w-r,y);ctx.arcTo(x+w,y,x+w,y+r,r);
ctx.lineTo(x+w,y+h-r);ctx.arcTo(x+w,y+h,x+w-r,y+h,r);ctx.lineTo(x+r,y+h);
ctx.arcTo(x,y+h,x,y+h-r,r);ctx.lineTo(x,y+r);ctx.arcTo(x,y,x+r,y,r);ctx.closePath();
}
```
平台跳跃 / 跑酷类游戏触摸控制规范(马里奥、跑酷等左右移动+跳跃类游戏必须遵守):
> 核心设计:左右半屏控制方向,上滑触发跳跃,斜滑(如右上方)同时触发移动+跳跃,完全模拟实体手柄的拇指操作。
```js
let tSX = 0, tSY = 0; // 触摸起点,每次 touchstart 或跳跃触发后重置
canvas.addEventListener('touchstart', e => {
e.preventDefault();
const t = e.touches[0];
tSX = t.clientX; tSY = t.clientY;
}, {passive: false});
canvas.addEventListener('touchmove', e => {
e.preventDefault();
const t = e.touches[0];
const dx = t.clientX - tSX;
const dy = t.clientY - tSY; // 负值 = 向上
// 左右移动:根据手指当前位置在画布左/右半区判断方向
const rect = canvas.getBoundingClientRect();
if (t.clientX < rect.left + rect.width / 2) {
moveLeft();
} else {
moveRight();
}
// 上滑检测跳跃:阈值 20px,触发后立即重置 tSY 防止重复触发
if (dy < -20) {
jump();
tSY = t.clientY; // 重置参考点,允许同一次触摸连续触发(斜滑等)
}
}, {passive: false});
canvas.addEventListener('touchend', e => {
stopMove(); // 手指抬起停止移动
}, {passive: false});
```
dy < -20(负值向上)触发跳跃,同一次 touchmove 序列可多次触发(重置 tSY)moveRight() + jump(),天然支持斜跳touch-action:none,Canvas 加 { passive: false } 并 e.preventDefault()alert(),改用页面内自定义弹窗显示胜负信息跳跃 / 平台类游戏角色定位规范(跳一跳、跑酷、马里奥等必须遵守):
> ⚠️ 已知 Bug 根因:用 ph = PLAYER_H / squish 作为绘制高度来定位头部/身体,当落地弹跳动画触发 squish ≠ 1 时,ph 变大,角色视觉上"沉入"平台下方。
正确做法:py 始终表示角色脚底的 y 坐标(即站在平台顶面),squish 只影响宽度(身体变宽/变窄),绝不影响高度定位:
```js
// py = 脚底 y(始终贴平台顶面,不受 squish 影响)
// squish: 落地压缩系数,1=正常,>1=横向压扁(宽变大、高不变)
function drawPlayer(ctx, px, py, squish) {
const W = PLAYER_W * squish; // 宽度随 squish 变化
const H = PLAYER_H; // 高度固定,不除以 squish
const x = px - W / 2;
const y = py - H; // 脚底 py 往上量固定 PLAYER_H
// 绘制身体:rr(ctx, x, y, W, H, radius)
}
```
squish 动画只修改 W(或同时等比缩小 H 但必须同步上移 y = py - H),确保脚底始终在 pyy = py - PLAYER_H / squish,这会在 squish > 1 时让角色上移,squish < 1 时下沉> ⚠️ 强制要求:所有游戏类应用必须内置难度阶段机制,默认从简单难度开始,通过 10 关后自动升级为中等难度,通过 20 关后升级为困难。以飞机大战为参照基准,其他游戏按比例换算。
难度分级标准(以飞机大战为基准示例):
| 阶段 | 关卡范围 | 敌机坠落速度 | 子弹发射间隔 | 过关目标分 | 每关目标分增量 |
|------|---------|------------|------------|---------|------------|
| 简单(Easy) | 第 1~10 关 | 基础速度 × 1/2 | 300ms/发 | 100 分 | +10 分/关 |
| 中等(Medium)| 第 11~20 关 | 基础速度 × 80% | 200ms/发 | 200 分 | +10 分/关 |
| 困难(Hard) | 第 21 关起 | 基础速度 | 100ms/发 | 300 分 | +10 分/关 |
```js
let level = 1; // 每通关 +1
function getDifficulty(level) {
if (level <= 10) return {
speedMul: 1/2, bulletInterval: 300,
targetScore: 100 + (level - 1) * 10 // 100, 110, 120...
};
if (level <= 20) return {
speedMul: 0.8, bulletInterval: 200,
targetScore: 200 + (level - 11) * 10 // 200, 210, 220...
};
return {
speedMul: 1, bulletInterval: 100,
targetScore: 300 + (level - 21) * 10 // 300, 310, 320...
};
}
```
文档/Word/PDF 风格:
max-width: min(1200px, 92vw); margin: 3vh auto; padding: 4vh 5vwclamp(14px, 1.8vw, 18px),标题 clamp(20px, 3vw, 32px)PPT/演示文稿风格:
包裹 是一页幻灯片reveal.js 内部会等比缩放到实际视口,960×600 基准保证所有设备文字清晰不溢出
Excel/表格风格:
width:100vw; height:100vh; overflow:auto; box-sizing:border-box; padding:2vh 3vw 宽度 100%,带 border 和交替行色- 表头字号
clamp(12px, 1.6vw, 16px),单元格字号 clamp(12px, 1.4vw, 15px) - 表头点击可排序(内联 JS 实现)
- 单元格
contenteditable="true" 可编辑 所有类型通用禁止项:
- 禁止出现"导出"、"打印"、"保存"功能按钮
- 禁止
@media print 相关样式 - 禁止
window.print()、文件下载等功能
- 图标生成(icon_base64):
- 产出一个 256×256 的 SVG 图标
- 风格:圆角矩形纯色背景 + 居中的主题图形(用 SVG path/circle/rect 绘制,禁止使用
或 emoji) - 背景用饱和度高的纯色,中间图形用与背景对比强烈的颜色(可用白色、也可用多色彩搭配,参考 App Store 图标风格)
- SVG 文本做 base64 编码后传入 icon_base64(不带
data:image/svg+xml;base64, 前缀) - SVG 原文不超过 3KB,禁止使用
元素或 emoji
- 【必须执行】HTML 自检 — 提交前逐项检查:
生成 html_content 后,必须在脑内逐项检查以下条件,任一不通过则必须修改代码后再提交:
自检项 A — 禁止 AI/电脑对手(仅游戏/棋类/对战类需检查):
- [ ] 代码中是否存在任何"电脑走棋/出牌/移动"的函数?(如
computerMove, aiMove, makeAIMove, botPlay, autoPlay, cpuTurn)→ 若有,删除整个函数及其调用 - [ ] 代码中是否存在
minimax, alphaBeta, evaluate, bestMove, getRandomMove, Math.random() 用于选择落子位置?→ 若有,删除 - [ ] 代码中是否存在"难度选择"(easy/medium/hard)或"人机/人人"模式切换?→ 若有,删除,只保留双人模式
- [ ] 游戏流程是否为:玩家A操作 → 切换到玩家B → 玩家B操作 → 切换回玩家A → 循环?→ 必须是
- [ ] 是否有任何
setTimeout/setInterval 用于延迟执行电脑的操作?→ 若有,删除
自检项 B — 禁止导出/打印/保存(所有类型需检查):
- [ ] 是否存在"导出"、"打印"、"保存"、"下载"按钮或菜单项?→ 若有,删除
- [ ] 是否存在
window.print(), document.execCommand('SaveAs'), URL.createObjectURL, download 属性?→ 若有,删除
自检项 C — Android 8.1 WebView 兼容(所有类型需检查):
- [ ] 是否使用了 Chrome 86+ 才支持的 CSS/JS API(
@property、content-visibility、structuredClone、??=/||=/&&= 赋值运算符)?→ 若有,替换为兼容写法 - [ ] 是否使用了
ctx.roundRect()?→ Chrome 99+,必须替换为自定义 rr() helper(用 arcTo 实现) - [ ] Canvas 内是否使用了 emoji 字符(fillText 中含 🔥❤️ 等)?→ WebView 字体渲染异常,必须替换为纯文字或几何图形
- [ ] 游戏布局是否为横版左右两栏(左栏游戏区、右栏控制区)?→ 若是竖版堆叠,必须改为横版
- [ ] 右栏有
#game-preview(下一块/预览区)时,是否已去掉 #leaderboard 和 #instructions?→ 预览区与排行/说明互斥,有预览时不展示排行和说明。无预览区的游戏中,排行/说明用 flex:1 1 0;min-height:0 + h3 固定 + .sb-inner 内滚动。#controls 始终 flex:0 0 auto 固定底部 - [ ] Canvas CSS 是否用
position:absolute;top:0;left:0;width:100%;height:100% 撑满左栏,而非依赖 flex 布局?→ 若只用 width:100%;height:100% 而无 position:absolute,必须修正 - [ ] Canvas 尺寸初始化是否在
window.load + 双层 requestAnimationFrame 内执行,且用 offsetWidth 做兜底(防止 Android 8.1 WebView 初始 clientWidth 为 0)?→ 若直接在顶层读 clientWidth,必须改为延迟初始化模板 - [ ] 需要方向控制的游戏:是否在 Canvas 上绑定了
touchstart/touchmove 做坐标直接映射(主控),且右下角仍保留方向键 + 暂停按钮(辅助)?→ 若只有方向按钮而无触摸坐标映射,必须补充 - [ ] 飞机大战类游戏:子弹是否自动连续发射(
setInterval 或主循环计时),而非要求玩家手动点击射击?→ 若需要手动触发,必须改为自动发射 - [ ] 俄罗斯方块/消除类游戏:①初始下落间隔是否为800ms(经典NES速度)?②贴地后是否立刻lock+spawn(零等待,无LOCK_DELAY缓冲)?③
spawnPiece()是否将新方块赋值给全局变量(cur=shape)?→ 若不符合,按专项规则修正 - [ ] 游戏内的开始/结束/暂停遮罩层是否用
position:absolute;top:0;left:0;right:0;bottom:0;display:flex;align-items:center;justify-content:center 居中(禁止用 inset:0,Chrome 85 不支持)?→ 若用 inset:0 或固定像素定位,内容会在智能屏上贴左上角,必须修正 - [ ] 游戏是否实现了三段式难度(简单1~10关:速度×1/2、300ms/发、100+10×n分 → 中等11~20关:速度×80%、200ms/发、200+10×n分 → 困难21关起:基础速度、100ms/发、300+10×n分),且第1关使用简单参数?→ 若全程同一难度或开局就很快,必须按难度分级模板修正
- [ ] 游戏角色是否有视觉辨识度(蛇头有眼睛、食物有形状等),而不是纯色圆点/方块?→ 若只是纯色几何形,必须改为卡通风格
- [ ] 是否使用了原生
alert()/confirm()/prompt()?→ 若有,改为页面内自定义弹窗 - [ ] 游戏容器/Canvas 是否加了
touch-action:none 并在 touch 事件中调用 e.preventDefault()?→ 若未处理,补充 - [ ] 可点击/可触摸元素的触摸区域是否 ≥ 44×44px?→ 若不足,调整尺寸
- [ ] 跳跃/平台类游戏(跳一跳、跑酷等):绘制角色时是否用固定常量
PLAYER_H 定位脚底坐标(py 为脚底 y,头顶为 py - PLAYER_H),而非用 PLAYER_H / squish 等变形高度定位?→ squish(落地压缩动画)只能影响角色宽度/变窄,绝不能影响脚底 py 的计算,否则 squish≠1 时角色视觉上会"沉入"平台下方
⚠️ 如果自检发现违规代码,不要提交,先修正 html_content 再继续下一步。
5.5. 【必须执行】JS 语法验证(应用类/游戏类必须,文件类可选):
在调用 myapp_register 之前,用 exec 工具对 html_content 中的 代码块做语法验证,确保不因语法错误导致游戏在智能屏上完全空白:
```bash
# 提取