默认参数下的跳跃我量了 12 组。重力 2400、起跳初速 900 时最高点实测 161.3 px理论值 168.8 px差 4.40%。滞空实测 0.733 秒理论 0.750 秒差 2.22%。同一个脚本玩家跑 25 局障碍间距保持默认的 280~520 px中位只有 848 米。把间距改成 700~1200 px中位直接摸到 3000 米的测试上限25 局里 21 局一次没死。劝退还是耐玩不由障碍数量决定由时间窗决定。间距写死在像素上速度却随米数一直涨两个障碍之间的可用时间一直在缩短。在线案例与源码https://atomgit.com/deli007/demo_park/tree/main/codearts-endless-runner-game页面是一个 3.7 万字符的单文件参数面板、物理解算、自检全在里面跑不发任何网络请求。一、为什么值得搞清楚间距是像素难度是时间障碍间距在页面里是一个区间默认 280 到 520 px每次随机取。速度是另一条式子速度 起步速度 每米增速 × 米数。默认 350 起步每米加 0.35。两条式子放在一起就得到一件反直觉的事间距不动时间窗一直在缩。我把三档间距在不同速度下的时间窗算了一遍单位毫秒。当前速度对应米数默认增速280 px 间距400 px 间距520 px 间距350 px/s0 米800 ms1143 ms1486 ms500 px/s429 米560 ms800 ms1040 ms700 px/s1000 米400 ms571 ms743 ms1000 px/s1857 米280 ms400 ms520 ms1350 px/s2857 米207 ms296 ms385 ms1600 px/s3571 米175 ms250 ms325 ms再放一把尺子默认参数下起跳一次人要离开地面 0.733 秒。两个数一摆就清楚了。1000 米处的 280 px 间距只剩 400 毫秒这 400 毫秒里还夹着一整次跳跃。二、准备环境在码道 Web 里把要求写死浏览器打开码道 Web 版https://devcloud.cn-north-4.huaweicloud.com/chat?sourcedmzntgwsfsourceaddmzntgwwbwz登录后就能在对话窗口输入需求不需要装软件。码道有三种使用方式WebUI浏览器对话、TUI终端命令行和桌面 IDEIDE 插件。本文用 WebUI 版演示。需求我写得比较死四块内容直接钉进去。参数面板重力、跳跃初速、基础速度、每米增速、障碍最小间距、障碍最大间距六个输入框都能现场改物理面板最大跳跃高度与滞空时间实测值和理论值并排附相对误差自检面板至少 20 条断言把结果逐条渲染到页面上三种障碍绿色矮箱直接跳过蓝色中箱要算起跳时机红白高梁必须滑铲钻过去最值钱的是第三条。自检和游戏共用同一套公式页面上量出来的数字才算数。三、12 组跳跃实测离散积分比理论低 2% 到 8%我把重力和初速拆成 12 组每组都走游戏自己的起跳与逐帧积分再和解析公式对一遍。重力初速最高点实测理论 v²/2g误差滞空实测理论 2v/g误差2400900161.3 px168.75 px4.40%0.7333 s0.7500 s2.22%240070096.3 px102.08 px5.63%0.5667 s0.5833 s2.86%24001200290.0 px300.00 px3.33%0.9833 s1.0000 s1.67%240050048.0 px52.08 px7.84%0.4000 s0.4167 s4.00%24001600520.0 px533.33 px2.50%1.3167 s1.3333 s1.25%1600900245.7 px253.13 px2.95%1.1167 s1.1250 s0.74%3200900119.1 px126.56 px5.89%0.5500 s0.5625 s2.22%1200900330.0 px337.50 px2.22%1.4833 s1.5000 s1.11%3600900105.0 px112.50 px6.67%0.4833 s0.5000 s3.33%1000600175.0 px180.00 px2.78%1.1833 s1.2000 s1.39%50001400184.4 px196.00 px5.90%0.5500 s0.5600 s1.79%40001000116.7 px125.00 px6.67%0.4833 s0.5000 s3.33%12 组的实测高度全部低于理论值滞空也一样。差值来自积分方式每帧先加速度、再走位移属于半隐式欧拉永远摸不到解析上界。初速越大、重力越小误差越小反方向就放大。初速 1600 那一组误差只有 2.50%重力 4000 那一组是 6.67%。四、快速下落把 0.733 秒的滞空压到 0.2 秒空中按 ↓下落速度会被直接顶到 1400 px/s。我在起跳后的第 N 帧按下 ↓试了 7 组记录这一跳的最高点和总滞空。按 ↓ 的时机最高点总滞空落地用了几帧不按基准161.3 px0.7333 s44第 0 帧0.0 px0.0167 s1第 2 帧28.0 px0.0667 s4第 4 帧53.3 px0.1167 s7第 8 帧96.0 px0.2000 s12第 14 帧140.0 px0.3333 s20第 20 帧160.0 px0.4500 s27第 30 帧161.3 px0.6000 s36这张表解释了「跳完接着钻高梁」为什么成立跳一个矮障碍只要 0.05 秒左右的滞空落地后还剩大半秒可以滑铲。第 14 帧那行是分水岭。等到从最高点开始下落再按 ↓滞空只压到 0.333 秒省不了多少。想省时间就得早按代价是高度矮障碍够用中箱可能就撞上去。五、跑满 1000 米要多久模拟与解析差 0.2% 以内我把 12 组速度参数各跑一遍逐帧累加算模拟耗时公式算解析耗时。起步速度每米增速1000 米处速度模拟耗时解析耗时误差3500350 px/s142.867 s142.857 s0.0067%3500.35700 px/s99.033 s99.021 s0.0124%35011350 px/s67.517 s67.496 s0.0301%35022350 px/s47.633 s47.606 s0.0575%2000.35550 px/s144.533 s144.514 s0.0131%5000.35850 px/s75.817 s75.804 s0.0167%7000.351050 px/s57.933 s57.924 s0.0168%10000.351350 px/s42.883 s42.872 s0.0262%15000.351850 px/s29.967 s29.960 s0.0220%35055350 px/s27.300 s27.269 s0.1130%3501010350 px/s16.967 s16.934 s0.1927%6001.52100 px/s41.783 s41.759 s0.0588%12 组误差全部小于 0.2%并且随增速单调变大。增速越大最后一帧的余量占比越高。默认参数跑满 1000 米要 99 秒。想压成一局 30 秒的短跑把每米增速调到 5 就行。六、障碍间距三档区间各生成 2000 次间距区间实测最小实测最大均值越界次数去重后的取值280 ~ 520 px280.127 px519.967 px398.46 px01994400 ~ 800 px400.138 px799.663 px596.87 px01997700 ~ 1200 px700.196 px1199.869 px947.62 px01995每档 2000 次采样越界 0 次去重后仍有近 2000 个不同取值说明随机是均匀铺开的不是少数几个档位。生成函数的写法很好懂间距 最小间距 随机数 × 区间宽度。真正需要防的是另一头。最小间距填得比最大间距大时面板会拒绝这次输入并保留上一个有效值。两个提示的原文是「minGap(1200) 不能大于当前 maxGap(520)已保留上一有效值 280」和「abc 不是合法数字已保留上一有效值 2400」。七、脚本玩家 25 局默认参数中位 848 米我写了一个脚本玩家规则只有三条遇到矮箱和中箱先算弧线算得出就在撞上之前起跳遇到高梁滑铲空中发现落地来不及就按快速下落。四档增速配三档间距每组各跑 25 局。每米增速间距 280~520间距 400~800间距 700~120003000 米25/25 触顶3000 米25/25 触顶3000 米25/25 触顶0.35848 米661~10121667 米1392~20303000 米21/25 触顶最低 27591346 米233~475671 米485~8731262 米1044~14412205 米159~322374 米285~528669 米562~775括号里是中位之外的最小值与最大值。增速为 0 的三组全部跑满测试上限一局没死。默认增速下间距放宽一档中位从 848 米涨到 1667 米再放宽一档就触顶了。把每米增速从 0.35 提到 2其他参数不动中位从 848 米掉到 205 米。死因统计更直白默认那组 25 局里有 21 局撞击发生在人还在空中的瞬间也就是「还没落地后一个障碍已经追上来」。八、两障碍简化扫描最小可过间距随速度涨我把场景压到极简只有两个障碍中间固定一个间距速度恒定。恒定速度矮箱接高梁中箱接高梁350 px/s80 px80 px500 px/s80 px80 px700 px/s80 px100 px900 px/s120 px120 px1200 px/s200 px200 px1600 px/s240 px260 px350 和 500 那两档80 px 就能过。两个障碍几乎贴在一起时脚本玩家靠「跳过第一个、立刻按快速下落」硬挤过去第四节那张表就是它的底气。1200 px/s 之后不行了。矮箱接高梁的最小可过间距涨到 200 px1600 px/s 时要 240 到 260 px。再对照第七节的默认间距下限 280 px到 1600 px/s 附近随机出来的最短间距已经贴住这个下限过不过得去开始靠运气。九、验证与踩坑页面自检在加载时跑完26 条全过。我把这 26 条逐条读过也在上面补了几轮真跑。第一处坑是我自己挖的。脚本玩家第一版把「已经越过」判断错了只比了障碍的左边缘和玩家身体前缘结果人还压在障碍上就触发了快速下落一头撞在矮箱顶上。那批失败很好认死因全是「空中撞上矮箱」而不是高梁。改成比对碰撞盒后缘之后同样的参数下中位从 443 米涨到 848 米。第二处坑在页面自检自己身上。第 1 条断言要求实测高度与理论值的相对误差小于 6%可离散积分的误差本来就随重力和初速变化。我把重力从 100 一路扫到 10000每 100 一组从 3300 开始误差就过 6%这一条断言会自己判失败。面板允许重力填到 10000。也就是说读者完全可以在合法范围内调出一组参数让页面自己判自己不合格。第三处是个设计判断不算缺陷。碰撞盒比看起来小一圈站立时是 34 × 62 px滑铲时压到 34 × 28 px高梁下面的净空是 60 px。宽容度给得偏大钻梁实际上比看上去轻松。如果你想让它更难把净空改成 40 px 就够了。十、本地复现两条命令加一段控制台脚本把页面存成 index.html起一个静态服务器就能跑。python-m http.server 8000# 浏览器打开 http://127.0.0.1:8000/index.html想自己复现第三节的 12 组跳跃把下面这段粘进浏览器控制台。functionprobe(g,v0){params.gravityg;params.jumpVelocityv0;resetGame();obstacles.length0;lastRight1e9;lastTypenull;staterunning;player.yGROUND-STAND_H;player.hSTAND_H;player.vy0;player.onGroundtrue;startJump(player);lett0,maxH0;while(!player.onGroundt3){step(1/60);t1/60;maxHMath.max(maxH,GROUND-(player.yplayer.h));}constththeoJump();return{g,v0,maxH:maxH.toFixed(2),th:th.h.toFixed(2),err:((Math.abs(maxH-th.h)/th.h)*100).toFixed(2)};}[2400,3200,4000].forEach(gconsole.log(probe(g,900)));这些函数跑在页面脚本的顶层作用域控制台里可以直接调量到的就是我表格里那些数。十一、直接下载试玩在线案例与源码都在这里https://atomgit.com/deli007/demo_park/tree/main/codearts-endless-runner-game目录里有单文件 index.html 和 5 张截图。双击 index.html 就能玩改右边那六个输入框物理面板和游戏行为会立刻跟着变。十二、使用码道的体会需求里写清楚「面板 自检渲染到页面」一轮对话就能拿到能自证的产物。这次我只提了一轮需求生成的页面就带着 26 条断言和两块实测面板。用中文写物理量比写画面有用。把重力、初速、滞空、碰撞盒这些词直接写进需求出来的代码里就能找到对应的常量和函数。它给的默认参数偏激进。每米 0.35 的增速看着温和实测到 1000 米处时间窗只剩 400 毫秒。这个结论是我自己跑出来才敢下的。十三、小结与下一步三句话收尾。跳跃的实测值永远低于解析上界2% 到 8% 的差额是积分方式的固有代价不是写错了。0.733 秒的滞空是这游戏难度的基准尺。任何间距和速度的组合都要按「时间窗够不够落地」来反推。按实测数据默认的 280 px 最小间距在 1600 px/s 附近就贴住极限了。下一步我会做三件事把默认最小间距从 280 提到 400给高梁单独拉长与上一个障碍的距离把自检第 1 条的固定百分比阈值改成按帧长归一化。
阅读完成 · 觉得有帮助?