初始提交:三玩法 Web 原型与设计文档

- 玩法一 数字一笔:规则库、求解器、26 关、竖屏触摸 Web 原型、测试
- 玩法二 连连气球:连消畅玩版核心、Web 原型、Cocos 球槽组件与导出脚本
- 玩法三 六边一笔:规则库、求解器、10 关 + 转墙实验包、Web 原型、测试
- 产品文档、UI 设计规范、各玩法 GDD / HANDOFF
- 文档移除全部外部参考作引用;删除玩法二 v0 旧稿
This commit is contained in:
cabinw
2026-09-16 14:30:59 +08:00
commit 8026e4628d
169 changed files with 22162 additions and 0 deletions
+80
View File
@@ -0,0 +1,80 @@
// Painter 的 Canvas 2D 实现。Cocos 版本换成 Graphics + Label 即可,HexBoardView 不用改。
import type { Point } from '../src/geom';
import type { Painter } from '../src/painter';
export class CanvasPainter implements Painter {
private readonly ctx: CanvasRenderingContext2D;
private readonly fontFamily =
'"SF Pro Rounded", "PingFang SC", "Helvetica Neue", "Segoe UI", system-ui, -apple-system, sans-serif';
constructor(ctx: CanvasRenderingContext2D) {
this.ctx = ctx;
}
private path(pts: Point[]): void {
const ctx = this.ctx;
ctx.beginPath();
ctx.moveTo(pts[0].x, pts[0].y);
for (let i = 1; i < pts.length; i++) ctx.lineTo(pts[i].x, pts[i].y);
ctx.closePath();
}
fillPolygon(pts: Point[], color: string, alpha = 1): void {
if (alpha <= 0 || pts.length < 3) return;
const ctx = this.ctx;
ctx.globalAlpha = alpha;
ctx.fillStyle = color;
this.path(pts);
ctx.fill();
ctx.globalAlpha = 1;
}
strokePolygon(pts: Point[], color: string, lineWidth: number, alpha = 1): void {
if (alpha <= 0 || pts.length < 2) return;
const ctx = this.ctx;
ctx.globalAlpha = alpha;
ctx.strokeStyle = color;
ctx.lineWidth = lineWidth;
ctx.lineJoin = 'round';
this.path(pts);
ctx.stroke();
ctx.globalAlpha = 1;
}
line(x1: number, y1: number, x2: number, y2: number, width: number, color: string, alpha = 1): void {
if (alpha <= 0) return;
const ctx = this.ctx;
ctx.globalAlpha = alpha;
ctx.strokeStyle = color;
ctx.lineWidth = width;
ctx.lineCap = 'round';
ctx.beginPath();
ctx.moveTo(x1, y1);
ctx.lineTo(x2, y2);
ctx.stroke();
ctx.globalAlpha = 1;
}
fillCircle(cx: number, cy: number, radius: number, color: string, alpha = 1): void {
if (alpha <= 0) return;
const ctx = this.ctx;
ctx.globalAlpha = alpha;
ctx.fillStyle = color;
ctx.beginPath();
ctx.arc(cx, cy, radius, 0, Math.PI * 2);
ctx.fill();
ctx.globalAlpha = 1;
}
text(s: string, cx: number, cy: number, size: number, color: string, bold = false, alpha = 1): void {
if (alpha <= 0) return;
const ctx = this.ctx;
ctx.globalAlpha = alpha;
ctx.fillStyle = color;
ctx.font = `${bold ? '700' : '500'} ${size}px ${this.fontFamily}`;
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText(s, cx, cy + size * 0.04);
ctx.globalAlpha = 1;
}
}