体感小游戏:
- 检测人头
上 / 下 / 左 / 右转动 - 用头部方向控制贪吃蛇运动轨迹
- Vite
- TypeScript
- 原生 DOM + Canvas
- MediaPipe Tasks Vision(PoseLandmarker)
npm install npm run dev浏览器打开 http://localhost:5173。
- 点击
1. 打开摄像头 - 保持坐正、肩膀入镜,点击
2. 校准中立姿态 - 点击
3. 开始/重开 - 通过头部上下左右转动控制蛇移动
- 网格数量控制:可设置网格列数(12-60)和行数(10-40)
- 边界穿越:开启后,蛇碰到边界会从另一侧出现
- 速度控制:4-14 格/秒,支持运行中动态调节
src/ ├─ main.ts # 应用入口:UI 组装、事件绑定、状态联动 ├─ styles.css # 页面样式 ├─ motion/ │ └─ HeadMotionController.ts # 头部动作识别(摄像头、校准、方向判定) └─ snake/ └─ SnakeGame.ts # 贪吃蛇核心逻辑(移动、碰撞、参数更新) . ├─ index.html # 挂载根节点与前端入口脚本 ├─ package.json # 依赖与 npm scripts ├─ tsconfig.json # TypeScript 配置 ├─ vite.config.ts # Vite 构建/开发配置 ├─ public/models/ # 本地模型与 wasm 资源 │ ├─ pose_landmarker_lite.task │ ├─ hand_landmarker.task # 当前玩法未使用,可保留 │ └─ wasm/ └─ README.md dist/ # 构建产物(npm run build 生成) node_modules/ # npm 依赖 -
src/main.ts- 构建页面 UI
- 驱动三步流程:打开摄像头 -> 校准 -> 开始游戏
- 接收
HeadMotionController输出方向并驱动SnakeGame - 管理可调参数(网格、边界穿越、速度)
-
src/motion/HeadMotionController.ts- 初始化 MediaPipe PoseLandmarker
- 读取摄像头视频帧并输出方向事件
- 处理校准、跟踪状态、抗抖/防误判逻辑
-
src/snake/SnakeGame.ts- 维护蛇、食物、分数、时钟
- 实现可配置网格、边界穿越、速度
- 支持运行中参数更新(必要时重启当前局)
动作识别与游戏逻辑完全分离HeadMotionController只负责给出方向事件,不关心蛇怎么移动SnakeGame只关心方向输入,不关心方向是来自键盘还是摄像头
好处:
- 识别算法可以单独迭代,不破坏游戏规则
- 未来替换识别源(比如手机 IMU)时,游戏层几乎不用改
- UI 采用原生 DOM
- 游戏采用原生 Canvas
- 只保留 PoseLandmarker 这一条识别链路
好处:
- 首屏更快,依赖更少
- 可控性高,排查误判更直接
SnakeGameOptions统一承载玩法参数:cols、rows、wrapAround、speedMsupdateOptions()支持运行中修改并返回结果(是否重启、是否速度变化)
好处:
- 主界面控制项可以无缝映射到游戏内核
- 后续新增难度档位不需要改底层架构
识别效果逐步优化的关键路径。
思路:
- 使用鼻尖相对基线偏移,超过阈值即触发方向
问题:
- 对抖动敏感
- 中立附近容易误判
- 连续动作时容易反复触发
引入策略:
- 候选方向稳定帧确认(不是单帧即触发)
- 上下左右阈值不对称,
up阈值更严格 - 中立附近忽略微动
收益:
- 减少了“中立误判向上”
- 降低单帧噪声造成的误触发
代价:
- 响应速度变慢
为解决“迟缓”与“偶发不准”,改成以下组合:
- 相对肩膀中心坐标
- 不再只看鼻尖绝对位置,而是看
nose - shoulderCenter - 抵抗人体整体在画面中的平移漂移
- 低延迟平滑(EMA)
- 对
dx/dy做轻量平滑 - 保留跟手性同时抑制抖动
- 轴向主导判定
- 只有 X 或 Y 方向足够主导才判为左/右或上/下
- 降低斜向动作造成的串向误判
- 动态触发门槛
- 高置信方向可 1 帧确认
- 普通置信方向需 2 帧确认
- 最小触发间隔按“同方向/异方向”区分
- 校准与漂移补偿
- 校准改为多帧采样平均,不用单帧
- 中立区域内缓慢更新基线,吸收坐姿慢漂移
综合效果:
- 响应更快(方向切换更敏捷)
- 中立区更稳(误判明显下降)
- 长时间游玩不容易“越玩越偏”
- 将
cols/rows从常量改为运行时配置 - 网格变更时重建棋盘并重置当前局,防止旧坐标越界
- 在 tick 中增加
wrapAround分支 - 关闭时撞墙结束;开启时进行坐标环绕映射
- 把速度改成
speedMs参数 - 运行中改速度时只重启计时器,不重置当前局
默认优先读取本地模型路径(已包含在 public/models):
public/models/pose_landmarker_lite.taskpublic/models/wasm/*
本地文件缺失时会自动回退到 CDN。