Files
Genarrative/docs/technical/BARK_BATTLE_2D_RUNTIME_TECHNICAL_PLAN_2026-05-11.md

25 KiB
Raw Permalink Blame History

bark-battle 2D Runtime 前端技术方案2026-05-11

1. 背景与目标

本方案基于 .hermes/plans/2026-05-11_144229-bark-battle-2d-game-bdd-ddd-tdd-plan.md,为“汪汪声浪大作战 / bark-battle”细化前端与浏览器游戏 runtime 的技术实现路线。

本任务只产出技术方案,不直接实现代码。后续编码应以本文作为 runtime 层设计约束,再按 TDD 小步落地。

1.1 玩法定位

bark-battle 是一个声控拔河式 2D 浏览器小游戏:玩家对麦克风发出狗叫声,系统根据音量峰值、有效叫声次数和节奏计算本方声浪推动力,在限时 30 秒内推动顶部红蓝能量条,时间结束后按能量条偏向判定胜负。

1.2 本文范围

本文覆盖:

  • Phaser + TypeScript + Vite 栈选择
  • simulation / render / HUD 边界
  • 建议目录结构
  • 核心 domain 类型
  • Web Audio 输入适配
  • Phaser Scene 切分
  • DOM HUD 设计
  • 移动端与权限降级
  • 测试与验证命令

本文不覆盖:

  • 后端表结构、持久化成绩、作品发布、广场接入
  • 实时多人对战协议
  • 复杂 AI 狗叫识别模型
  • 美术素材正式生产流程

2. 技术栈选择

2.1 推荐栈

Runtime Renderer: Phaser 3
Language: TypeScript
Build: Vite
Host UI: React / DOM overlay
Audio Input: Web Audio API + MediaDevices.getUserMedia
Test: Vitest + Testing Library + 浏览器 smoke / Playwright 可选

2.2 选择理由

  1. 玩法是横版 2D 舞台,核心表现是狗狗 sprite、声浪、拟声词、粒子、屏幕震动与能量条反馈Phaser 对 2D 渲染、时间循环、sprite animation、camera、粒子和 Scene 生命周期支持成熟。
  2. 当前项目主前端已使用 TypeScript + Vite继续复用现有构建和测试体系避免引入独立构建链。
  3. 文字密集、权限提示、结算、设置和移动端响应式布局适合 DOM HUDCanvas 保持负责 playfield 和动态特效。
  4. Web Audio API 可以在浏览器端完成 MVP 所需音量采样、RMS/peak 计算、环境噪音校准和输入归一化,不需要首版接入后端音频处理。

2.3 不选择其它路线的原因

  • 不使用 Three.js / 3D当前玩法画面是 2D 横版舞台,不需要 3D 相机、模型和材质管线。
  • 不把 HUD 全部塞入 Phaser Canvas权限说明、重试、结算、移动端布局和可访问性更适合 DOM。
  • 不在前端实现正式业务真相:浏览器 runtime 可承载单局即时 simulation但若后续涉及成绩、作品、排行榜、发布和奖励必须交给后端投影/API 裁决。

3. 总体架构

3.1 分层总览

React Runtime Shell
  ├─ DOM HUD / Panels
  │   ├─ PermissionPanel
  │   ├─ TopEnergyBar
  │   ├─ TimerChip
  │   └─ ResultPanel
  │
  ├─ Application Controller
  │   ├─ permission / calibration orchestration
  │   ├─ simulation tick
  │   ├─ audio sample submission
  │   └─ snapshot publish
  │
  ├─ Pure Domain / Simulation
  │   ├─ BarkBattleSession
  │   ├─ BarkDetector
  │   ├─ EnergyTugOfWar
  │   ├─ BarkBattleScoring
  │   └─ OpponentStrategy
  │
  ├─ Infrastructure Adapters
  │   ├─ BrowserMicrophoneInput
  │   ├─ AudioAnalyserSampler
  │   └─ PhaserGameHost
  │
  └─ Phaser Renderer
      ├─ BootScene
      ├─ PreloadScene
      ├─ BattleScene
      ├─ FxScene / DebugScene可选
      └─ Asset manifest

