48 KiB
48 KiB
家庭游戏中心 (Family Game Center) - 产品需求文档 (PRD)
文档版本: 1.0
最后更新: 2024-12-29
状态: 已审批,进入开发阶段
目标平台: Linux (Ubuntu 22.04 LTS) / 开发平台: Windows 11
📋 文档目录
项目概述
1.1 项目背景
为父母(年龄 50+ 岁)设计的易用家庭游戏平台。该平台旨在提供一个完全锁定的游戏环境,使非技术用户无法误操作进入系统,只能使用游戏功能。
1.2 项目目标
| 目标 | 说明 |
|---|---|
| 易用性 | 父母无计算机知识也能独立使用 |
| 安全性 | 系统级锁定,用户无法退出或破坏系统 |
| 稳定性 | 无中断运行 48+ 小时 |
| 游戏性 | AI 对手难度可调,赢率 40-60% |
| 维护性 | 单人可维护和更新 |
1.3 产品范围
包含:
- 4 款主要游戏 (斗地主、拖拉机、卡五星、赛车)
- Linux Kiosk 启动器
- 响应式菜单 UI
- 本地 AI 对手
- 游戏存档管理
- 基础统计功能
不包含:
- 网络在线多人对战
- 云同步和备份
- 用户账户系统
- 第三方集成
- 语音/视频功能
1.4 项目成功标准
✓ MVP 版本 (斗地主 + 菜单): 6-8 周
✓ 完整版 (全部游戏): 12-18 周
✓ 总成本: 硬件 5-15K,软件 0 元
✓ 团队规模: 1-3 人
✓ 风险等级: 低-中等
功能需求
2.1 用户故事
故事 1: 启动和菜单
作为 父亲
我想 开机后直接进入游戏菜单
这样 我无需学习系统操作,只需选择想玩的游戏
验收标准:
- 系统启动后 5 秒内进入菜单
- 菜单显示 4 个游戏卡片
- 卡片字体 32px+,易于阅读
- 按钮 80px+,易于点击
- 菜单背景颜色高对比度 (WCAG AAA)
故事 2: 启动游戏
作为 母亲
我想 点击游戏卡片启动游戏
这样 我可以开始游玩
验收标准:
- 点击卡片 3 秒内启动游戏
- 游戏加载时显示进度提示
- 游戏结束自动返回菜单
- 支持按 ESC 返回菜单
故事 3: 无法退出
作为 家人
我想 用户无法通过快捷键或菜单退出游戏
这样 游戏不会被误关闭或系统被破坏
验收标准:
- Alt+F4 无效
- Alt+Tab 无效
- Windows 键无效
- 右键菜单禁用
- 任务栏隐藏
- TTY 无法切换 (Ctrl+Alt+Fx)
故事 4: AI 对手
作为 玩家
我想 和智能电脑对手对战
这样 我可以随时游戏,不需要其他玩家
验收标准:
- 所有游戏都有 2-4 个 AI 对手
- AI 难度可调 (简单/中等/困难)
- AI 出牌合法且有策略性
- AI 响应时间 1-2 秒
故事 5: 保存进度
作为 玩家
我想 每局游戏的成绩和统计被自动保存
这样 我可以看到我的进度和胜率
验收标准:
- 游戏结束后自动保存成绩
- 显示历史成绩和统计数据
- 统计包括: 总局数、胜率、平均用时
- 数据持久存储在本地数据库
2.2 功能清单
2.2.1 菜单系统 (Priority: P0 - 必须)
| 功能 | 说明 | 优先级 |
|---|---|---|
| 游戏列表展示 | 网格布局显示 4 个游戏卡片 | P0 |
| 游戏卡片 | 包含海报、名称、简描 | P0 |
| 启动游戏 | 点击卡片启动对应游戏 | P0 |
| 时间显示 | 菜单顶部显示当前时间 | P1 |
| 返回菜单 | 游戏结束自动返回,支持 ESC 返回 | P0 |
| 响应式布局 | 适配 1080p 到 4K 分辨率, 以2K分辨率 150%缩放作为基准开发 | P0 |
| 高对比度 | WCAG AAA 标准配色 | P0 |
| 父母友好 | 大字体、大按钮 | P0 |
2.2.2 斗地主游戏 (Priority: P0 - 必须)
| 功能 | 说明 | 优先级 |
|---|---|---|
| 基础规则 | 单张、对、顺、炸、王炸判定 | P0 |
| 手牌显示 | 玩家手牌按大小排序显示 | P0 |
| 出牌交互 | 鼠标/触屏选牌并出牌 | P0 |
| 地主判定 | 系统自动判定地主 | P0 |
| AI 对手 | 2 个 AI 农民对手 | P0 |
| 游戏流程 | 发牌→抢地主→轮流出牌→结束 | P0 |
| 声音效果 | 出牌音效、胜负音效 | P1 |
| 游戏统计 | 保存胜负和用时 | P1 |
牌型规则:
- 单张: 任何一张牌
- 对牌: 同点数的两张牌
- 三张: 同点数的三张牌
- 顺子: 5 张及以上连续的牌 (2 不能在顺子中)
- 炸弹: 同点数的四张牌 (可以压任何牌)
- 王炸: 红黑王 (最大)
- 其他: 简化版,不实现飞机等复杂牌型
2.2.3 拖拉机游戏 (Priority: P1 - 高)
| 功能 | 说明 | 优先级 |
|---|---|---|
| 基础规则 | 四人跑牌,拖拉机机制 | P1 |
| 主牌概念 | 主花色、主点数 | P1 |
| AI 对手 | 3 个 AI 对手 | P1 |
| 游戏流程 | 发牌→选主→轮流出牌→计分→结束 | P1 |
2.2.4 卡五星麻将 (Priority: P2 - 可选)
| 功能 | 说明 | 优先级 |
|---|---|---|
| 基础规则 | 五人麻将变种 | P2 |
| AI 对手 | 4 个 AI 对手 | P2 |
| 胡牌类型 | 简化版,5-10 种常见胡牌 | P2 |
2.2.5 赛车游戏 (Priority: P1 - 高)
| 功能 | 说明 | 优先级 |
|---|---|---|
| 基础机制 | 加速、减速、转向、碰撞 | P1 |
| AI 对手 | 1 个 AI 赛手 | P1 |
| 赛道 | 简单环形赛道,3 圈 | P1 |
| 计时 | 完成赛道的用时和排名 | P1 |
2.2.6 系统功能 (Priority: P0 - 必须)
| 功能 | 说明 | 优先级 |
|---|---|---|
| 自动启动 | 系统启动后自动进入菜单 | P0 |
| 快捷键禁用 | 完全禁用系统快捷键 | P0 |
| 进程监控 | 游戏异常退出时自动返回菜单 | P0 |
| 数据持久化 | 游戏数据本地保存 | P0 |
| 日志记录 | 系统和游戏日志记录 | P1 |
| 故障恢复 | 自动修复数据损坏和异常状态 | P1 |
2.3 非功能需求
| 类别 | 需求 | 验收标准 |
|---|---|---|
| 性能 | 启动时间 | < 5 秒进入菜单 |
| 性能 | 菜单响应 | < 100ms UI 反馈 |
| 性能 | 内存占用** | < 500MB (菜单+游戏) |
| 性能 | 帧率 | 60fps 稳定 |
| 可靠性 | 运行时长 | 无中断 48+ 小时 |
| 可靠性 | 故障恢复 | 自动重启恢复 |
| 安全性 | 系统锁定 | 三层防护机制 |
| 兼容性 | 分辨率 | 1080p, 1440p, 2560p, 4K |
| 兼容性 | 输入 | 鼠标、触屏、手柄 |
| 可维护性 | 代码** | 注释完整,易扩展 |
| 可维护性 | 文档** | API、部署、故障排查 |
技术方案
3.1 技术栈选择
3.1.1 开发平台 (Windows 11)
IDE/编辑器: VS Code / JetBrains IDE
Golang: 1.20+ (Golang for Windows)
Node.js: 18+ LTS (Windows版)
Godot Engine: 4.1+ (Windows版)
Git: Git Bash or GitHub Desktop
包管理器: npm (Node.js), go mod (Golang)
开发优势:
- 所有开发工具都有完整的 Windows 支持
- Golang 和 Node.js 可在 Windows 上原生编译
- Godot 有完整的 Windows 编辑器
- 跨平台编译: 在 Windows 编译 Linux 二进制
3.1.2 部署平台 (Ubuntu 22.04 LTS)
操作系统: Ubuntu 22.04 LTS (Server 版本推荐)
系统配置: Kiosk 模式 + systemd + Openbox
启动器: Golang 二进制 + zserge/webview
菜单UI: Vue 3 构建输出
游戏: Godot 导出的 Linux 64-bit 二进制
运行时: 无需额外依赖 (所有编译为静态二进制)
部署优势:
- Fedora Silverblue 不可变文件系统,或 Ubuntu Server 最小化
- Kiosk 配置完全锁定系统
- 无强制系统更新
- 极低资源占用
3.2 架构设计
┌─────────────────────────────────────────┐
│ 用户交互层 (User Layer) │
│ 鼠标/触屏/键盘 │
└──────────────────┬──────────────────────┘
↓
┌─────────────────────────────────────────┐
│ 应用层 (Application Layer) │
│ │
│ ┌────────────────┐ ┌──────────────┐ │
│ │ Golang Launcher│ │ Process Mgr │ │
│ │ - Webview │ │ - Game spawn │ │
│ │ - 全屏模式 │ │ - 监控 │ │
│ └────────┬───────┘ └──────┬───────┘ │
│ │ │ │
│ ┌────────┴──────────────────┴────┐ │
│ │ HTTP Server (localhost:8888) │ │
│ └────────────────┬───────────────┘ │
│ │ │
│ ┌────────────────┴──────────────┐ │
│ │ Vue 3 前端菜单 │ │
│ │ - 游戏网格 │ │
│ │ - 响应式布局 │ │
│ └────────────────┬──────────────┘ │
└─────────────────────────────────────────┘
↓
┌─────────────────────────────────────────┐
│ 游戏层 (Game Layer) │
│ │
│ ┌──────────┐ ┌──────────┐ ┌────────┐ │
│ │Godot Game│ │Godot Game│ │Pixi.js │ │
│ │ 斗地主 │ │ 拖拉机 │ │ 赛车 │ │
│ └──────────┘ └──────────┘ └────────┘ │
└─────────────────────────────────────────┘
↓
┌─────────────────────────────────────────┐
│ 系统层 (System Layer - Linux) │
│ │
│ ┌────────────────────────────────────┐ │
│ │ systemd 系统管理 │ │
│ │ - 自动登录 kiosk 用户 │ │
│ │ - 启动 game-launcher 服务 │ │
│ │ - 禁用睡眠、更新等 │ │
│ └────────────────────────────────────┘ │
│ │
│ ┌────────────────────────────────────┐ │
│ │ X11/Wayland Display Server │ │
│ │ - Openbox 极简窗口管理 │ │
│ │ - 禁用快捷键 (X11 事件) │ │
│ │ - 禁用系统菜单 │ │
│ └────────────────────────────────────┘ │
│ │
│ ┌────────────────────────────────────┐ │
│ │ Linux Kernel │ │
│ │ - SELinux / AppArmor │ │
│ │ - kiosk 用户权限管理 │ │
│ └────────────────────────────────────┘ │
└─────────────────────────────────────────┘
3.3 技术决策表
| 组件 | 选型 | 原因 |
|---|---|---|
| 启动器 | Golang | 内存 20MB, 启动 <100ms, 单二进制 |
| 菜单 UI | Vue 3 + TypeScript | 响应式, 易修改, 开发快 |
| 卡牌游戏 | Godot 4 + GDScript | UI 系统优秀, 易学, 体积小 |
| 赛车游戏 | Pixi.js (Web 版) | 2D 性能最优, 易于参数调整 |
| AI 算法 | 决策树 + 蒙特卡洛 | 快速开发, 难度可调, 可解释 |
| 数据库 | SQLite | 轻量级, 无需服务器, 易于备份 |
| 配置 | JSON | 易读易写, 无需解析库 |
系统架构
4.1 模块划分
project-root/
│
├─ launcher/ # Golang 启动器 (Module-1)
│ ├─ cmd/main.go # 入口点
│ ├─ internal/
│ │ ├─ launcher/launcher.go # 核心启动器逻辑
│ │ ├─ server/http.go # HTTP 服务器
│ │ ├─ process/manager.go # 进程管理
│ │ ├─ config/config.go # 配置加载
│ │ └─ logger/logger.go # 日志系统
│ ├─ go.mod
│ ├─ go.sum
│ └─ Makefile # 编译脚本
│
├─ frontend/ # Vue 3 菜单 (Module-2)
│ ├─ src/
│ │ ├─ App.vue # 根组件
│ │ ├─ main.ts # 入口
│ │ ├─ components/
│ │ │ ├─ GameGrid.vue # 游戏网格
│ │ │ ├─ GameCard.vue # 游戏卡片
│ │ │ └─ LoadingSpinner.vue # 加载提示
│ │ ├─ views/
│ │ │ ├─ MainMenu.vue # 菜单视图
│ │ │ ├─ StatisticsView.vue # 统计视图 (可选)
│ │ │ └─ SettingsView.vue # 设置视图 (可选)
│ │ ├─ services/
│ │ │ └─ api.ts # API 通信
│ │ ├─ types/
│ │ │ └─ game.ts # 类型定义
│ │ └─ styles/
│ │ └─ main.css # 全局样式
│ ├─ public/
│ │ ├─ games.json # 游戏配置
│ │ ├─ images/ # 游戏海报
│ │ └─ fonts/ # 中文字体
│ ├─ package.json
│ ├─ tsconfig.json
│ ├─ vite.config.ts
│ └─ Makefile
│
├─ games/ # 游戏项目
│ │
│ ├─ doudizhu/ # 斗地主 (Module-3)
│ │ ├─ project.godot # Godot 项目配置
│ │ ├─ scenes/
│ │ │ ├─ Game.tscn # 主场景
│ │ │ ├─ Card.tscn # 卡牌节点
│ │ │ ├─ Player.tscn # 玩家区域
│ │ │ └─ UI/
│ │ │ ├─ HandUI.tscn
│ │ │ ├─ TableUI.tscn
│ │ │ └─ InfoPanel.tscn
│ │ ├─ scripts/
│ │ │ ├─ Game.gd # 主逻辑 (~800 行)
│ │ │ ├─ CardLogic.gd # 规则判定 (~300 行)
│ │ │ ├─ AIPlayer.gd # AI 对手 (~500 行)
│ │ │ └─ UIController.gd # UI 控制 (~200 行)
│ │ ├─ assets/
│ │ │ ├─ images/
│ │ │ │ ├─ cards/ # 108 张牌纹理
│ │ │ │ └─ ui/
│ │ │ ├─ sounds/
│ │ │ │ ├─ card_play.wav
│ │ │ │ ├─ win.wav
│ │ │ │ └─ lose.wav
│ │ │ └─ fonts/
│ │ └─ export_presets.cfg # 导出配置
│ │
│ ├─ tractor/ # 拖拉机 (Module-4)
│ │ ├─ project.godot
│ │ ├─ scenes/
│ │ ├─ scripts/
│ │ │ ├─ Game.gd
│ │ │ ├─ CardLogic.gd # 拖拉机规则
│ │ │ └─ AIPlayer.gd
│ │ └─ assets/
│ │
│ ├─ mahjong/ # 卡五星 (Module-5, 可选)
│ │ ├─ project.godot
│ │ ├─ scenes/
│ │ ├─ scripts/
│ │ │ ├─ Game.gd
│ │ │ ├─ MahjongLogic.gd # 麻将规则
│ │ │ └─ AIPlayer.gd
│ │ └─ assets/
│ │
│ └─ racing/ # 赛车 (Module-6, Pixi.js)
│ ├─ src/
│ │ ├─ main.ts
│ │ ├─ Game.ts # 游戏主类
│ │ ├─ Car.ts # 车辆物理
│ │ ├─ Track.ts # 赛道管理
│ │ ├─ AI.ts # AI 赛手
│ │ └─ UI.ts # UI 管理
│ ├─ assets/
│ │ ├─ images/
│ │ ├─ sounds/
│ │ └─ fonts/
│ ├─ package.json
│ └─ tsconfig.json
│
├─ shared/ # 共享资源
│ ├─ fonts/
│ │ ├─ SourceHanSansSC-Regular.otf # 中文字体
│ │ └─ SourceHanSansSC-Bold.otf
│ ├─ images/
│ │ ├─ doudizhu.jpg # 游戏海报
│ │ ├─ tractor.jpg
│ │ ├─ mahjong.jpg
│ │ └─ racing.jpg
│ └─ sounds/
│ ├─ ui_click.wav
│ ├─ ui_hover.wav
│ └─ bgm.ogg
│
├─ config/ # 配置文件
│ ├─ kiosk/
│ │ ├─ gdm-custom.conf # GDM 自动登录
│ │ ├─ systemd-service.ini # systemd 服务
│ │ ├─ openbox-rc.xml # Openbox 配置
│ │ └─ xset-config.sh # X11 配置脚本
│ └─ app/
│ ├─ launcher.json # 启动器配置
│ └─ games.json # 游戏清单
│
├─ scripts/ # 构建和部署脚本
│ ├─ build-windows.bat # Windows 编译脚本
│ ├─ build-linux.sh # Linux 编译脚本
│ ├─ build-all.sh # 全量编译脚本
│ ├─ cross-compile.sh # 交叉编译脚本
│ ├─ deploy.sh # 部署脚本
│ ├─ setup-kiosk.sh # Kiosk 配置脚本
│ └─ verify.sh # 验证脚本
│
├─ docs/ # 文档
│ ├─ PRD.md # 本文档
│ ├─ ARCHITECTURE.md # 架构详解
│ ├─ DEVELOPMENT.md # 开发指南
│ ├─ API.md # API 文档
│ ├─ DEPLOYMENT.md # 部署指南
│ ├─ TESTING.md # 测试指南
│ └─ TROUBLESHOOTING.md # 故障排查
│
├─ tests/ # 测试用例
│ ├─ launcher/
│ ├─ frontend/
│ ├─ games/
│ └─ integration/
│
├─ README.md # 项目概述
├─ Makefile # 顶层编译脚本
├─ docker-compose.yml # 可选: Docker 支持
└─ .github/
└─ workflows/ # CI/CD 配置
4.2 依赖关系
启动器 (Golang)
│
├─ 依赖: zserge/webview (嵌入式浏览器)
│ stdlib (net/http, os/exec, etc.)
│
└─→ 启动 HTTP 服务器 (localhost:8888)
│
├─ 提供静态文件: 菜单 UI (Vue)
│
├─ 提供 API 端点: /api/launch
│ │
│ └─→ 调用 os/exec 启动游戏进程
│
└─ 提供 Webview
│
└─→ 显示菜单 UI
│
└─→ 用户交互
│
└─→ 调用 API 启动游戏
游戏进程 (Godot / Pixi.js)
│
├─ 通过 IPC/stdio 接收启动参数
│
└─ 独立运行,不需与启动器通信
(游戏结束时自己主动退出)
菜单 UI (Vue 3)
│
├─ 依赖: Vue 3, TypeScript, Vite
│
└─ 运行于 Webview 中
│
├─ 加载 games.json
│
└─ 通过 fetch API 调用启动器 HTTP 接口
开发规范
5.1 代码规范
5.1.1 Golang 代码规范
// 文件头注释
// Package main 描述此包的用途
package main
import (
"fmt"
"os"
// 标准库分组
"github.com/zserge/webview"
// 第三方库分组
)
const (
// 常量大写 + 下划线
DEFAULT_WEB_PORT = 8888
LAUNCHER_VERSION = "1.0.0"
)
type GameLauncher struct {
// 字段注释
currentGame *exec.Cmd // 当前运行的游戏进程
webPort int // HTTP 服务器端口
config Config // 配置对象
}
// NewGameLauncher 创建启动器实例
// 返回: *GameLauncher - 启动器指针
func NewGameLauncher() *GameLauncher {
return &GameLauncher{
webPort: DEFAULT_WEB_PORT,
}
}
// Start 启动游戏启动器
// 返回: error - 错误信息
func (gl *GameLauncher) Start() error {
// 函数实现
return nil
}
规则:
- 变量名用驼峰命名 (camelCase)
- 常量用大写 (CONSTANT_NAME)
- 每个公共函数必须有注释
- 每个包必须有注释说明其用途
- 错误处理不能忽略:
if err != nil { return err } - 使用
fmt.Sprintf构建字符串,不要用+
5.1.2 Vue/TypeScript 代码规范
// 文件头注释
/**
* GameLauncher.vue - 游戏启动器主组件
*
* 功能:
* - 显示游戏菜单网格
* - 处理游戏启动逻辑
* - 显示加载状态
*/
<template>
<div class="game-launcher" role="main">
<!-- 语义化 HTML -->
<header class="header">
<h1>家庭游戏中心</h1>
</header>
<!-- v-for 必须使用 :key -->
<div class="game-grid">
<button
v-for="game in games"
:key="game.id"
class="game-card"
@click="launchGame(game.id)"
:aria-label="`启动${game.name}游戏`"
>
{{ game.name }}
</button>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted, computed } from 'vue'
// 类型定义
interface Game {
id: string
name: string
poster: string
desc: string
}
// 响应式变量声明
const games = ref<Game[]>([])
const isLoading = ref(false)
// 计算属性
const gameCount = computed(() => games.value.length)
// 生命周期
onMounted(async () => {
await loadGames()
})
// 函数
async function loadGames(): Promise<void> {
try {
const response = await fetch('/games.json')
games.value = await response.json()
} catch (error) {
console.error('加载游戏列表失败:', error)
}
}
async function launchGame(gameId: string): Promise<void> {
isLoading.value = true
try {
const response = await fetch(`/api/launch?id=${gameId}`)
const data = await response.json()
if (data.error) {
console.error('启动失败:', data.error)
}
} finally {
isLoading.value = false
}
}
</script>
<style scoped lang="css">
/* BEM 命名法 */
.game-launcher {
display: flex;
flex-direction: column;
min-height: 100vh;
}
.game-launcher__header {
padding: 40px;
}
.game-card {
/* 移动优先,逐级增强 */
width: 100%;
/* 触屏友好的最小高度 */
min-height: 80px;
font-size: 1.5rem;
}
@media (min-width: 768px) {
.game-card {
width: 300px;
font-size: 1.8rem;
}
}
@media (min-width: 1920px) {
.game-card {
width: 400px;
font-size: 2.2rem;
}
}
</style>
规则:
- 使用
const优于let,尽量避免var - 使用 TypeScript,不要用 JavaScript
- 组件名使用 PascalCase (GameCard.vue)
- 变量名使用 camelCase
- 使用 BEM 命名法管理 CSS 类名
- 必须包含 ARIA 无障碍属性
- 类型定义在每个文件头部
5.1.3 GDScript 代码规范
# 文件头注释
## CardLogic.gd - 卡牌逻辑和规则判定
##
## 实现斗地主的卡牌判定算法
## 包括牌型检查、胜负判定等
class_name CardLogic
# 常量定义
const CARD_RANKS = ["9", "10", "J", "Q", "K", "A", "2"]
const CARD_SUITS = ["♠", "♥", "♦", "♣", "王"]
# 静态函数
static func is_valid_play(cards: Array) -> bool:
"""检查出牌是否合法"""
if cards.is_empty():
return false
# 单张
if cards.size() == 1:
return true
# 对牌
if cards.size() == 2:
return cards[0].rank == cards[1].rank
return false
# 实例函数
func get_best_play(hand: Array, table_cards: Array) -> Array:
"""根据手牌和桌面牌,给出最佳出牌"""
# 实现逻辑
return []
规则:
- 类名使用 PascalCase (CardLogic)
- 函数名使用 snake_case (is_valid_play)
- 常量使用 SCREAMING_SNAKE_CASE
- 每个公共函数使用
##文档注释 - 避免全局变量,使用类成员变量
5.2 分支管理
main (生产分支)
│
├─ develop (开发分支)
│ │
│ ├─ feature/launcher (启动器功能)
│ ├─ feature/menu-ui (菜单 UI)
│ ├─ feature/doudizhu (斗地主)
│ ├─ feature/ai-algorithm (AI 算法)
│ └─ bugfix/xxx (bug 修复)
│
└─ release/v1.0 (发版分支)
规则:
- 功能开发:
feature/功能名 - bug 修复:
bugfix/bug名 - 发版准备:
release/版本号 - 每个 PR 必须有描述和关联 issue
- 至少一人 code review 后才能合并
5.3 提交规范
# 格式: <type>(<scope>): <subject>
# <body>
# <footer>
# 示例
git commit -m "feat(launcher): 实现游戏启动功能
- 支持从菜单启动游戏
- 实现进程监控和自动返回
- 添加错误日志记录
Closes #123"
# type 类型:
# feat: 新功能
# fix: bug 修复
# docs: 文档修改
# style: 代码格式 (不影响功能)
# refactor: 代码重构
# test: 添加测试
# chore: 构建、依赖等
5.4 测试规范
测试文件位置: 与源文件同目录,使用 _test 后缀
launcher/
├─ cmd/main.go
├─ cmd/main_test.go # 对应的测试文件
├─ internal/launcher/launcher.go
└─ internal/launcher/launcher_test.go
规则:
- 每个模块必须有单元测试
- 测试覆盖率目标: > 80%
- 关键业务逻辑必须有集成测试
- 使用表驱动测试法组织测试用例
// 表驱动测试示例
func TestIsValidPlay(t *testing.T) {
tests := []struct {
name string
cards []Card
want bool
}{
{
name: "单张",
cards: []Card{{rank: 9}},
want: true,
},
{
name: "对牌",
cards: []Card{{rank: 9}, {rank: 9}},
want: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := CardLogic.IsValidPlay(tt.cards)
if got != tt.want {
t.Errorf("got %v, want %v", got, tt.want)
}
})
}
}
API 设计
6.1 HTTP API 规范
6.1.1 基础信息
基础 URL: http://localhost:8888
版本: v1
认证: 无 (本地应用)
错误格式: JSON
6.1.2 API 端点
1. 启动游戏 (POST /api/launch)
请求:
GET /api/launch?id=doudizhu&difficulty=normal
响应 (200 OK):
{
"status": "launched",
"gameId": "doudizhu",
"timestamp": 1704028800000
}
响应 (400 Bad Request):
{
"error": "game not found",
"gameId": "invalid_game"
}
响应 (500 Internal Error):
{
"error": "failed to launch game",
"details": "process spawn failed"
}
参数:
id(string, required): 游戏 ID (doudizhu, tractor, mahjong, racing)difficulty(string, optional): 难度 (easy, normal, hard),默认 normal
业务逻辑:
- 验证游戏 ID 是否存在
- 如果有游戏在运行,先关闭它
- 使用 os/exec 启动游戏进程
- 通过 goroutine 监听游戏退出事件
- 游戏退出后自动返回菜单
6.1.3 游戏状态 (GET /api/status)
请求:
GET /api/status
响应 (200 OK):
{
"status": "ok",
"currentGame": "doudizhu",
"uptime": 3600,
"memory": 256,
"version": "1.0.0"
}
6.1.4 获取游戏列表 (GET /games.json)
请求:
GET /games.json
响应 (200 OK):
[
{
"id": "doudizhu",
"name": "🎴 斗地主",
"desc": "经典三人卡牌游戏",
"poster": "/images/doudizhu.jpg",
"players": 3,
"difficulty": "easy"
},
...
]
6.2 IPC 通信 (可选)
游戏进程可通过标准输入接收启动参数:
./doudizhu --difficulty normal --players 3
游戏进程通过标准输出向启动器报告状态:
{"status": "loaded", "game": "doudizhu"}
{"status": "game_over", "winner": "player1", "score": 100}
数据库设计
7.1 SQLite 数据库
文件位置: ~/.game-center/data/game.db
7.1.1 表结构
表 1: games (游戏元数据)
CREATE TABLE games (
id TEXT PRIMARY KEY, -- doudizhu, tractor, etc.
name TEXT NOT NULL, -- 显示名称
description TEXT, -- 游戏描述
version TEXT, -- 游戏版本
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
表 2: game_stats (游戏统计)
CREATE TABLE game_stats (
id INTEGER PRIMARY KEY AUTOINCREMENT,
game_id TEXT NOT NULL, -- 外键: games.id
player_name TEXT DEFAULT '玩家',
result TEXT NOT NULL, -- win, loss, draw
score INTEGER DEFAULT 0,
duration_seconds INTEGER, -- 游戏时长
play_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (game_id) REFERENCES games(id)
);
-- 索引优化查询
CREATE INDEX idx_game_stats_game_id ON game_stats(game_id);
CREATE INDEX idx_game_stats_play_date ON game_stats(play_date);
表 3: ai_profiles (AI 配置档)
CREATE TABLE ai_profiles (
id INTEGER PRIMARY KEY AUTOINCREMENT,
game_id TEXT NOT NULL, -- 外键: games.id
difficulty TEXT NOT NULL, -- easy, normal, hard
strategy_version INTEGER DEFAULT 1,
win_rate REAL DEFAULT 0.5, -- 当前胜率
last_updated TIMESTAMP,
FOREIGN KEY (game_id) REFERENCES games(id)
);
7.1.2 初始化脚本
-- init.sql
PRAGMA foreign_keys = ON;
INSERT INTO games (id, name, description, version) VALUES
('doudizhu', '🎴 斗地主', '经典三人卡牌游戏', '1.0.0'),
('tractor', '🎯 拖拉机', '四人跑牌游戏', '1.0.0'),
('mahjong', '🀄 卡五星', '五人麻将变种', '1.0.0'),
('racing', '🏎️ 赛车', '简单竞速游戏', '1.0.0');
INSERT INTO ai_profiles (game_id, difficulty) VALUES
('doudizhu', 'easy'),
('doudizhu', 'normal'),
('doudizhu', 'hard');
7.2 配置文件 (JSON)
文件位置: ~/.game-center/config/launcher.json
{
"version": "1.0.0",
"launcher": {
"port": 8888,
"host": "127.0.0.1",
"auto_launch": true,
"fullscreen": true
},
"ui": {
"theme": "dark",
"language": "zh-CN",
"font_size": 32,
"button_size": 80
},
"system": {
"log_level": "INFO",
"log_file": "/home/kiosk/.game-center/logs/launcher.log",
"max_log_size_mb": 100,
"auto_restart": true,
"restart_delay_seconds": 10
},
"games": {
"doudizhu": {
"enabled": true,
"difficulty": "normal",
"ai_count": 2
},
"tractor": {
"enabled": true,
"difficulty": "normal",
"ai_count": 3
}
}
}
UI/UX 设计规范
8.1 响应式设计
断点定义:
- 移动设备 (1080p): 最小 (grid: 1 列)
- 平板/小屏 (1440p): 中等 (grid: 2 列)
- 桌面/大屏 (1920p): 标准 (grid: 3-4 列)
- 超大屏 (2560p+): 超大 (grid: 4-6 列)
布局优化:
- 游戏卡片宽度: 300-400px
- 卡片间距: 30px (响应式调整)
- 按钮最小高度: 80px (触屏友好)
- 文字最小大小: 32px (易读)
8.2 颜色方案
主色调: #667eea (紫色)
辅色调: #764ba2 (深紫色)
背景色: #f5f5f5 (浅灰)
文字色: #333333 (深灰)
强调色: #FFD700 (金色,焦点)
高对比度检查:
- 文本 vs 背景: 7:1 (WCAG AAA 标准)
- 焦点指示器: 金色边框,4px + 外阴影
8.3 字体规范
中文字体: SourceHanSansSC (思源黑体)
英文字体: -apple-system, BlinkMacSystemFont, Segoe UI
字体大小阶梯:
- 菜单标题: 3em (48px)
- 游戏名称: 1.8em (32px 基础)
- 描述文本: 1em (14-16px)
- 小提示: 0.875em (12px)
8.4 交互反馈
鼠标悬停:
- 缩放: scale(1.08)
- 阴影增强: 0 12px 24px
- 过渡: 0.3s ease
点击按钮:
- 按下: scale(0.95)
- 释放: scale(1.0)
- 反馈: 音效 + 视觉效果
加载状态:
- 显示加载动画 (旋转圈/进度条)
- 禁用按钮交互
- 提示文字: "游戏启动中..."
焦点管理:
- Tab 键导航支持
- 焦点圈清晰可见 (金色, 4px)
- 焦点陷阱: 最后一个卡片按 Tab 回到第一个
安全需求
9.1 系统级防护
9.1.1 BIOS/UEFI 级别
配置项:
- 启用 Secure Boot (可选,增强安全)
- 设置管理员密码
- 禁用 USB 启动
- 禁用光驱启动
9.1.2 Bootloader 级别 (GRUB)
配置:
/boot/grub/grub.cfg
# 自动启动,隐藏菜单
set timeout=0
# 禁用编辑功能
CLASS="--class gnu-linux --class os"
9.1.3 Linux 内核级别
# SELinux 强制模式
selinux=1
enforcing=1
# kiosk 用户权限限制
# /etc/sudoers
kiosk ALL=(ALL) NOPASSWD: /bin/false # 拒绝所有 sudo
9.2 应用级防护
9.2.1 快捷键拦截
# X11 级别禁用快捷键
xset r 133 # 禁用 Windows 键 (keycode 133)
xset r 108 # 禁用 Menu 键 (keycode 108)
# GTK 级别
gsettings set org.gnome.desktop.session idle-delay 0
gsettings set org.gnome.desktop.lockdown disable-lock-screen true
gsettings set org.gnome.desktop.lockdown disable-user-switching true
9.2.2 进程级防护
// Golang 启动器
- 不能 fork 到 shell (os/exec 设置 Stdout/Stderr)
- 不能修改文件权限 (运行于受限用户)
- 定期检查子进程 (监听 SIGCHLD)
- 自动重启崩溃的进程
9.3 数据安全
9.3.1 存档保护
# 文件权限
~/.game-center/data/ 644 # 用户可读写,组不可写
~/.game-center/data/game.db 600 # 仅所有者可读写
# 定期备份
cron: 每天 00:00 自动备份到 ~/.game-center/backup/
备份保留: 7 天轮转
# 损坏恢复
检测到数据库损坏时:
1. 恢复最新的完整备份
2. 删除损坏的数据
3. 清空日志并重启
9.3.2 日志安全
日志位置: ~/.game-center/logs/
权限: 644 (仅所有者可写)
日志级别:
- ERROR: 错误信息
- WARN: 警告信息
- INFO: 启动/停止事件
- DEBUG: 详细过程 (仅开发环境)
日志内容不包含:
- 用户隐私信息
- 系统配置敏感信息
- 完整错误堆栈 (用户环境)
9.4 网络安全
限制条件:
- 启动器仅监听 localhost (127.0.0.1:8888)
- Webview 无法访问外部 URL
- 游戏进程无网络权限
跨域限制:
- Content-Security-Policy 头严格
- 禁止 <iframe> 嵌入
- 禁止 fetch 外部资源
测试计划
10.1 测试分层
单元测试 (Unit Tests)
├─ Golang 启动器单元测试
├─ Vue 菜单组件测试
└─ Godot 游戏逻辑测试
集成测试 (Integration Tests)
├─ 启动器 + 菜单 UI 集成
├─ 菜单 UI + 游戏启动
└─ 多个游戏顺序启动
系统测试 (System Tests)
├─ 完整流程测试
├─ 长时间稳定性测试 (24+ 小时)
└─ 异常恢复测试
用户验收测试 (UAT)
├─ 真实用户操作验证
├─ 易用性评估
└─ 性能满足度评估
10.2 测试用例
10.2.1 菜单系统测试
TC-MENU-001: 菜单启动和显示
步骤: 1. 系统启动
2. 等待菜单显示
预期: 5 秒内显示菜单,显示 4 个游戏卡片
TC-MENU-002: 游戏启动
步骤: 1. 菜单显示
2. 点击"斗地主"卡片
3. 等待游戏加载
预期: 3 秒内启动游戏,显示加载提示
TC-MENU-003: 无法通过 Alt+F4 退出
步骤: 1. 菜单显示
2. 按下 Alt+F4
预期: 菜单继续显示,不退出
TC-MENU-004: 响应式布局
步骤: 1. 菜单显示
2. 改变窗口大小或分辨率
预期: 布局自适应,卡片大小调整,文字清晰
10.2.2 游戏逻辑测试
TC-GAME-001: 斗地主发牌
步骤: 1. 启动斗地主
2. 游戏初始化
预期: 每个玩家 17 张牌,地主额外 3 张
TC-GAME-002: 无法出非法牌
步骤: 1. 游戏进行中
2. 尝试出非法牌型
预期: 系统提示"非法出牌",不执行操作
TC-GAME-003: AI 出牌合法
步骤: 1. AI 轮到时
2. AI 自动出牌
预期: AI 出牌合法,不低于 1 秒延迟
TC-GAME-004: 游戏结束和返回
步骤: 1. 游戏进行到结束
2. 胜者产生
预期: 显示胜者信息,2 秒后自动返回菜单
10.2.3 安全性测试
TC-SEC-001: 快捷键禁用
步骤: 1. 菜单/游戏显示
2. 尝试按 Alt+Tab, Alt+F4, Windows 键等
预期: 所有快捷键无效,界面不变
TC-SEC-002: 无法访问系统文件
步骤: 1. 尝试通过菜单/游戏访问系统文件
预期: 无法进入文件管理器或系统设置
TC-SEC-003: 24 小时稳定运行
步骤: 1. 启动系统
2. 自动化循环启动所有游戏,每 30 分钟换一个
3. 持续 24 小时
预期: 无崩溃、无内存泄漏、无响应问题
10.3 性能指标
指标 目标值 验收标准
─────────────────────────────────────────────
系统启动时间 < 5 秒 进入菜单
菜单响应时间 < 100ms 点击到反馈
游戏启动时间 < 3 秒 游戏显示
菜单内存占用 < 300MB 启动器 + UI
游戏内存占用 < 300MB 单个游戏
总内存占用 < 600MB 启动器 + 菜单 + 游戏
菜单帧率 60fps 稳定渲染
游戏帧率 60fps 卡牌游戏
AI 响应时间 1-2 秒 出牌决策
CPU 占用 (空闲) < 5% 菜单显示,无交互
部署方案
11.1 开发环境 (Windows 11)
11.1.1 环境配置
# 安装 Golang (Windows)
下载: https://golang.org/dl/
版本: 1.20 LTS
设置 GOPATH 和 GOROOT 环境变量
验证: go version
# 安装 Node.js (Windows)
下载: https://nodejs.org/
版本: 18 LTS
验证: node --version, npm --version
# 安装 Godot (Windows)
下载: https://godotengine.org/
版本: 4.1 LTS
设置可执行文件路径
# 安装 Git
下载: https://git-scm.com/
使用 Git Bash 或 GitHub Desktop
11.1.2 项目初始化 (Windows)
# 克隆项目
git clone https://github.com/xxx/game-center.git
cd game-center
# 安装依赖
cd launcher && go mod download
cd ../frontend && npm install
cd ../games/racing && npm install
# 编译启动器 (针对 Windows)
cd launcher
go build -o launcher-windows.exe
# 编译菜单 UI
cd ../frontend
npm run build
# 输出到 dist/ 目录
# Godot 项目在 VS Code 中打开
# 在 Godot 编辑器中导入 games/doudizhu 项目
11.1.3 Windows 本地测试
# 启动菜单 (需要 Node.js)
cd frontend
npm run dev
# 访问 http://localhost:5173
# 启动 Golang 启动器 (Windows)
cd launcher
go run cmd/main.go
# 启动 localhost:8888 HTTP 服务器
# 测试 API
curl http://localhost:8888/api/status
curl http://localhost:8888/games.json
# 在 Godot 中运行游戏
# 在编辑器中点击"播放"按钮运行
11.2 生产环境 (Ubuntu 22.04 LTS)
11.2.1 系统安装
# 基础系统安装
1. 下载 Ubuntu 22.04 Server ISO
2. 最小化安装 (仅命令行)
3. 设置主机名: game-center
4. 设置网络: 有线网络
5. 创建用户: root (设置强密码)
6. 安装完成后重启
# 初始更新
sudo apt-get update
sudo apt-get upgrade -y
sudo apt-get install -y \
git curl wget \
gcc make pkg-config \
libxcb-1-0 libxcb-devel \
xorg-x11-server xorg-x11-utils \
openbox \
pulseaudio alsa-utils
11.2.2 Kiosk 配置
# 创建 kiosk 用户
sudo useradd -m -s /bin/bash kiosk
sudo passwd kiosk # 设置简单密码 (如: 1234)
# 编辑 /etc/gdm/custom.conf
[daemon]
AutomaticLoginEnable=true
AutomaticLogin=kiosk
# 禁用不必要服务
sudo systemctl disable cups
sudo systemctl disable ssh
sudo systemctl disable bluetooth
# 禁用睡眠/休眠
sudo systemctl mask sleep.target suspend.target hibernate.target
# 隐藏 GRUB 菜单
sudo vi /etc/default/grub
# 修改: GRUB_TIMEOUT=0
# 修改: GRUB_TIMEOUT_STYLE=hidden
sudo update-grub
11.2.3 部署应用
# 创建应用目录
sudo mkdir -p /opt/game-center
sudo mkdir -p /opt/games
# 复制已编译的启动器 (从 Windows 交叉编译得到)
sudo cp launcher-linux /opt/game-center/launcher
sudo chmod +x /opt/game-center/launcher
# 复制菜单 UI
sudo cp -r frontend/dist/* /opt/game-center/html/
sudo chmod -R 755 /opt/game-center/html
# 复制游戏二进制 (从 Godot 导出)
sudo cp games/doudizhu/doudizhu.x86_64 /opt/games/
sudo cp games/tractor/tractor.x86_64 /opt/games/
sudo cp games/racing/racing.x86_64 /opt/games/
sudo chmod +x /opt/games/*.x86_64
# 创建 systemd 服务
sudo cat > /etc/systemd/system/game-launcher.service << 'EOF'
[Unit]
Description=Game Launcher Service
After=network.target
[Service]
Type=simple
User=kiosk
WorkingDirectory=/opt/game-center
ExecStart=/opt/game-center/launcher
Restart=always
RestartSec=10
[Install]
WantedBy=multi-user.target
EOF
# 启用和启动服务
sudo systemctl enable game-launcher.service
sudo systemctl start game-launcher.service
sudo systemctl status game-launcher.service
11.2.4 验证部署
# 检查服务状态
sudo systemctl status game-launcher.service
# 查看日志
sudo journalctl -u game-launcher.service -f
# 测试 HTTP 接口
curl http://localhost:8888/
curl http://localhost:8888/api/status
curl http://localhost:8888/games.json
# 检查进程
ps aux | grep launcher
ps aux | grep doudizhu
# 检查内存使用
free -h
top
# 测试快捷键禁用
# 在显示器上尝试按 Alt+F4, Alt+Tab 等
# 验证无法退出或切换窗口
11.3 交叉编译指南
11.3.1 在 Windows 编译 Linux 二进制
# PowerShell (Windows)
# 设置交叉编译环境变量
$env:GOOS = "linux"
$env:GOARCH = "amd64"
$env:CGO_ENABLED = "0"
# 编译 Golang 启动器
cd launcher
go build -o launcher-linux -ldflags="-w -s"
# 输出: launcher-linux (无扩展名)
# 验证生成的二进制
file launcher-linux # 应显示 ELF 64-bit
# 复制到部署目录
Copy-Item launcher-linux ../deploy/
11.3.2 Godot 导出配置
# project.godot 中的导出设置
[preset.0]
name="Linux/X11"
platform="Linux/X11"
runnable=true
custom_features=""
export_filter="all_resources"
export_files=[]
script_export_mode=1
script_encryption_key=""
[preset.0.options]
binary_format/64_bits=true
binary_format/embed_pck=true
texture_format/vram_compression="ETC2"
导出步骤:
- Godot 编辑器 → 项目 → 导出项目
- 选择 Linux/X11 预设
- 点击"导出项目"
- 输出文件:
doudizhu.x86_64
验收标准
12.1 功能验收
| 功能 | 验收标准 | 优先级 |
|---|---|---|
| 菜单显示 | 5秒内显示,4个卡片清晰可见,点击可启动 | P0 |
| 游戏启动 | 3秒内启动游戏,显示加载提示 | P0 |
| 快捷键禁用 | Alt+F4, Alt+Tab, Win 键等全部无效 | P0 |
| AI 对手 | 合法出牌,响应时间 1-2 秒 | P0 |
| 游戏结束 | 自动返回菜单,保存成绩 | P0 |
| 数据持久化 | 成绩保存到数据库,重启后可查询 | P1 |
| 故障恢复 | 游戏崩溃自动返回菜单 | P1 |
| 响应式布局 | 适配 1080p 到 4K,自动调整 | P0 |
12.2 性能验收
| 指标 | 目标 | 验收标准 |
|---|---|---|
| 启动时间 | < 5s | 进入菜单显示 |
| 菜单响应 | < 100ms | 点击到视觉反馈 |
| 游戏启动 | < 3s | 游戏窗口显示 |
| 内存占用 | < 600MB | 启动器+菜单+游戏总计 |
| 帧率稳定 | 60fps | 无卡顿掉帧 |
| 24小时稳定 | 无崩溃 | 连续运行无异常 |
12.3 安全验收
| 项目 | 验收标准 |
|---|---|
| 快捷键 | Alt+F4, Alt+Tab, Win, Ctrl+Alt+Fx 全部禁用 |
| 菜单 | 右键菜单禁用,任务栏隐藏 |
| 文件访问 | 无法进入文件管理器或系统设置 |
| 进程管理 | 用户无法修改或杀死游戏进程 |
| 数据安全 | 游戏数据加密存储,自动备份 |
12.4 用户验收
| 项目 | 验收标准 |
|---|---|
| 易用性 | 无计算机知识的用户能独立操作 |
| 可读性 | 字体大小 32px+,易于老年人阅读 |
| 按钮大小 | 80px+ 易于点击,触屏友好 |
| 游戏规则 | 规则简洁明确,易于理解 |
| 反馈及时 | 点击、加载、结果等有明确反馈 |
12.5 交付清单
□ 源代码 (GitHub 仓库)
├─ launcher/ (Golang)
├─ frontend/ (Vue 3)
├─ games/ (Godot + Pixi.js)
├─ config/ (系统配置)
├─ scripts/ (部署脚本)
└─ docs/ (文档)
□ 编译产物
├─ launcher-linux (Golang 二进制)
├─ launcher-windows.exe (Windows 二进制,用于本地测试)
├─ frontend/dist/ (Vue 构建输出)
├─ games/doudizhu/doudizhu.x86_64 (Godot 导出)
├─ games/tractor/tractor.x86_64
├─ games/racing/racing.x86_64
└─ games/mahjong/mahjong.x86_64 (可选)
□ 文档
├─ PRD.md (本文档)
├─ ARCHITECTURE.md (架构详解)
├─ DEVELOPMENT.md (开发指南)
├─ DEPLOYMENT.md (部署指南)
├─ API.md (API 文档)
├─ TESTING.md (测试指南)
└─ TROUBLESHOOTING.md (故障排查)
□ 系统镜像 (可选)
├─ game-center-base.iso (基础 Ubuntu 镜像)
├─ game-center-complete.img (完整部署镜像)
└─ backup.tar.gz (系统备份)
□ 支持材料
├─ 用户手册 (PDF)
├─ 快速开始指南
├─ 故障排查表
└─ 维护清单
附录 A: 术语表
| 术语 | 说明 |
|---|---|
| Kiosk 模式 | 系统锁定模式,用户只能运行指定应用 |
| 启动器 | Golang 应用,负责管理菜单和游戏进程 |
| Webview | 嵌入式浏览器,显示 Vue 菜单 |
| AI 对手 | 游戏中的电脑玩家 |
| 卡牌游戏 | 基于卡牌规则的游戏 (斗地主、拖拉机等) |
| 赛车游戏 | 基于物理引擎的竞速游戏 |
| 决策树 | AI 算法,通过规则树进行决策 |
| 蒙特卡洛树搜索 | AI 算法,通过模拟进行决策优化 |
| 交叉编译 | 在一个平台编译另一个平台的可执行文件 |
| systemd | Linux 系统服务管理器 |
| Openbox | 极简窗口管理器 |
附录 B: 参考资源
官方文档
开发工具
第三方库
- zserge/webview - Golang 嵌入式浏览器
- Pixi.js - 2D 渲染引擎