返回
玩法拆解 · 代码 · Demo
物理射击

足球射门:把点球大战压缩成一张「角度 × 力度」判定表

5 轮点球对决的规则骨架:瞄准角与力度被切成必偏区、风险区、甜区三段判定带,用概率裁决进球、被扑与射偏。文末附可试玩 demo。

0

玩法拆解

规则骨架
  • 5 轮点球(maxRounds=5),每射一脚轮次 +1,轮次用尽进入结算
  • 判定三态:goal / save / miss,仅进球记分,AI 对手占位(aiScore 恒 0)
  • 胜负=双方进球数对比,平局允许;进 1 球即锁定胜利
行为循环
  • 瞄准(aim.update 写入角度/力度,角度钳制 ±80°)→ 射门(shot.take)
  • executeShot 按三段判定带裁决 → shot.result 回传 → 进球记分 + 音效
  • resetScene 重置场景进入下一轮;round>5 时 emit game.over 转结算
实现要点
  • 判定模型下沉在私有模块 football/football-model,甜区被扑率=0.25+|angle|/200
  • 状态机与记分由 v2-kernel LogicGraph 驱动:boot→menu→gameplay→result
  • 当前构建为规则层骨架:瞄准控件与 3D 场景未上屏,判定链路完整闭环

关键代码

三段判定带(football/football-model)
js· 10 行
玩法行为:本文属于「射击」母型瞄准、命中与资源管理形成闭环;核心是命中反馈与风险收益。
查看母型详解

Demo

点「开始游戏」进入点球对决,连点「射门」完成 5 轮判定——每脚射门的角度与力度由模型层概率表裁决进球/被扑/射偏,进 1 球即胜。

足球射门:把点球大战压缩成一张「角度 × 力度」判定表 Demo
游戏加载中…

文章

足球射门:把点球大战压缩成一张「角度 × 力度」判定表

一句话本质

表面上是滑动瞄准、蓄力射门的 3D 点球大战;落到规则层,它的核心其实是一张二维判定表——把「瞄准角度 × 射门力度」切成必偏区、风险区、甜区三段,用概率裁决进球 / 被扑 / 射偏,再套一个 5 轮记分循环定胜负。

行为循环

  1. 进入对决:菜单点「开始游戏」→ gameplay,比分归 0、轮次归 1、gamePhase=aiming,哨声开场。
  2. 瞄准:aim.update 事件写入角度(钳制在 ±80°)与力度(0–100)。
  3. 射门:shot.take 触发 executeShot,gamePhase 切到 shooting,按判定表当场得出 goal / save / miss。
  4. 裁决反馈:shot.result 回传结果,进球则玩家记分 +1,并播放对应音效(射门 / 进球 / 射偏)。
  5. 轮次推进:round +1;未满 5 轮则 resetScene 回到 aiming 继续下一脚,满 5 轮 emit game.over。
  6. 结算重开:result 状态对比双方比分判出胜 / 负 / 平,可「再来一局」重置重赛,或「返回菜单」。

文末嵌了本作的可试玩 demo,可以直接把这条循环点一遍。

原语标注

  • Aim(angle, power) —— 写入瞄准参数,角度钳制 [-80°, 80°]
  • Shoot() —— 确认射门,锁定输入进入 shooting 阶段
  • Judge(angle, power) —— 三段判定带概率裁决,输出 goal / save / miss
  • Score() —— 进球方记分(本样本仅 goal 记分)
  • RoundNext() —— 轮次 +1,到顶触发 game.over
  • ResetScene() —— 每轮结束重置射门场景数据

参数速查表

| 参数 | 说明 | 默认值 | 单位 | |------|------|--------|------| | maxRounds | 点球轮次上限 | 5 | 轮 | | shotAngle | 瞄准角度(钳制 ±80) | 0 | 度 | | shotPower | 射门力度 | 80(首轮) | 0–100 | | 必偏区 | power ≤ 10 或 |angle| > 75 → 100% 射偏 | — | — | | 风险区 | |angle| > 55 或 power > 95 → 60% 偏 / 40% 被扑 | — | — | | 甜区被扑率 | 0.25 + |angle| / 200(进球率 47.5%–75%) | 0.25–0.525 | 概率 | | 蓄力周期 | 模型层预留的三角波蓄力(0→100→0) | 1.2 | 秒 |

设计取舍

  1. 概率表代替刚体弹道。设计原案是 Three.js 3D 物理弹道 + 守门员扑救;落地时收敛为数据层概率模型。好处是同输入同分布、可单测、可调参,判定可信度高;代价是没有真实弹道动画。对「5 轮定胜负」的短局来说,判定公式的手感曲线比弹道逼真度更影响体验。
  2. 三段判定带的形状。必偏区惩罚极端输入(力度过轻或角度超过 75°);风险区给「爆杆 / 大角度」高方差(六成射偏);甜区奖励稳妥射门。值得注意的一个反直觉设计:甜区内被扑率随 |angle| 上升——瞄得越刁钻越容易被扑,相当于给「瞄死角」标了价格,逼玩家在稳妥与角度之间做权衡。
  3. AI 对手占位。数据模型里 aiScore 与三相 winner(player / ai / draw)是齐全的,但当前构建 AI 不出脚(aiScore 恒 0),玩家进 1 球即胜、0 球平局。这是刻意的切分:先保证「可结算、可重开」的规则骨架成立,AI 决策(原案中的 football-ai 模块)留作下一步补齐。
  4. 规则先行、表现占位。当前 demo 是规则层骨架:瞄准控件、比分文字绑定、3D 球场都还没上屏,但状态机、判定、记分、重开已完整闭环。这种切法让自动化测试能先锁死规则正确性,表现层之后再迭代,不会被美术进度卡住。

实现要点

  • 模块划分:复用 common-ui(按钮事件绑定与 boot 自动推进),新建私有模块 football/football-model 承载射门判定、场景重置与音效钩子。设计原案曾计划拆出 aim-controller / football-ai 三个新模块,落地时收敛为一个模型模块,降低接口数量。
  • 判定函数:executeShot 读取 shotAngle / shotPower,走三段 if 分支写回 shotResult,并自触发 shot.result(带 _ovoInternal 标记防止与外部事件重复计数)。
  • 状态机驱动:规则被编译进 v2-kernel 的状态逻辑图——onEnter 初始化记分板,onEvent(aim.update / shot.take / shot.result) 做分支,round > maxRounds 时 emit game.over 切到 result,restart / return 两个出口都先 resetScene 清场。
  • 事件契约:aim.update {angle, power}、shot.take、shot.result {result, angle, power}、game.over、game.restart、menu.return,全部走内核事件总线。
  • 已知缺口(试玩时可见):gameplay 皮肤目前只有「射门」按钮可交互,瞄准参数接口预留在模型层(aim.update);比分与轮次文字是静态占位,result 文案尚未做数据绑定。这些不影响判定链路——demo 里从菜单到 5 轮射门再到结算的每一步,都是真实规则在跑。

这个样本的价值不在完成度,而在于它展示了一种可复制的开发顺序:先把判定模型做成可验证的规则骨架,再往上长表现层。点球、高尔夫挥杆、篮球罚球,凡是「两个输入参数决定一次出手结果」的玩法,都可以套这张判定表的结构。