3.2 强制边界

  1. domain/ 不依赖 Phaser、Web Audio、DOM、React、浏览器全局对象或后端 API。
  2. domain/ 只接收 plain data例如时间增量、归一化音量样本、对手 power、配置参数。
  3. application/ 负责编排权限、校准、音频输入、AI 对手、tick 和 snapshot 分发。
  4. Phaser Scene 只消费 BarkBattleSnapshot,把 snapshot 映射成 sprite、动画、粒子、camera 和 sound effect不得持有核心胜负、计数和能量条规则。
  5. DOM HUD 只消费 snapshot 和少量 runtime UI 状态,负责展示、按钮和弹层;不得重复实现核心胜负规则。
  6. 若后续接入平台作品/成绩/排行榜,前端只调用后端 API 和展示投影,不在本地绕过后端生成正式结论。

4. 建议目录结构

首版建议以独立 runtime 原型落在 src/games/bark-battle/,避免提前侵入平台创作链路。

src/games/bark-battle/
  domain/
    BarkBattleTypes.ts
    BarkBattleSession.ts
    BarkDetector.ts
    EnergyTugOfWar.ts
    BarkBattleScoring.ts
    OpponentStrategy.ts
    __tests__/
      BarkDetector.test.ts
      EnergyTugOfWar.test.ts
      BarkBattleSession.test.ts
      BarkBattleScoring.test.ts

  application/
    BarkBattleController.ts
    BarkBattleConfig.ts
    BarkBattleSnapshotStore.ts
    __tests__/
      BarkBattleController.test.ts

  infrastructure/
    BrowserMicrophoneInput.ts
    AudioAnalyserSampler.ts
    MicrophonePermission.ts
    __tests__/
      BrowserMicrophoneInput.test.ts
      AudioAnalyserSampler.test.ts

  phaser/
    BarkBattleGameHost.ts
    scenes/
      BarkBattleBootScene.ts
      BarkBattlePreloadScene.ts
      BarkBattleScene.ts
      BarkBattleFxScene.ts
    assets/
      barkBattleAssetManifest.ts

  ui/
    BarkBattleRuntimeShell.tsx
    BarkBattleHud.tsx
    BarkBattlePermissionPanel.tsx
    BarkBattleResultPanel.tsx
    BarkBattleMobileControls.tsx
    BarkBattleHud.css
    __tests__/
      BarkBattleHud.test.tsx
      BarkBattlePermissionPanel.test.tsx
      BarkBattleResultPanel.test.tsx

若后续进入 Genarrative 正式玩法类型闭环,再按 genarrative-play-type-integration 扩展到:

src/components/bark-battle-runtime/BarkBattleRuntimeShell.tsx
src/components/bark-battle-result/BarkBattleResultView.tsx
src/services/barkBattleRuntimeClient.ts
packages/shared/src/contracts/barkBattle.ts
server-rs/crates/shared-contracts/src/bark_battle.rs

首版不建议直接新增后端表或正式作品链路,除非产品明确要求成绩、发布和广场能力。

5. 核心 Domain 类型

5.1 基础枚举与数值约定

export type BarkBattlePhase =
  | 'permission'
  | 'calibration'
  | 'countdown'
  | 'playing'
  | 'finished'
  | 'unavailable'

export type BarkBattleSide = 'player' | 'opponent'

export type BarkBattleWinner = BarkBattleSide | 'draw' | null

export type BarkBattleDifficulty = 'easy' | 'normal' | 'hard'

export type BarkBattleUiState =
  | 'idle'
  | 'permission-ready'
  | 'microphone-authorized'
  | 'calibrating'
  | 'ready-countdown'
  | 'playing'
  | 'finished'
  | 'microphone-unavailable'

