双座固定路线兼容记录
当前主玩法已统一为「僵尸突围」:1–4 人在广州城市中步行、寻找电动车、驾驶、双座共乘并停车下车。本文保留旧两人固定路线配送规则与验收记录,旧邀请和云端房通过 /coop-legacy.html 继续兼容,不再作为首页的独立玩法选择。
两名玩家共乘一辆双座电动车,把外卖穿过尸群送到指定地点:驾驶员负责路线、速度和近身踢击,后座射手负责瞄准、武器与弹药,途中可停车搜集资源。旧兼容入口位于 /coop-legacy.html,场景独立按需加载。原有单座竞速、街区和车库继续保留。
组队与目标
Steam 桌面版创建双人好友房,邀请好友或输入房间号加入。两人选择座位、分别准备后,由房主开局;房主也能选择后座。接受 Steam 合作邀请会先打开合作页,再加入对应房间。创建和加入时沿用各自已解锁的角色外观,车体沿用驾驶员外观,双座附件收起后置配送箱及音箱。
网页版已增加云乐坊登录与 Play 云端合作房间接入代码,详见 云端合作;需要发布 Play 房间、签票函数和网页后生效。Steam 正式 App ID 尚未配置,真实双设备邀请与联机尚未验收;开发 WebSocket 可进行双浏览器试玩。
8 分钟内沿广州道路,把外卖送到约 5.398 公里外的腾讯广州大厦。主目标是送达:进入绿色送餐点,在目的地 6 m 范围内、速度不超过 0.6 m/s 连续停稳 2 秒,即完成交付;驶离或加速会清空交付进度,需要重新停稳。HUD 显示订单地点、剩余距离与交付进度。
沿途有六个可选资源箱,分为弹药和维修:在 4 m 范围内停稳自动领取,补充双方共享的弹药库存或车体耐久;每箱只能领取一次,库存和耐久遵守原有上限。不要求收齐资源箱即可送达,搜集数量会在 HUD 与结算中显示。
关卡保留九处交错路障、三处固定补给区、普通僵尸与跑动僵尸。补给点在约 1.587 / 3.262 / 4.706 公里;停稳自动领取一次修车和弹药,两位队员确认后可交换座位,同一补给区最多一次。驶离后继续前往下一补给点,路过未停车不会领取补给。
车体耐久归零、超时或一人断线会结束本局。结算显示外卖送达或配送失败、用时、搜集资源数与双方击退数,点击「重新组队」创建新房间。开局后不接受新玩家;此双座玩法暂不支持断线续局、房主迁移、步行战斗、单人 AI 后座和四人合作。本局统计独立于竞速排行榜与奖励。新 /coop.html 使用统一城市规则,/city-coop.html 是其兼容地址;旧云端房不会自动迁移为新城市房。
广州路线与地图来源
路线从珠江南岸玉菡路、滨江东路附近出发,经艺洲路、广州塔路、赤岗北路、新港中路、新港东路、海洲路、科展街,到鼎新路腾讯广州大厦附近送餐。广州塔补给后按现有道路连接短距离折返,再沿新港路前往琶洲;没有补出地图中尚未连通的滨江捷径。
| 站点 | 累计路程 | 用途 |
|---|---|---|
| 滨江东路 · 南岸 | 0 km | 出发 |
| 广州塔路 | 1.587 km | 第一次补给与换座 |
| 广东省第二人民医院 · 新港中路 | 3.262 km | 第二次补给与换座 |
| 海洲路 | 4.706 km | 第三次补给与换座 |
| 腾讯广州大厦 · 鼎新路 | 5.398 km | 送餐交付 |
合作模式复用街区已发布的 public/maps/kecun.json、riverside.json、pazhou.json。道路与建筑轮廓保留 OSM 节点、道路及建筑 ID,珠江保留原水岸和岛屿孔洞;版权为 © OpenStreetMap contributors,数据采用 ODbL-1.0。现有已确认的短运河桥作为平地道路通行,未验证高架、隧道和步道不纳入合作路线。道路为应急封闭场景,可双向通过;停车位置是沿途街边玩法点,不代表建筑实测入口。
权威端与客户端使用 packages/dayun-kicker-coop/src/guangzhouRouteData.ts 的同一份 172 点离线路线。客户端进入合作场景后读取上述三份已有地图文件,只构建路线附近的背景道路和建筑,按块显示,退出合作场景统一释放;复用现有广州塔、腾讯楼层与斜幕峡谷、医院分高凹院模型结构,近景参观细节不加载。普通背景楼缺少高度时使用外观估算。蓝色地面导向线与箭头沿真实路线铺设。地图模块不加载整套街区 UI、任务、Rapier 或 BFF。
地图就绪后才可准备,避免开局时读取道路数据。路线进度、转弯提示与补给使用沿路累计距离;广州塔折返段不会跳到重叠的另一分支。
配送与资源箱由同一权威规则推进。快照中的可选 delivery 描述目的地、交付状态和毫秒进度,resources 描述箱子位置、种类与共享领取状态;客户端只负责展示。旧版未携带这些字段的快照继续显示撤离目标与 escaped 结算,不会把旧比赛误报为送餐完成。
个人资料与教程
候场「名称与衣服配色」支持每人独立修改 1–24 字符名称、选择常用衣服色或自定义颜色;「恢复原配色」保留原皮肤配色。名称和颜色保存于本机,创建/加入时同步给伙伴;颜色仅覆盖上衣、袖口,不改变车身、裤子或头盔。候场修改资料会取消本人的准备;伙伴准备状态保留。开局后资料固定,本局结束再编辑。
每位玩家首次驾驶与首次担任射手分别获得三步操作引导。驾驶员练习加速转向、刹车和踢击;射手练习瞄准、射击和换弹。引导按本人实际输入和权威弹药变化推进,完成或跳过后在本机记忆;HUD「教程」可重新打开。引导不暂停对局,点击按钮后焦点返回场景,伙伴可继续操作。
操作
| 操作 | 键鼠 | 标准手柄 |
|---|---|---|
| 加速 / 刹车 | W / S 或上下方向键 | RT / LT |
| 转向 | A / D 或左右方向键 | 左摇杆 |
| 司机踢击 | Space / Q | X |
| 后座瞄准 | 按住拖动或方向键 | 右摇杆 |
| 射击 | 鼠标左键 / Space | RT |
| 换弹 | R | X |
| 冲锋枪 / 霰弹枪 / 鸡毛掸子 / 煤气罐 / 弹弓 | 1–5 / E | Y 切换 |
| 操作菜单 | Escape | 菜单键 |
触屏提供基础驾驶、拖动瞄准、开火和换弹按钮。手机实机合作尚未验收。菜单只释放自己的操作,伙伴与房主模拟继续;失焦和切后台也释放操作。声音使用现有主音量、引擎、音效及静音设置,菜单可切换静音,合作模式不会加载背景音乐。
智能侧踢
司机按原来的 Space / Q、手柄 X 或触屏「踢击」,由权威端在车身左右各 120°、2.4 m 内选择最近可击中目标的一侧;同距离按目标 ID 固定选择。只有选中侧受击,正前、正后、隔着路障或高度不符的目标不会被范围误伤。没有目标时交替空踢;HUD 显示实际左右侧别,双方模型使用同一权威腿部动画。伤害保持 70,冷却保持 0.7 秒,不消耗弹药。
命中先沿原目标到远离车身的方向扫掠最多 1.1 m,考虑僵尸半径、路障和本地道路边界,再结算伤害和击杀。现有 50/70 血僵尸仍会被一脚击倒;倒地反馈在权威命中点与受阻落点之间短暂插值,不用假位移代替碰撞结算。司机视角只有轻微悬挂反馈,不改变速度、车向或射手瞄准;减少动态效果时跳过这段视觉位移。
新增可选输入 kickId、玩家 lastKickId 与事件 kickSide / kickId,由 Steam、开发 WebSocket 和云端共用的解析器验证;输入不接受目标、侧别、伤害或落点。新输入按动作 ID 去重,冷却中的新动作被消费,不在冷却结束后补踢;旧布尔输入只接受松开后再次按下,持续为 true 不会自动连踢。可选字段保留旧快照读取和旧输入兼容,未更改 transport 版本;旧客户端无法显示新的精确左右侧别,旧权威仍运行旧范围规则。完整新行为要求双方和权威均更新,云端还需部署新版规则包。
以下增强与验证发生在旧双人固定路线版本,仍需两名已准备玩家;不描述当前统一城市玩法。旧版未增加单人 AI 后座或自由城市骑行战斗。已有冲锋枪、霰弹枪、鸡毛掸子、煤气罐和弹弓保持原样。
开发试玩
运行 pnpm dev,两个独立浏览器会话打开同一开发服务器的 /coop-legacy.html。一个创建房间,另一个填写房间号。此时界面明确标注「开发联机测试」;客户端通过真正的 WebSocket 连接到 Vite 的 Node 规则端,模拟不依赖任何浏览器的渲染循环。
开发 WebSocket 只在开发服务器加载,不随生产 Web / Electron 发布。正式网页使用 Play 云端权威端,开发中可通过 ?transport=cloud 验证同一适配器。云端合作需按 发布顺序 部署并开启服务端合作开关。Steam App ID 默认仍为 0,正式好友联机必须使用同一正式 App ID、两个有测试权限的账号/设备和在线 Steam。模拟测试、开发双浏览器和离线 Electron 检查不代表真实云端或 Steam 中继/NAT 已通过。
实现与验证边界
packages/dayun-kicker-coop:无 DOM / Three / Steam 依赖的规则、输入与快照校验;60 Hz 固定步进、32 活动僵尸、64 历史事件上限。500 ms 未收到有效输入自动释放持续操作,保留瞄准与武器;10 秒心跳失联由 transport 结束房间。steamCoopRoom.ts:独立合作协议dayun-kicker:sockets-coop:v2,Sockets 虚拟端口 1;既有竞速使用端口 0。v2 的配送事件与终局原因不兼容旧 v1,加入房间和接收数据包时均校验协议,防止旧客户端卡在配送结算。20 Hz 快照、可靠准备/换座/终局、成员身份与权限、输入和射击序号去重,以及同 tick 控制 revision 防回滚。src/coop:共享ScooterRig、后座模型与 UI 组件;司机最多 150 ms 本地视觉预测并平滑校正,远端插值;射手即时本地反馈,命中和击退等待权威确认。瞄准限左右 85°/上下 40°,真实枪口射线检查路障遮挡,无友军伤害。- 射击携带最近收到的权威
observedTick,最多查询过去 15 tick / 250 ms 的车体与敌人历史。未来或过旧 tick 回当前状态,非法 tick 拒绝;当下弹药、冷却和活目标 ID 仍决定能否命中。换座、重新开局和结算清历史。 - 本轮
pnpm test:coop的 28 个文件、256 项单测通过。默认广州约 5.4 公里全程规则测试经过三处补给,领取一个可选箱并略过其余五箱,最终停稳 2 秒送达;共享预测与权威车辆位置逐步一致。配送判定、驶过不领取、库存上限、重开清理与旧快照兼容均有覆盖。 - 13 项 Chromium Metal GPU E2E 通过,覆盖原双座操作、教程、暂停菜单、WebGL 恢复及配送;新增配送测试用服务端持有的短路线,两个真实浏览器仅发送普通驾驶输入,验证资源单次领取、接受输入后忽略伪造领取字段、连续停稳 2 秒交付及两端一致结算。320/390 px 两个座位无溢出、触控目标至少 44 px,按钮保持单行。
pnpm test:e2e:coop:delivery冷启动两个独立规则端,2 项测试均通过。 - 云端规则门禁的全 SDK 30 项、Play 相关 19 项测试通过:缺失或不匹配的版本不能创建/加入房间,合法收件人确认版本前不能接收快照或操作。Play 本地规则 tarball、锁文件及容器上下文已同步;本轮未远端发布。
- 模拟 Steam 网络覆盖 RTT 0 / 80 / 150 / 250 ms 与丢包 0 / 2% / 5% 的 12 场矩阵,包含乱序和射击重传。150 ms RTT 的快照年龄 P50 100 ms、P95 150 ms、最大 200 ms;本测试不测真实 Steam 路由或驾驶视觉校正误差。
- 广州 GPU 合成压力包含 32 活动僵尸、16 倒地模型、满射击/命中特效与双座车。广州塔/医院/腾讯大厦分别为 111/114/112 calls、31,162/31,942/29,294 triangles;本机 Chromium GPU P95 不超过 16.80 ms,低于既定 300 calls/90k triangles 预算。此为本机结果,目标设备真实对局仍需验收。
- 射击输入当场触发本地画面、声音与网络发送,连续射击按武器冷却独立调度;键盘/手柄瞄准跟随渲染帧,网络维持 20 Hz。同一模拟时钟的五个相位,首次本地回调等待由 1–49 ms 降至 0 ms。实际双浏览器首发到下一绘制 19.30 ms、连续开火最后一次 4.30 ms,36 帧均值 16.27 ms/P95 16.70 ms;这些度量不代表真实 Steam 命中延迟或所有设备帧率。
- 全仓类型检查、Web/桌面构建、网页/桌面性能预算和发布产物校验通过。完整广州地图与战斗闭包均计入:合作入口 301.6 KiB gzip/350 KiB、CSS 4.4 KiB/16 KiB、Web 完整资源 13.48 MiB/20 MiB;发布 449 文件。旧直线路线测试布景独立加载,预算没有提高。
- 生产 Electron 合作入口 E2E 1/1 通过,涵盖当时的
dayun://game/coop.html(现为/coop-legacy.html兼容入口)、40 px 标题栏、可信 Steam 资料桥、App ID 0 离线提示、离线保存名称、无开发入口及返回原单座首页。
运行 pnpm test:coop 与 pnpm test:e2e:coop;配送与资源验收使用 pnpm test:e2e:coop:delivery,自动启动默认广州规则端与短路线测试规则端,测试地图由服务端选定,客户端仅提交普通驾驶输入。macOS 原生 GPU 使用 pnpm exec playwright test --config playwright.gpu.config.ts e2e/coop.spec.ts e2e/coop-profile.spec.ts。另按仓库要求运行类型检查、Web / 桌面构建、性能预算与发布产物校验。正式 Steam 邀请、不同网络、后台、关闭窗口、断网以及 Windows / Steam Deck 试玩留在真实平台验收清单。