返回

客厅街机

一台可以真正玩起来的浏览器复古街机:拖动旋转 3D 桌面上的主机、CRT 与手柄,点击开机进入卡带库,挑选 Snake、Breakout 或 Blocks 三款原创小游戏——无需安装、没有存档,只有一个下午和「再来一局」。

作者: Theodore
在线体验
客厅街机在线体验

关于项目

Playroom 是一台开在浏览器里的客厅街机。打开页面就是一张 1989 年风格的游戏桌:CRT 电视、主机、手柄和一张插好的 Snake 卡带,可以拖动旋转、随意打量;点击主机或 POWER ON,镜头沿屏幕推近、穿过 CRT,进入陈列三张卡带的收藏库。

三款游戏都是 Playroom 原创:Snake 讲究连吃节奏,Breakout 有三关砖阵与道具,Blocks 用落点虚影和七块一袋的规则把堆叠做得可预期。没有账号、没有更新、没有存档——最高分只留在本机浏览器,音乐和音效可以一键关掉。

整个项目只依赖 Three.js 与 Vite,无后端、无环境变量,六个 GLB 硬件模型和一张 Tripo 面标贴花就是全部素材;WebGL 不可用时退回文字菜单,仍可完整游玩。

体验要点

  1. 拖动旋转客厅桌面上的整套硬件——CRT、主机、手柄与卡带;点击主机或 POWER ON,镜头沿屏幕推近、穿过 CRT 进入卡带库。
  2. 三张卡带、三款原创游戏:Snake 五秒内连吃叠到 5× 连锁、金果 50 分;Breakout 三关砖阵,接住 W 加宽球拍、3 变三球;Blocks 七块一袋、落点虚影引导、连消加分。
  3. 键盘方向键 / WASD 与空格操作,P 暂停、ESC 退出,移动端自动出现触控按键;最高分只存在你自己的浏览器里。
  4. 每款游戏有自己的芯片音乐循环与音效,菜单播放 CC BY 4.0 授权曲目《Trip Of Your Smile》;WebGL 不可用时退回文字按钮,照样能玩。

技术解析

  • 3D 资产 · 六件 GLB 硬件与运行时重绑定

    CRT、主机、手柄和三张卡带以六个 GLB 随项目交付(Three.js GLTFExporter 导出,每件 128–384 个三角面的低模),GLTFLoader 并行加载后按需克隆——克隆时把导出时为截图居中的位移归零、材质逐份复制,投影与接收阴影全部开启。卡带上的标签面按导出时的局部偏移定位识别并命名为 cartridge-label,运行时换成 Canvas 绘制的封面纹理;像素小人由 mascot-body、左右臂、天线、logo 贴花等命名节点组成骑装,logo 贴花是 Tripo 面标位图,关闭色调映射与深度写入以保持平面贴片清晰。

    模型层还留了一个替换口:public/models/manifest.json 的 controller 字段指向任意 GLB 时,fitProp 会按包围盒把它缩放到 2.54 单位宽、落地居中并接管原手柄的位置——换成自己(例如 Tripo 生成)的手柄模型不用改一行代码。

  • 屏幕与标签 · Canvas 纹理即 UI

    场景里没有一张外部贴图:CRT 屏幕是一块 1024×700 的 CanvasTexture,待机画面(PLAYROOM SYSTEM / PLAYER ONE / PRESS START)由 2D 上下文逐帧绘制,同时作为 map 与 emissiveMap 让屏幕自发光;三张卡带封面各自是 640×610 的 Canvas,按 catalog 里每款游戏的主色、墨色、编号与标语现场画出像素图案与标题字。

    点击开机时,屏幕生命周期控制器把材质切到「数据流」纹理,相机沿屏幕中心的球面轨道绕回正面再推近,最后穿过 CRT 切到卡带库;prefers-reduced-motion 下跳过整段镜头,直接切换。

  • 交互 · 指针手势、射线拾取与电影感轨道

    桌面视图里,同一个指针序列既是拖动轨道也是点击:手势控制器记录按下时的 theta / phi,位移超过阈值就当拖动、否则按点击处理;点击命中由 Raycaster 判定——桌面阶段整张桌子都是「开机」按钮,卡带库阶段命中卡带后沿父链回溯到带 gameId 的节点。松手后轨道不会僵在原地,电影感控制器会把手动偏移缓缓混回自动缓摆。

    悬停或聚焦某张卡带时,它抬高 0.24、前倾并轻微侧转,像素小人换到该游戏的专属动作;卡带按各自的正弦相位轻浮动。渲染循环里的插值统一用 1 − e^(−5·dt) 的帧率无关缓动,慢帧下也不会跳变。

  • 游戏与声音 · 独立 2D 会话与合成音效

    三款游戏是纯逻辑模块(create / step / input),会话层把它们画到独立的 2D Canvas 上——Snake 与 Breakout 480×480、Blocks 300×540;只在运行态调度 requestAnimationFrame,暂停即停帧,单帧步进上限 250ms,切标签页回来不会穿墙。游戏事件(吃到、碎砖、消行、道具、过关)统一进入特效层,并按优先级折叠成一条音效提示。

    音频全部由 Web Audio 振荡器现场合成:每款游戏一段 16 步的芯片旋律(BPM 82–120),二十余种 cue 是频率滑音加包络的组合,同时最多 64 个声部;菜单音乐是 CC BY 4.0 曲目,下载失败自动退回合成曲,标签页隐藏时静音、首次点击后才解锁音频上下文。最高分写入 localStorage,被拒绝时退回内存,绝不打断游戏。