export type MicrophoneFailureReason =
  | 'unsupported'
  | 'permission-denied'
  | 'non-secure-context'
  | 'not-found'
  | 'not-readable'
  | 'audio-context-blocked'
  | 'calibration-timeout'
  | 'calibration-sample-unreadable'
  | 'unknown'

关键数值:

  • energy: -100..100,正数偏玩家侧,负数偏对手侧。
  • currentVolume: 0..1,音频采样归一化后的瞬时音量。
  • recentPeak: 0..1,短窗口内峰值。
  • power: 0..10..100 二选一,建议 domain 内统一 0..1HUD 显示再转百分比。
  • remainingMs: 单局剩余毫秒。

5.2 Snapshot

export type BarkBattleSnapshot = {
  phase: BarkBattlePhase
  uiState: BarkBattleUiState
  errorReason: MicrophoneFailureReason | null
  statusMessageKey: BarkBattleStatusMessageKey | null
  elapsedMs: number
  remainingMs: number
  countdownMs: number
  energy: number
  player: BarkSideState
  opponent: BarkSideState
  winner: BarkBattleWinner
  result: BarkBattleResult | null
  lastEvents: BarkBattleVisualEvent[]
}

export type BarkSideState = {
  side: BarkBattleSide
  barkCount: number
  currentVolume: number
  recentPeak: number
  combo: number
  power: number
  isBarking: boolean
  lastBarkAtMs: number | null
  maxVolume: number
}

export type BarkBattleResult = {
  winner: BarkBattleWinner
  finalEnergy: number
  playerBarkCount: number
  playerMaxVolume: number
  playerAveragePower: number
  score: number
}

export type BarkBattleStatusMessageKey =
  | 'microphone-unsupported'
  | 'microphone-permission-denied'
  | 'microphone-non-secure-context'
  | 'microphone-not-found'
  | 'microphone-not-readable'
  | 'microphone-audio-context-blocked'
  | 'microphone-calibration-timeout'
  | 'microphone-calibration-sample-unreadable'
  | 'microphone-unknown-error'

5.3 输入样本与叫声事件

export type BarkAudioSample = {
  atMs: number
  volume: number
  peak: number
  rms: number
}

export type BarkDetectedEvent = {
  id: string
  atMs: number
  side: BarkBattleSide
  volume: number
  strength: number
  combo: number
}

5.4 视觉事件

视觉事件由 domain 或 application 生成,但只包含 plain data不包含 Phaser 对象:

export type BarkBattleVisualEvent =
  | {
      type: 'bark-word'
      id: string
      side: BarkBattleSide
      atMs: number
      strength: number
      text: 'BARK' | 'WOOF' | 'WAN' | 'WANGOOF'
    }
  | {
      type: 'shockwave'
      id: string
      side: BarkBattleSide
      atMs: number
      strength: number
    }
  | {
      type: 'combo-burst'
      id: string
      side: BarkBattleSide
      atMs: number
      combo: number
    }

Phaser 只根据这些事件播放一次性特效,并维护已消费事件 ID避免重复播放。

6. Domain 模块职责

6.1 BarkDetector

职责:把连续音频样本转换为有效叫声事件。

输入:

  • BarkAudioSample
  • 校准后的 ambientNoiseFloor
  • barkThreshold
  • minBarkGapMs
  • minBarkDurationMs
  • maxBarkDurationMs

规则建议:

  1. 音量超过动态阈值进入 candidate 状态。
  2. 峰值回落到阈值以下或持续时长达到上限时结束 candidate。
  3. candidate 持续时间在 80ms..1200ms 且与上一次有效叫声间隔足够时,记为一次叫声。
  4. 长时间持续噪音不应无限计数,只能按冷却和峰值回落形成新事件。
  5. MVP 不要求识别“是否真狗叫”,先基于音量峰值、时长和间隔判断。

6.2 EnergyTugOfWar

职责:更新红蓝拉锯条。

