基于 Vue 3 + TypeScript + Vite 的大富翁活动页。玩家通过掷骰子探索岛屿,在 30 格外围路线与 7 格特殊路线中触发后退、慢行、传送和奖励,完成整圈后领取里程碑宝箱。
项目包含 PC/H5 响应式界面、独立玩法引擎、Spine 骰子动画、角色移动动画、本地进度恢复,以及规则与浏览器自动化测试。
当前版本为可完整游玩的本地演示。 无需后端或环境变量即可启动;骰子结果、道具和奖励均由前端模拟,尚未接入真实活动、账户或奖励发放接口。
PC 使用棋盘与操作区双栏布局;H5 将圈数奖励、完整棋盘和操作区按纵向排列。两端共用地图和玩法配置,页面在 760px 及以下切换为移动布局。
当前功能包括:
- 37 格棋盘:外围闭环与七格特殊奖励路线,支持点击格子查看详情。
- 道具触发:后退、三次慢行、定点传送及落地后的连续结算。
- 奖励收集:金币、水晶、宝箱金币、额外骰子与前五圈里程碑宝箱。
- 动画反馈:Spine 投掷动画、角色逐格跳跃、朝向切换与传送效果。
- 本地恢复:保存位置、道具次数、奖励、圈数、领取记录和活动到期时间。
- 交互保护:移动中禁止重复投掷,宝箱禁止重复领取,异常道具链不部分结算。
| 项目 | 要求 |
|---|---|
| Node.js | `^20.19.0 |
| 包管理器 | npm,使用仓库中的 package-lock.json 安装 |
| 浏览器 | 支持现代 CSS Grid、ResizeObserver 和 ES Modules 的浏览器 |
| 自动化测试 | 本机安装 Google Chrome,测试配置使用 channel: 'chrome' |
进入项目目录后运行:
npm ci
npm run dev打开终端输出的地址。Vite 默认使用 http://localhost:5173,端口占用时会尝试下一个可用端口。
如需固定使用当前演示的 5174 端口:
npm run dev -- --port 5174 --strictPort手机联调时运行:
npm run dev -- --host 0.0.0.0手机与电脑连接同一网络,使用终端显示的局域网地址访问。
| 命令 | 用途 |
|---|---|
npm ci |
按锁文件安装依赖 |
npm run dev |
启动 Vite 开发服务 |
npm run test |
执行规则与状态边界单元测试 |
npm run test:e2e |
执行 Playwright 浏览器测试 |
npm run build |
TypeScript 检查、生产构建并生成 ZIP |
npm run preview |
本地预览已构建的 dist/ |
npm audit --registry=https://registry.npmjs.org |
使用 npm 官方接口检查依赖安全告警 |
npm run build
npm run preview构建产物:
dist/ 静态站点文件,可部署到静态服务器
dist.zip dist 目录内容的压缩包
dist.zip 解压后直接得到站点文件。构建目录、压缩包及测试临时输出均已加入 .gitignore。
项目使用 Hash 路由,入口为 /#/。部署到子路径时,需要设置 vite.config.ts 中的 base,并确认 spine/dice/ 下的动画资源能够访问。预览与部署应通过 HTTP 服务访问。
在线地址:https://fluenthaze.github.io/Monopoly/
仓库使用 .github/workflows/pages.yml 自动部署:
- 将本地提交推送到
main分支。 - GitHub Actions 使用 Node.js 24 安装依赖、运行规则测试并进行类型检查与构建。
- 成功后上传
dist/并发布至 Pages;构建或测试失败不会发布新的页面。
git push origin main本地 git commit 不会触发线上更新,需要推送到 GitHub。 也可以在仓库 Actions 中选择 Deploy to GitHub Pages → Run workflow 手动发布。
Pages 的发布来源为 GitHub Actions。工作流通过 VITE_BASE_PATH 自动设置仓库子路径,本地开发仍默认使用 /,无需手动修改配置或提交 dist/。
如需本地检查与 Pages 相同的构建路径:
VITE_BASE_PATH=/Monopoly/ npm run build
VITE_BASE_PATH=/Monopoly/ npm run preview访问预览服务的 /Monopoly/ 路径,检查图片、静态文件和 Spine 动画。
界面编号从 1 开始,代码内部 ID 从 0 开始。 下表使用界面编号,配置章节使用内部 ID。
| 玩法 | 当前规则 |
|---|---|
| 投掷 | 每次消耗 1 个骰子,正常前进 1–6 格;初始提供 99 个骰子 |
| 落地 | 仅最终停留的格子触发效果,途经格子不会发放奖励或触发道具 |
| 后退 | 第 13 格后退 4 格,第 25 格后退 2 格;退到起点即停止 |
| 慢行 | 第 8、22 格触发,接下来三次投掷各走 1 格,每次仍消耗一个骰子;重入刷新为三次 |
| 传送 | 第 6 格传送到第 10 格,继续结算目标格效果;传送本身不增加圈数 |
| 特殊路线 | 停留在第 28 格后,下次投掷进入第 31–37 格,再回到外围第 12 格;仅经过入口不会转入内圈 |
| 跨圈 | 正常向前从第 30 格回到起点计 1 圈;一次投掷跨过起点后会继续走完剩余步数 |
| 圈数宝箱 | 前五圈分别奖励 50、100、150、200、300 金币,各领取一次 |
| 活动时间 | 创建新进度时开始三天倒计时;刷新不会延长时间,结束后禁止投掷 |
后退和传送的最终落点可以继续触发道具。结算器会检查循环触发,避免无限执行;调整地图时,也应避免跨回合反复触发慢行与后退、导致角色无法离开的配置。
本地状态保存在 localStorage,键名为 monopoly-adventure-v2,包含:
position 当前格子 ID dice 剩余骰子
completed 已完成圈数 slow 剩余慢行次数
coins 金币 gems 水晶
claimed 已领取圈数宝箱 trail 行走历史
expiresAt 固定到期时间 version 存档结构版本
刷新后恢复有效存档,损坏或结构不兼容的存档会重置。点击页面底部 “重新冒险” 并确认,可以清空当前进度、重新开始。
存储按浏览器和访问来源隔离,不同端口、域名或设备不会共享进度;当前也未实现多标签页并发同步。
主要入口:src/views/monopoly/game.ts。修改格子内容通常只需调整 specials;修改路线结构还需要同步移动规则与界面。
interface Tile {
id: number; // 内部 ID,界面显示为 id + 1
kind: TileKind; // 格子类型,决定效果与默认图标
label: string; // 详情与冒险记录文案
amount?: number; // 奖励数量、后退步数或慢行次数
target?: number; // 传送目标的内部 ID
x: number; // 棋盘坐标
y: number;
}支持以下格子类型:
kind |
效果 | 主要参数 |
|---|---|---|
normal |
普通格,继续前进 | — |
start |
起点 | 与跨圈规则关联 |
coin |
增加金币 | amount |
gem |
增加水晶 | amount |
chest |
宝箱,目前增加金币 | amount |
dice |
增加骰子 | amount |
back |
沿行走历史后退 | amount |
slow |
后续投掷每次走一格 | amount |
portal |
传送并结算新落点 | target |
gate |
特殊路线入口标识 | 与分支规则关联 |
在 specials 中替换或新增对应的配置项。例如:
const specials: Record<number, Partial<Tile>> = {
// 内部 ID 3,即页面第 4 格:获得 50 金币
3: { kind: 'coin', label: '金币 +50', amount: 50 },
// 页面第 6 格:传送到页面第 10 格
5: { kind: 'portal', label: '传送至第 10 格', target: 9 },
// 页面第 8 格:接下来三次投掷各走一格
7: { kind: 'slow', label: '慢行 3 次', amount: 3 },
// 页面第 13 格:改成后退两格
12: { kind: 'back', label: '后退 2 格', amount: 2 },
};上面仅展示部分配置,修改时保留其他已有项。未列入 specials 的位置会生成普通格。
label与amount、target分开维护,数值改变后应同步修改文案。target使用内部 ID,必须指向有效格子。- 奖励与步数应使用有效正整数。
- 当前存档校验允许的慢行次数为 0–3;如需提高上限,应同步调整
restoreGame、说明文案与测试。
positions 按顺序提供全部 37 个格子的原始 [x, y] 坐标,数组下标即格子 ID。外围分为四段生成,最后七项为内圈坐标。
坐标经过两层处理后显示:
BOARD以x = 355为中心,通过84 / 90系数收紧水平间距。Map.vue在 PC 布局中将纵坐标乘以0.8;页面再根据容器宽度缩放整个棋盘。
修改位置只改变排布,不会改变行走顺序。角色与连接线共用处理后的坐标,无需分别修改角色落点。
格子尺寸、底图显示和选中效果在 Box.vue 中调整;PC 的紧凑样式位于 Map.vue。
当前移动逻辑使用固定连接:
外围:0 → 1 → … → 29 → 0
分支:从 27 出发 → 30 → 31 → … → 36 → 11
只有一轮投掷开始时角色位于 27,才会进入特殊路线。
增加、删除格子或更换分支入口时,需要一起检查:
game.ts:positions、specials、walk、跨圈判定和restoreGame的 ID 范围。Map.vue:外围及内圈连接线、入口提示牌。Box.vue:普通格箭头指向。index.vue:玩法说明与格子详情文案。tests/:路线、后退、跨圈、存档恢复和浏览器用例。
仅给其他格子设置 kind: 'gate' 不会自动创建新分支。
createGame() 控制初始骰子、位置、资产和到期时间;MILESTONE_REWARDS 控制前五圈宝箱的金币数:
export const MILESTONE_REWARDS = [50, 100, 150, 200, 300] as const;若要改变宝箱数量,还需修改领取校验及 TurnsReward.vue 中的展示数量。
修改初始值后,使用 “重新冒险” 查看效果;已有本地存档会优先恢复,不会被新的初始配置覆盖。
Monopoly/
├── public/spine/dice/ Spine JSON、atlas 和纹理,运行时直接请求
├── src/
│ ├── assets/adventure/ 当前页面使用的 WebP 美术素材
│ ├── components/
│ │ ├── SpinePlayer/ Spine 播放器封装与生命周期清理
│ │ └── BtModal.vue 旧弹窗封装,Vant 样式按需加载
│ ├── global/global.scss 全局基础样式
│ ├── routers/ Hash 路由、页面标题
│ ├── stores/ Pinia 与旧动态弹窗状态
│ └── views/monopoly/
│ ├── index.vue 页面编排、交互、本地存储和回合提交
│ ├── game.ts 棋盘数据、纯规则结算、存档校验和领奖
│ ├── animation.ts 支持取消的动画等待工具
│ └── components/
│ ├── Map.vue 地图、路线、角色移动及传送动画
│ ├── Box.vue 单格底图、图标与当前位置效果
│ ├── Dice.vue 骰子素材、Spine 投掷与降级动画
│ ├── TurnsReward.vue 圈数宝箱展示
│ └── GameIcon.vue 素材名称到图片的映射
├── tests/
│ ├── game.test.ts 规则、存档及状态边界单元测试
│ └── e2e/adventure.spec.ts 浏览器玩法与响应式测试
├── design/ 设计稿、实际截图、素材说明与测试记录
├── playwright.config.ts 测试服务、浏览器和追踪配置
├── tsconfig*.json 应用、构建配置与测试的类型检查
├── vite.config.ts Vue、样式处理与路径别名
└── zip-dist.mjs 将构建结果打包为 dist.zip
Task.vue、PageHeader.vue、Time.vue、旧 rem/棋盘样式及旧美术文件作为历史参考保留,当前主页面不使用这些组件和全局 rem 缩放。任务接口尚未接入。
game.ts 不操作 DOM,Map.vue 不负责发放奖励。一次投掷按以下顺序执行:
- 页面检查剩余骰子、活动时间与移动锁。
planTurn()根据当前状态和点数计算整轮路线与落地效果。- 返回最终状态及
move、teleport、effect、lap事件列表。 Dice.vue播放投掷动画,Map.vue按事件播放移动与道具反馈。- 动画成功后,页面一次性提交新状态并保存进度。
计算过程不修改原始状态;异常或卸载取消时不会提交半个回合。移动中的重复投掷与领奖也不会并行结算。
| 技术 | 当前用途 |
|---|---|
| Vue 3 + TypeScript | 组件、响应式状态与类型检查 |
| Vite + SCSS | 开发服务、构建与样式 |
| Vue Router | Hash 路由 |
| Pinia | 全局旧弹窗状态;当前玩法状态由页面和规则模块管理 |
| Spine Player | 骰子投掷动画 |
| Vant | 保留的旧 Popup 组件,按需加载样式 |
| Node Test Runner + tsx | TypeScript 单元测试 |
| Playwright | Chrome 浏览器自动化测试 |
| fflate | 生成构建压缩包 |
实际依赖版本以 package-lock.json 为准。
当前素材统一放在 src/assets/adventure,由 GameIcon.vue 根据名称加载。例如 name="back-v2" 对应 back-v2.webp。
| 资源 | 用途 |
|---|---|
island.webp |
岛屿与页面环境背景 |
tile-ivory/mint/violet/gold.webp |
普通、道具、特殊路线和入口格子 |
explorer-v2.webp |
完整角色,左右朝向通过 CSS 镜像实现 |
dice.webp |
完整骰子图片,用于静态展示、按钮与图标 |
back-v2.webp |
后退折返箭头 |
turtle.webp、portal.webp |
慢行与传送道具 |
coin.webp、crystal.webp、star.webp |
奖励及特殊入口 |
chest-wood.webp、chest-jade.webp |
圈数宝箱及奖励 |
替换图片时保留透明背景和足够边距,尤其注意角色帽顶、脚部与道具轮廓。若修改资源名称,需要同步更新 Box.vue、页面详情或其他使用该名称的位置。
骰子投掷使用 public/spine/dice/ 中的 animation1–animation6。静止时展示整张生成素材,实际点数以下方文字为准;WebGL 不可用时旋转整张素材作为降级反馈。角色移动与传送效果使用 CSS,不依赖 Spine。
素材生成记录和提示词见 design/ASSETS.md、视觉修正记录 与 后退图标说明。
# 规则、存档、奖励和状态边界
npm run test
# PC/H5 实际浏览器交互
npm run test:e2e
# 应用、构建配置和测试的类型检查,以及生产打包
npm run buildPlaywright 默认启动或复用 http://127.0.0.1:5180 的测试服务,使用本机 Chrome。测试失败时保留追踪文件,临时结果位于 test-results/。
可定向运行部分测试:
npm run test:e2e -- --grep 'responsive'
npm run test:e2e -- --grep 'complete adventure'截至 2026-10-25 的提交前验证:
| 检查 | 结果 |
|---|---|
| 单元测试 | 25 项通过 |
| 浏览器测试 | 26 项通过 |
| 连续回合模拟 | 10,000 回合,覆盖全部 37 格 |
| 响应式检查 | 320、375、390、560、760、768、1024、1440、1920px |
| 图片完整性 | 55 个图片资源解码通过 |
| 干净安装、类型检查与构建 | 通过 |
| npm 官方安全审计 | 0 项告警,具体结果以重新审计为准 |
覆盖范围包含道具链、后退边界、特殊路线进出、跨圈、领取防重、慢行恢复、到期限制、无骰子、WebGL 降级、动画中切换尺寸、键盘弹窗交互与页内导航。
浏览器结果基于桌面 Chrome。iOS Safari 和 Android WebView 的真机验证尚未完成。
当前演示由页面生成随机点数,并通过 planTurn() 本地计算结果。正式接入时,应由服务端提供可确认的结果与最终状态,页面继续复用事件动画展示。
建议接入以下业务数据:
| 业务入口 | 服务端需要提供或校验的内容 |
|---|---|
| 活动初始化 | 活动起止时间、地图配置、当前位置、资产、道具与已领取记录 |
| 投掷 | 剩余次数、有效点数、逐步移动/传送事件与最终状态 |
| 宝箱领取 | 解锁条件、防重复领取、奖励结果与更新后的资产 |
| 任务系统 | 任务列表、进度、完成条件及领取结果 |
接入时同步调整:
- 用服务端活动时间和用户状态替换
createGame()的本地默认值。 - 用投掷接口结果替换页面中的随机点数与本地结算入口。
- 复用
Map.vue的事件动画,并在完成后应用服务端状态。 - 将
claimMilestone()的本地领奖替换为有幂等校验的领取接口。 - 明确刷新、动画中断、网络重试和多端登录后的恢复方式。
当前 GameEvent 已定义移动、传送、效果提示和跨圈事件,可作为前后端约定的起点。
浏览器正在恢复旧存档。点击 “重新冒险”;若修改了存档结构,还需同步调整版本和恢复逻辑。
只有最终落地格触发效果。传送或后退引起的新落点会继续结算。
必须先停留在页面第 28 格,下一次投掷才会进入。一次移动中途经过入口,会继续沿外围前进。
静态图片是装饰素材,实际结果以下方点数文字及角色步数为准;投掷时的 Spine 动画按有效点数播放。
将 WebP 素材放入 src/assets/adventure/,修改 Box.vue 的 symbol 映射,并同步调整页面详情及玩法说明使用的图标名称。
确认已安装 Google Chrome。若使用其他浏览器,需要调整 playwright.config.ts 中的 channel 和相应浏览器环境。
部分 npm 镜像不提供安全审计接口,可直接运行:
npm audit --registry=https://registry.npmjs.org| 文档 | 内容 |
|---|---|
| PROJECT-REVIEW.md | 提交前检查、依赖修复与验证记录 |
| REFACTOR.md | 重构背景、规则来源及状态/动画设计 |
| design/TEST-REPORT.md | 完整玩法路径与测试结果 |
| design/ASSETS.md | 美术资源清单与生成提示词 |
| design/VISUAL-FIXES.md | 地图、角色、骰子和选中效果调整 |
| design/BACK-ICON.md | 后退图标设计记录 |

