为什么这个玩法不能只写成演示
商场主题小游戏里,玩家控制一个三爪机械臂,左右移动、按下按钮、爪子下降,抓起毛绒玩具后摇摇晃晃地回到出口。看起来像一个轻松的小游戏,实际上它同时考验物理表现、概率边界、奖品状态和玩家信任。
抓娃娃机不能只靠随机数决定成败,也不能完全靠真实物理。完全随机会让玩家觉得被骗,完全物理又很难保证节奏和商业规则。更稳的做法是把爪子运动、候选奖品、夹持强度、掉落轨迹和保底策略拆成可解释的系统。 本文按一个可上线的小系统拆解,重点不是罗列 Phaser API,而是把输入、规则、表现、调试和内容配置的边界说明白。只要这些边界清楚,后续加关卡、加活动、加存档或加移动端适配,都不会反复推倒。
核心架构
flowchart TD
N1["ClawInput"] --> N2["ClawRig"]
N2["ClawRig"] --> N3["PrizeField"]
N3["PrizeField"] --> N4["GripResolver"]
N4["GripResolver"] --> N5["DropSimulator"]
N5["DropSimulator"] --> N6["PayoutPolicy"]
N6["PayoutPolicy"] --> N7["ResultFeedback"]
N7["ResultFeedback"] --> N8["Phaser 表现层"]
这套结构的关键是让 ClawInput、ClawRig、PrizeField、GripResolver、DropSimulator、PayoutPolicy、ResultFeedback 各司其职。输入层提交意图,规则层产出确定结果,Phaser 层负责把结果演出来。不要让 Tween 完成回调、Sprite 是否可见或某个音效是否播放成为规则事实。规则事实必须能被序列化、测试和回放。
爪子运动要有阶段
抓娃娃机的操作不是简单拖拽一个 Sprite。更可信的流程是 aim、descend、close、lift、return、release。每个阶段都有输入权限和动画约束:瞄准阶段允许左右移动,下降阶段锁定输入,闭合阶段计算夹持,回程阶段根据夹持强度制造轻微晃动。把阶段写成状态机后,玩家快速连点、暂停或切场景都不会打乱流程。
实现时建议先用调试图形把这部分规则跑通,再接正式美术。比如先画命中范围、路径、候选区域、分数来源或状态机阶段,确认数据没有问题后,再加入粒子、音效、镜头和 UI 动效。这样做看似慢,实际会减少大量返工。
奖品不是静态装饰
PrizeField 应记录每个奖品的形状、重量、摩擦、重心、稀有度、当前位置和是否已被抓取。即使用 Arcade Physics 简化,也要给奖品一个逻辑包围盒和抓取点。美术图上看起来被爪子碰到,不等于规则上能抓住;规则命中的边界要在开发模式画出来。
实现时建议先用调试图形把这部分规则跑通,再接正式美术。比如先画命中范围、路径、候选区域、分数来源或状态机阶段,确认数据没有问题后,再加入粒子、音效、镜头和 UI 动效。这样做看似慢,实际会减少大量返工。
夹持判定要混合物理和规则
GripResolver 可以根据爪子闭合时的覆盖面积、奖品重心距离、爪子速度、奖品重量和当前保底状态计算 gripScore。分数高时稳定抓取,分数中等时回程中可能掉落,分数低时立即滑落。这样玩家能通过瞄准影响结果,同时系统仍能控制整体掉落节奏。
实现时建议先用调试图形把这部分规则跑通,再接正式美术。比如先画命中范围、路径、候选区域、分数来源或状态机阶段,确认数据没有问题后,再加入粒子、音效、镜头和 UI 动效。这样做看似慢,实际会减少大量返工。
掉落过程需要表演
很多抓娃娃机的张力来自回程途中摇晃。DropSimulator 不必完全还原物理,可以用 gripScore 决定晃动幅度和掉落检查点。比如起吊、横移中点、出口上方各检查一次,低分奖品更容易在中途滑落。掉落时要播放清楚的原因反馈,而不是突然消失。
实现时建议先用调试图形把这部分规则跑通,再接正式美术。比如先画命中范围、路径、候选区域、分数来源或状态机阶段,确认数据没有问题后,再加入粒子、音效、镜头和 UI 动效。这样做看似慢,实际会减少大量返工。
保底策略要隐形但可审计
如果项目有消耗货币或广告奖励,必须有 PayoutPolicy 记录尝试次数、奖品价值和保底规则。保底不应直接让爪子穿模成功,而是提高夹持强度或降低掉落概率。内部日志要能说明本次是否触发保底,玩家层面则只感到这次抓得更稳。
实现时建议先用调试图形把这部分规则跑通,再接正式美术。比如先画命中范围、路径、候选区域、分数来源或状态机阶段,确认数据没有问题后,再加入粒子、音效、镜头和 UI 动效。这样做看似慢,实际会减少大量返工。
奖品出口要防重复领取
奖品进入出口 sensor 后才算成功。成功事件必须有 prizeId 和 attemptId,避免同一个奖品在抖动中重复触发。领取动画、背包入账和结算弹窗都订阅同一个成功事件。不要在粒子播放完成时发奖励,动画可以跳过,奖励不能丢。
实现时建议先用调试图形把这部分规则跑通,再接正式美术。比如先画命中范围、路径、候选区域、分数来源或状态机阶段,确认数据没有问题后,再加入粒子、音效、镜头和 UI 动效。这样做看似慢,实际会减少大量返工。
调试面板比概率表更重要
开发模式显示爪子阶段、候选奖品、gripScore、保底修正、掉落检查点和最终结果。抓娃娃机玩法很容易被玩家质疑,团队内部必须先能解释每一次成功和失败。没有调试证据,调概率只会变成争论。
实现时建议先用调试图形把这部分规则跑通,再接正式美术。比如先画命中范围、路径、候选区域、分数来源或状态机阶段,确认数据没有问题后,再加入粒子、音效、镜头和 UI 动效。这样做看似慢,实际会减少大量返工。
TypeScript 实现骨架
type ClawPhase = "aim" | "descend" | "close" | "lift" | "return" | "release";
interface Prize { id: string; x: number; y: number; weight: number; rarity: number }
interface GripResult { prizeId?: string; score: number; stable: boolean }
function resolveGrip(clawX: number, clawY: number, prizes: Prize[], pityBoost: number): GripResult {
let best: GripResult = { score: 0, stable: false };
for (const prize of prizes) {
const distance = Phaser.Math.Distance.Between(clawX, clawY, prize.x, prize.y);
const coverage = Phaser.Math.Clamp(1 - distance / 72, 0, 1);
const score = coverage * 0.72 + pityBoost - prize.weight * 0.08 - prize.rarity * 0.03;
if (score > best.score) best = { prizeId: prize.id, score, stable: score > 0.68 };
}
return best;
}
class ClawAttempt {
phase: ClawPhase = "aim";
attemptId = crypto.randomUUID();
next(phase: ClawPhase) { this.phase = phase; }
}
这段代码只展示核心边界。真实项目里还需要配置加载、错误码、事件总线、对象池、存档字段和测试夹具。原则是核心系统不依赖 Scene,Scene 只把玩家输入和系统结果连接到 Phaser 的显示对象。
落地步骤
- 第一步,先把 ClawInput 和 ClawRig 写成纯数据模型,准备两三个最小样例。
- 第二步,给 PrizeField 增加调试可视化,确保中间状态能被看见。
- 第三步,把 Phaser 动画接到规则结果上,而不是让动画反过来提交规则。
- 第四步,补齐失败原因、暂停恢复、重复点击保护和读档恢复。
- 第五步,用正常流程、边界流程、错误配置三类夹具做校验。
常见坑
- 把画面当作状态来源。显示对象可能被对象池回收、被镜头隐藏或被动画临时改值,不能作为规则真相。
- 只为第一关写逻辑。第一关对象少、节奏慢,很多问题不会暴露;内容扩张后,重复触发和配置错误会一起出现。
- 失败反馈太笼统。玩家需要知道是条件不满足、资源不足、路径不可达、输入太晚,还是系统正在等待确认。
- 调试面板缺失。复杂玩法没有中间状态可视化,后期只能靠录屏和猜测定位。
运行时观测
上线后建议记录每次尝试的 aim 时长、候选奖品、gripScore、是否触发保底、中途掉落点和成功奖品价值。若玩家大量在高分情况下失败,说明表现和规则不一致;若低分也频繁成功,奖品经济会失控。 这些指标不一定都要上报到线上,但至少应该在开发版能导出。玩法系统越依赖手感和解释,越需要用数据区分规则问题、表现问题和关卡配置问题。
边界测试与移动端验证
抓娃娃机 在桌面浏览器里跑通,只能说明主流程成立,还不能说明它适合发布。建议为它准备一组专门的边界测试:爪子在下降时暂停、奖品卡在出口边缘、保底触发后玩家跳过动画、连续点击开始按钮、稀有奖品被两个碰撞体同时命中。这些测试不用都做成复杂自动化,至少要有可重复的调试入口,让开发、策划和 QA 能在同一状态下观察同一个问题。每次测试都要记录 attemptId、gripScore、出口 sensor 和奖励入账事件,否则失败只会变成“刚才好像不对”。如果玩法会出现在移动端,还要额外检查触控误差、浏览器切后台、低电量降频、横竖屏切换和音频恢复。很多 Phaser 小游戏不是输在核心规则,而是输在这些边界恢复上。
移动端验证还要关注触摸反馈和文字密度。按钮按下后要有立即响应,即使规则结果需要等待;长文本提示要能在窄屏换行;关键数值不能只靠颜色表达。若系统在低端机关闭粒子、阴影或轨迹后仍能保持同样的规则结果,就说明表现层和规则层边界清楚。发布前把这些用例整理成清单,后续每次改配置、换美术或加活动,都可以快速回归。
发布前检查
发布前至少确认四件事:第一,所有配置引用的 id 都存在;第二,核心状态能存档并恢复;第三,快速输入和跳过动画不会重复结算;第四,低端机可以关闭高成本表现但不改变规则。若系统涉及奖励、货币或排行榜,还要确认事件 id 幂等,避免重复发放或重复扣除。
内容生产与奖品配置
抓娃娃机最容易在内容扩张后失控。新增一个奖品时,不应该只上传贴图,还要填写重量、重心偏移、可抓取半径、稀有度、出口尺寸和推荐摆放层级。运营活动里常会临时加入节日奖品,如果这些字段缺失,爪子可能看起来夹住了,规则却认为没有命中;或者奖品过大,进入出口时重复触发。建议给奖品配置做校验:重心必须在碰撞盒内,稀有度必须有对应保底档位,出口尺寸必须大于奖品最小宽度。调试房间里还可以自动生成一排同类奖品,连续跑一百次抓取,输出成功率分布。这样每次上新奖品前都能知道它是“难抓但合理”,还是“物理形状本身有问题”。
玩家信任的表现细节
抓取失败时,玩家要能看懂原因。爪子没有碰到、夹住但太偏、起吊时滑落、出口前掉落,这四类失败应该有不同动画和音效。若所有失败都只是奖品突然掉下去,玩家会自然怀疑系统暗箱。成功时也不要急着弹奖励,先让奖品落入出口,再播放领取反馈。这个短短的顺序能让规则结果显得可信。
结语
抓娃娃机玩法:爪子物理、抓取判定和保底体验 的价值在于可解释。Phaser 可以把反馈做得很快,但真正决定项目能不能持续扩展的,是规则层是否稳定、表现层是否服从结果、调试层是否能讲清楚每一次失败。把这些边界立住,玩法才能从一个好看的 Demo 变成可维护的系统。
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。