建议公式:

playerPower = volumeScore * 0.65 + barkRateScore * 0.35 + comboBonus
opponentPower = opponentStrategy.tick(...)
energyDelta = (playerPower - opponentPower) * deltaSeconds * balanceFactor
energy = clamp(energy + energyDelta, -100, 100)

约束:

  • EnergyTugOfWar 不知道玩家来自麦克风还是 mock input。
  • EnergyTugOfWar 不知道 Phaser 能量条宽度。
  • 平衡参数集中在 BarkBattleConfig,不要散落在 Scene 或 HUD 中。

6.3 BarkBattleSession

职责:管理局内 phase、计时、胜负和 snapshot。

状态机建议:

permission → calibration → countdown → playing → finished
        ↘ unavailable

phase 只表达 runtime 是否可继续参与局内流程;所有麦克风不可用、权限失败、非安全上下文和校准失败都统一收敛到 phase: 'unavailable',再通过 uiState: 'microphone-unavailable'errorReason 区分 HUD 展示和重试策略,避免把基础设施错误枚举直接扩散成 domain 阶段。

关键规则:

  • countdown 结束才进入 playing
  • playingremainingMs 随 tick 递减。
  • remainingMs <= 0 后进入 finished
  • energy > drawThreshold 判定玩家胜利。
  • energy < -drawThreshold 判定对手胜利。
  • abs(energy) <= drawThreshold 判定平局。

6.4 OpponentStrategy

职责:为单机 MVP 提供对手推动力。

export interface OpponentStrategy {
  tick(input: OpponentTickInput): OpponentTickOutput
}

普通难度建议:

  • 周期性小叫声提供基础压力。
  • 每 3~6 秒一次短爆发。
  • 玩家大幅领先时可轻微增强,但不能追到不可赢。

7. Web Audio 输入适配

7.1 BrowserMicrophoneInput

职责:封装浏览器麦克风权限与音频流生命周期。

建议 API

export interface MicrophoneInputPort {
  isSupported(): boolean
  requestPermission(): Promise<MicrophoneSession>
  stop(): void
}

export interface MicrophoneSession {
  sample(atMs: number): BarkAudioSample
  stop(): void
}

实现要点:

  1. 使用 navigator.mediaDevices?.getUserMedia({ audio: true })
  2. 在用户点击“开始”后创建或 resume AudioContext,避免移动端自动播放策略拦截。
  3. 使用 AnalyserNode 读取时域数据,计算 RMS 与 peak。
  4. 输出归一化样本,不把 MediaStreamAudioContextAnalyserNode 泄漏到 domain。
  5. 退出、重开、页面卸载时停止 track避免麦克风占用残留。

7.2 校准流程

calibration 阶段建议持续 800ms..1500ms

  1. 收集静默环境样本。
  2. 计算 ambientNoiseFloor,例如 p75 或均值 + 标准差。
  3. 设置动态阈值:
barkThreshold = clamp(ambientNoiseFloor + 0.12, 0.18, 0.55)
  1. 若环境噪音过高HUD 给出简短提示和“继续 / 重新校准”入口,但不要把长说明常驻在画面上。

7.3 权限与错误分类

export type MicrophoneFailureReason =
  | 'unsupported'
  | 'permission-denied'
  | 'non-secure-context'
  | 'not-found'
  | 'not-readable'
  | 'audio-context-blocked'
  | 'calibration-timeout'
  | 'calibration-sample-unreadable'
  | 'unknown'

错误来源与分层归属:

失败原因 主要检测位置 controller snapshot 表达 HUD 可区分状态
浏览器无 mediaDevices.getUserMedia BrowserMicrophoneInput.isSupported() phase: 'unavailable', uiState: 'microphone-unavailable', errorReason: 'unsupported' 设备或浏览器不支持麦克风输入,只提供返回入口,不展示可开始声控按钮
非安全上下文 BrowserMicrophoneInput.isSupported()MicrophonePermission 预检 window.isSecureContext phase: 'unavailable', errorReason: 'non-secure-context' 当前环境无法使用麦克风,提示使用受支持的安全环境或返回
用户拒绝授权 BrowserMicrophoneInput.requestPermission() 捕获 NotAllowedError / SecurityError phase: 'unavailable', errorReason: 'permission-denied' 提供重新授权或返回入口,不进入 calibration/countdown/playing
未检测到设备 getUserMedia 捕获 NotFoundError / DevicesNotFoundError phase: 'unavailable', errorReason: 'not-found' 展示麦克风不可用,可重试授权或返回
设备被占用或不可读 getUserMedia 捕获 NotReadableError / TrackStartError phase: 'unavailable', errorReason: 'not-readable' 展示麦克风不可用,可重试授权或返回
AudioContext 被移动端策略拦截 用户手势后创建 / resume AudioContext 失败 phase: 'unavailable', errorReason: 'audio-context-blocked' 提示点击重试,不自动循环请求
校准超时 BarkBattleController 在 calibration 阶段等待样本超出 calibrationMaxWaitMs phase: 'unavailable', errorReason: 'calibration-timeout' 展示麦克风输入不可用,提供重试校准入口
校准样本不可读 AudioAnalyserSampler.sample() 持续返回空样本、NaN 或无法读取 buffer phase: 'unavailable', errorReason: 'calibration-sample-unreadable' 展示麦克风输入不可用,提供重试校准入口

前端只根据错误分类展示可操作状态:重试授权、重试校准、返回、或使用调试备用输入。不要把浏览器原始错误堆栈展示给玩家。

8. Phaser Scene 切分

8.1 BarkBattleBootScene

职责:

  • 初始化 Phaser 全局配置。
  • 注册 scale、background color、全局事件桥。
  • 不加载重资源,不处理玩法规则。

8.2 BarkBattlePreloadScene

职责:

  • 根据 barkBattleAssetManifest 加载背景、狗狗 sprite、声浪 FX、拟声词 bitmap / atlas、轻量音效。
  • 使用稳定 manifest key不在 gameplay 代码中散写文件路径。
  • 加载完成后进入 BarkBattleScene

8.3 BarkBattleScene

职责:

  • 创建横版舞台、左右狗狗、背景层、声浪层、拟声词层。
  • 每帧读取最新 BarkBattleSnapshot
  • 根据 snapshot 更新:
    • 狗狗 idle / bark / win / lose 动画
    • 声浪强度
    • camera shake
    • transient bark words
    • shockwave
  • 把可选的调试输入 action 传给 controller但不处理麦克风和规则。

不得在 Scene 中实现:

  • 叫声计数
  • 胜负判定
  • 能量条规则
  • 权限流程
  • 结算数据计算

8.4 BarkBattleFxScene可选

如果特效复杂,可拆出叠加 Scene

  • 专门处理拟声词、粒子、冲击波和 camera shake。
  • 通过视觉事件 ID 去重。
  • prefers-reduced-motion 或低端设备降级。

首版也可以先把 FX 保持在 BarkBattleScene 内,但必须仍然只消费 snapshot / visual events。

9. DOM HUD 设计

9.1 HUD 层级

DOM HUD 建议覆盖在 Phaser Canvas 上方:

BarkBattleRuntimeShell
  ├─ <div className="bark-battle-canvas-host" />
  └─ <BarkBattleHud snapshot={snapshot} uiState={uiState} />

HUD 分区:

  • 顶部:红蓝声浪能量条 + 小型剩余时间。
  • 中央:仅在倒计时、关键提示或结算时短暂展示大号状态。
  • 左右边缘:双方简洁状态,例如叫声次数 / combo chip。
  • 底部角落:麦克风状态、重试、小菜单。
  • 结算:独立居中面板,显示胜负、叫声次数、最大音量、评分、再来一局、返回。

9.2 Playfield 保护

遵循 game UI 约束:

  1. 正常 playing 阶段保持中心和下中部 playfield 清爽,不常驻长文案。
  2. 不把规则说明、长控制说明、多段提示默认铺在画面上。
  3. 权限、设置、结算使用独立面板或弹层,不在当前面板下面展开一大块内容。
  4. 移动端优先保证顶部能量条、倒计时、狗狗和重试入口可见可点。
  5. 大动效不能遮挡顶部能量条和倒计时。

9.3 CSS 设计建议

  • 使用局部 CSS class 或 CSS module避免污染全站。
  • 使用 CSS 变量定义主题:
    • --bark-player-color
    • --bark-opponent-color
    • --bark-panel-bg
    • --bark-safe-bottom
  • 使用 dvh / svh 和 safe-area inset 处理移动端地址栏与刘海。
  • pointer-events 分层HUD 容器默认 pointer-events: none,按钮和面板恢复 pointer-events: auto

10. 移动端与权限降级

10.1 移动端输入约束

移动端浏览器通常要求用户手势才能启动 AudioContext。开局流程必须是

玩家点击“开始” → requestPermission → 创建/恢复 AudioContext → calibration → countdown → playing

不要在页面加载时自动请求或自动启动 AudioContext。

10.2 响应式布局

移动端建议:

  • 横屏优先呈现完整舞台;竖屏可保持舞台居中并缩小 HUD。
  • 顶部能量条高度保持可读,但不要占满大面积。
  • 结算面板宽度使用 min(92vw, 420px)
  • 底部按钮避开 env(safe-area-inset-bottom)
  • 非关键设置折叠进小菜单。

10.3 权限失败降级

权限失败时:

  • unsupported:展示“当前浏览器不支持麦克风输入”,提供返回入口,不展示开始声控按钮。
  • non-secure-context:展示“当前环境无法使用麦克风”,提示切换到受支持的安全环境或返回。
  • permission-denied:展示简短说明和“重新授权”入口。
  • not-found:提示未检测到麦克风,提供重试授权或返回入口。
  • not-readable:提示麦克风被占用或暂时不可读,提供重试授权或返回入口。
  • audio-context-blocked:提示点击重试。
  • calibration-timeout / calibration-sample-unreadable:提示麦克风输入不可用,提供“重新校准”和返回入口。

可选开发调试降级:

  • 本地 dev 可启用键盘 mock input例如按住空格模拟音量峰值。
  • mock input 必须标记为开发/调试能力,不作为正式竞技能力。

11. 测试策略

11.1 Domain 单元测试(优先)

目标:不接 Phaser、不接 DOM、不接 Web Audio。

建议测试:

  • BarkDetector:超过阈值且间隔足够时计为一次有效叫声。
  • BarkDetector:持续噪音不会无限计数。
  • BarkDetector:低于环境噪音阈值不计入叫声。
  • EnergyTugOfWar:玩家 power 高于对手时 energy 向玩家侧移动。
  • EnergyTugOfWarenergy clamp 在 -100..100
  • BarkBattleSession:倒计时结束进入 playing。
  • BarkBattleSession:剩余时间归零进入 finished。
  • BarkBattleSession:按 energy 和 drawThreshold 判定胜 / 负 / 平。

11.2 Application 测试

目标验证输入样本、AI、tick 和 snapshot 编排。

建议测试:

  • 权限允许后进入校准,再进入倒计时。
  • 权限拒绝后 phaseunavailableerrorReasonpermission-denied,不进入 playing。
  • 非安全上下文、设备未找到、设备不可读、AudioContext 被拦截时controller snapshot 都进入 phase: 'unavailable',并保留可供 HUD 区分的 errorReason
  • 校准超时或样本持续不可读时controller snapshot 使用 errorReason: 'calibration-timeout'calibration-sample-unreadable,并提供重试校准动作。
  • 提交 mock audio sample 后 snapshot 中玩家状态更新。
  • AI 对手 power 参与能量条拉锯。
  • lastEvents 只发布新增视觉事件。

11.3 HUD 组件测试

目标:验证 snapshot 到 DOM 的展示映射。

建议测试:

  • playing 阶段展示倒计时和能量条。
  • energy 正负值映射到玩家 / 对手侧比例。
  • errorReason: 'permission-denied' 展示重试授权入口。
  • errorReason: 'unsupported' 展示返回入口且不展示开始声控按钮。
  • not-foundnot-readablenon-secure-contextaudio-context-blockedcalibration-timeoutcalibration-sample-unreadable 分别映射到可区分的简短状态文案和对应操作。
  • finished 展示胜负、叫声次数和再来一局。
  • 移动端 class / 结构不依赖 Phaser Canvas 才能渲染。

11.4 Phaser 集成与 smoke

自动化层面不强行在 Vitest 中完整启动 Phaser。建议

  • 用 adapter mock 测试 Scene 消费 snapshot 的纯映射函数。
  • 浏览器 smoke 验证真实 Canvas、Web Audio 和动画。
  • 若后续引入 Playwright再做最小视觉和交互 smoke。

12. 验证命令建议

文档阶段只需要编码检查和 diff 检查:

npm run check:encoding
git diff -- docs/prd/BARK_BATTLE_BDD_2026-05-11.md docs/technical/BARK_BATTLE_2D_RUNTIME_TECHNICAL_PLAN_2026-05-11.md

后续实现 domain 后建议:

npm run test -- --run src/games/bark-battle/domain/**/*.test.ts
npm run typecheck
npm run check:encoding

后续实现 infrastructure/application 错误状态后建议:

npm run test -- --run src/games/bark-battle/infrastructure/__tests__/BrowserMicrophoneInput.test.ts src/games/bark-battle/application/__tests__/BarkBattleController.test.ts
npm run typecheck
npm run check:encoding

后续实现 HUD 后建议:

npm run test -- --run src/games/bark-battle/ui/**/*.test.tsx
npm run lint:eslint
npm run typecheck
npm run check:encoding

后续接入浏览器 runtime 后建议:

npm run dev:web
# 人工 smoke授权麦克风 → 校准 → 发声 → 能量条变化 → 结算 → 再来一局

若未来接入 Genarrative 正式玩法类型、后端持久化或发布链路再追加对应契约、api-server 和 SpacetimeDB 验证;首版 runtime 原型不应提前新增这些命令作为门槛。

13. 后续落地顺序

建议后续实现按以下顺序推进:

  1. 先建 domain/ 和纯单元测试。
  2. 实现 BarkDetectorEnergyTugOfWarBarkBattleSession 的最小规则。
  3. application/ controller用 mock audio sample 跑通 snapshot。
  4. 实现 DOM HUD 的 permission / energy / timer / result 展示。
  5. 接入 BrowserMicrophoneInput 和校准流程。
  6. 接入 Phaser host、Scene 和 asset manifest占位素材先跑通视觉反馈。
  7. 做移动端视口和权限失败 smoke。
  8. 产品确认后再决定是否进入正式玩法类型、作品发布和后端真相链。

14. 关键技术决策

  1. 默认采用 Phaser 3 + TypeScript + Vite符合 2D 浏览器游戏默认路线。
  2. 核心 simulation 放在纯 TypeScript domain严格不依赖 Phaser / Web Audio / DOM。
  3. Web Audio 只作为输入 adapter输出归一化 BarkAudioSample
  4. Phaser Scene 是 renderer只消费 snapshot 和 visual events不承载规则真相。
  5. HUD 使用 DOM overlay承载权限、能量条、倒计时、结算和移动端响应式布局。
  6. MVP 不做复杂狗叫语义识别,先用音量峰值、持续时长、冷却和环境噪音校准。
  7. MVP 建议玩家 vs AI 单机 runtime正式成绩、排行榜、发布和奖励后续再交给后端链路。