上周带几个小朋友做项目有个家长凑过来看了一眼屏幕问我“这球怎么还会在坡上弹”我说这不是画出来的动画是算出来的。我们做的是 Scratch 3.0 里的两个小 Demo一个叫“山地足球”一个叫卡通射击打靶。这两个东西单看都不复杂但放在一起能做到一件事把“物理”和“随机”这两类最难讲清楚的概念变成孩子能自己拧的旋钮。山地足球里有重力、有速度、有斜坡法线、有滚动摩擦这些在课本上是一堆公式在 Scratch 里就是几个变量和一行“把 y速度 增加 -0.6”。打靶射击里则全是克隆体、命中判定、生命周期管理——这是 Scratch 里最容易做崩、也最能练出工程习惯的部分。这篇文章写给谁看写给带娃做过 Scr atch、但一做到“球落地弹起来”就卡住的家长写给想把课程从“走迷宫”升级到“做游戏”的老师也写给刚学完基础积木、想认真做一个能玩超过三分钟的作品的学生。我不讲“积木在哪一栏”只讲为什么这么搭、参数怎么定、坑在哪。代码我会贴关键片段但真正的价值在后面几节的调参经验和排查清单——这些是文档里不会写的。1. 先把这两个项目在教什么想明白1.1 为什么把“山地足球”和“打靶射击”配成一对很多人看到“01”这个编号会以为这是一篇合集其实是刻意配对的。山地足球解决的是连续变化的问题球的位置、速度、角度每一帧都在变变量之间互相影响孩子必须理解“先算速度再用速度改位置”这个顺序否则球会抽搐。打靶射击解决的是离散事件的问题什么时候生成一个靶子、什么时候销毁一颗弹丸、命中之后谁来加分这些是“有没有”而不是“多少”。这两类问题的思维方式完全不同但它们在真实项目里必须共存。你在山地足球里进球的那一瞬间要触发音效、要加分、要重置球的位置——这就是一个离散事件被连续运动触发的场景。所以我把两个 Demo 放进同一节课不是为了凑内容量是为了让孩子在一个作品里同时练两种肌肉。实操下来只做过连续运动的同学写计分逻辑时经常忘记“进球后要把球放回中场”只做过打靶的同学写球的运动时会把“位置”和“速度”搞混。两边一起做反而互相提醒。还有一个现实考量山地足球的成品率低。物理调不好球会陷进地面、会穿过墙壁、会原地抖动孩子容易挫败。打靶射击的成品率高十几分钟就能出效果能立刻给正反馈。我把打靶放在后半段就是让它当“情绪缓冲”——前面啃物理啃累了后半段爽一下顺便把克隆体这类高级积木学了。1.2 角色、背景和变量先把骨架摆出来动手之前先画一张表这件事我强烈建议带着孩子一起做。不做规划的后果是做到一半发现要加一个“球出界”的判断结果没有变量可用于是到处补补丁。类别名称类型职责角色球员普通角色左右移动、跳跃、踢球角色足球普通角色受重力、受碰撞、滚动角色球门左/球门右普通角色进球判定角色地形克隆体角色提供可检测的地面角色发射器普通角色固定在底部提供出生点角色准星普通角色跟随鼠标提供方向角色弹丸克隆体角色短生命周期高速移动角色靶子克隆体角色从上方飘落被命中即销毁变量x速度 / y速度仅适用于足球球的速度分量变量重力所有角色统一的重力常数变量进球数 / 分数所有角色计分变量连击所有角色连续命中计数变量游戏状态所有角色菜单/足球/射击/结束注意“仅适用于当前角色”和“适用于所有角色”的区别这是 Scratch 3.0 里最容易被忽略的一个功能。球的速度变量一定要设成“仅适用于足球”否则两个角色写同名的变量会互相覆盖症状是打完靶回到足球球一出来就飞出去。这个坑我见过太多次。1.3 用一个变量把两关串起来“游戏状态”这个变量值我一般就四个菜单、足球、射击、结束。所有角色在“当绿旗被点击”之后第一件事不是开始动而是先判断状态。足球角色的脚本开头长这样当绿旗被点击 把 [游戏状态] 设为 [菜单] 隐藏 重复执行 如果 (游戏状态) [足球] 那么 显示 否则 隐藏这样做的好处是两个玩法各自的初始化脚本可以完整保留切换的时候不会互相污染。我试过用“广播”来切换、不用变量也能跑但广播是“一次性”的角色如果在这一瞬间正在执行一个长循环比如球在飞行中就收不到。变量是“持续”的状态随时可查更稳。还有一个细节切换状态的时候要发一个“清理广播”。足球这边切走要把球的速度清零、位置归位射击这边切走要让所有靶子克隆体和弹丸克隆体删除自己。清理不做干净下次进来就会看到上一局残留的靶子在空中飘孩子会以为程序坏了。2. 山地足球把“斜坡”这件事拆成三块2.1 地形怎么做才能既好看又好判定这是整个项目第一个分水岭。我见过三种做法各有适用场景。第一种造型拼接。做三到四个山坡造型缓坡、陡坡、平台、凹坑用“移到 x y”摆成一条地面线。判定用“碰到颜色 深绿色”或者“碰到 [地形]”。优点是纯积木拖拽一年级也能做缺点是坡度是“硬编码”的球碰到哪个坡你不知道该按哪个角度反弹除非把每个坡做成独立角色一个一个去判断“碰到 缓坡”“碰到 陡坡”角色一多脚本就长得没法看。第二种画笔生成。用一个自定义积木从 x-240 画到 x240y 用几个正弦叠加算出来先画一条深绿色的地面线再落笔填到底部。这个做法的地面是连续的看着舒服但 Scratch 的画笔绘制结果不能直接读坐标判定还是得靠“碰到颜色”。而“碰到颜色”是逐个像素扫描的克隆体一多就卡。第三种列表查表。画面上照样用画笔或造型但同时在后台生成一张“地形高度表”——一个长度 241 的列表第 i 项对应 x -240 (i-1)×2 那一列的地面高度。球要判断自己有没有落地不用去扫描像素直接查表。这个方法最快也最准是我最终采用的方案。为什么选它因为查表是 O(1) 的不管屏幕上有多少个克隆体查询成本都一样。而碰撞检测的成本跟造型复杂度、克隆体数量成正比。做过一个对比测试同样 20 个克隆体用“碰到颜色”那一版在低配笔记本上掉到 22 帧左右换成查表之后稳定 30 帧。生成地形表的脚本大概这样当绿旗被点击 删除 [地形表 v] 的全部项目 把 [i] 设为 0 重复执行 241 次 把 [x] 设为 (-240 (i * 2)) 把 [h] 设为 ((30 * [sin v] of (x / 40)) (18 * [sin v] of ((x / 13) 20)) - 60) 将 (h) 加入 [地形表 v] 把 [i] 增加 1这里的 30 和 18 是两个波形的振幅40 和 13 是波长-60 是整体下移让地形落在屏幕下半部分。这三个参数你可以随便改改完地面形状立刻不一样。我一般让孩子先随便拧看到地面变形了再告诉他们“这就是参数化”。2.2 重力、速度和反弹三个数字定生死很多教程写“把 y速度 增加 -1”然后就没有然后了。但 -1 这个数字背后是有依据的。Scratch 舞台高 360 像素y 从 -180 到 180。球被我做成直径 24 像素半径 12。我希望球被踢一下能跳到大约 100 像素高然后在 1 秒左右落回地面。按匀加速运动的公式跳跃高度 h v₀² / (2g)。假设重力 g 0.6 像素/帧²那 v₀² 2 × 0.6 × 100 120v₀ ≈ 11。所以踢球的垂直初速度设 11 是对的。再看滞空时间。从起跳到落地的时间 t 2v₀ / g 22 / 0.6 ≈ 37 帧。Scratch 默认 30 帧/秒也就是 1.2 秒左右。这个时长我觉得刚好——太短了看不清轨迹太长了等得难受。水平方向我设初速度 10 像素/帧飞 37 帧理论水平距离 370 像素但因为有空气阻力每帧乘 0.99实际大概 310 像素。舞台宽 480刚好能从中间飞到左侧球门附近。这个“刚好够到球门”的感觉是调出来的第一次我设成 6球飞一半就掉了孩子踢十次进不了一次很快就失去兴趣。关键在一个顺序问题先更新速度再用新速度更新位置。把 [y速度] 增加 (重力) // 先算速度被重力改变 把 y 增加 (y速度) // 后动位置被新速度改变如果反过来先移动再加速球在最高点会“愣”一下视觉上能看出来。这是数值积分里最基础的一阶欧拉法小学生不用知道名字但顺序必须记牢。我用一个类比讲速度是你的车速位置是你在哪。你先踩刹车速度变了车才慢慢停下位置变了。如果先算位置再踩刹车那这一帧你还是全速冲过去的。在 Scratch 里还有一个隐藏坑如果你用了“移动 10 步”这种积木方向是角色朝向。足球这个角色的朝向如果被踢球脚本改过重力方向就乱套了。所以球的移动必须用把 x 增加和把 y 增加绝对不能用“移动”。这一点看起来是常识但每年都有孩子掉进去。2.3 让球真的“滚”起来而不只是“滑”球贴着地面滑行和球在滚动观感差得很远。滚动其实就是一个换算球滚过的弧长等于球心移动的水平距离转角 位移 / 半径再转成度数。球的半径 12周长 2π×12 ≈ 75。滚 75 像素转一整圈 360°。所以每移动 1 像素转 360/75 ≈ 4.8°。脚本就一行右转 ((x速度) * (4.8)) 度这里要注意方向。球向右滚x速度为正在屏幕上应该顺时针转Scratch 的“右转”是顺时针所以直接乘正数就对了。如果球向左滚x速度为负“右转”负数就等于“左转”自动正确。这个写法不需要判断符号很省事。但还有个细节只在球落地滚动的状态下才转在空中飞的阶段不该转或者说转得慢一点也行。我一开始没加判断球在空中也疯狂旋转看着像陀螺。后来加了一个“如果碰到地面就转”观感立刻正常。还有一个更隐蔽的问题旋转中心。造型的旋转中心必须设在球心。如果是默认的“造型中心”有时候因为造型不对称球转起来会像在绕着一个看不见的轴晃动。改法是在造型编辑器里点“设置旋转中心”把十字拖到球的正中间。这个操作很多孩子不知道做出来的球转起来一抖一抖的找半天找不到原因。2.4 踢球判定和球员操作的手感球员的操作我做成三段式左右方向键改 x上键跳跃空格踢球。这里有几个取舍要说。移动用“当按下按键”还是“重复执行里判断按键”我坚持后者。用“当按下右移键”这种事件触发的写法按键的重复频率取决于系统的按键重复率而且松开键之后角色会“滑”一段。正确写法是在主循环里判断重复执行 如果 按下 [右移 v] 键? 那么 把 x 增加 6 如果 按下 [左移 v] 键? 那么 把 x 增加 -6这样每一帧的位移是确定的速度稳定。6 这个值也是调出来的太小走得像蜗牛太大走过头。舞台宽 480从中间走到边缘 240 像素6 像素/帧大约 40 帧1.3 秒走完半个屏幕这个速度是有“操作感”的。跳跃检测用 y 坐标还是用地面高度简单版直接判断y -110就认为在地面。但如果地形是起伏的这个判断在坡上就失效了——你站在高处y 还是 -60程序认为你在空中于是按上键不跳。所以必须用查表得到当前 x 的地面高度把 [地面高度] 设为 (第 (四舍五入 (((x位置) 240) / 2) 1) 项) 如果 ((y位置) - (地面高度)) 3 那么 把 [球员y速度] 设为 11这段“索引换算”是整个项目里唯一需要动点脑筋的数学列表的索引从 1 开始x 从 -240 开始步长 2所以 x 对应的索引是 (x240)/2 1。你可以直接告诉孩子这个公式也可以让他们自己推一遍——从 x-240 开始数是第 1 个x-238 是第 2 个这个推理对孩子来说并不难。踢球判定。球员碰到球、并且按下空格就给球一个速度如果 碰到 [足球 v]? 与 按下 [空格 v] 键? 那么 把 [x速度] 设为 ((10) * (朝向)) 把 [y速度] 设为 9朝向变量存的是球员面朝左还是右1 或 -1这样踢球有方向。这里我做了一个小优化加一个 0.15 秒的冷却计时器否则按住空格不松手球会被连续“踢”速度叠得离谱。冷却用“计时器”积木最简单如果 (计时器) (上次踢球时间 0.15) 那么 把 [上次踢球时间] 设为 (计时器) ... 给速度 ...计时器这个积木很好用它从绿旗开始计时精度够、不占变量面板。缺点是它不会自动重置所以切场景的时候要记得“计时器归零”否则切回来冷却时间对不上。3. 打靶射击瞄准、克隆体与命中3.1 准星跟随和发射方向避开 atan 的坑先说明一下玩法屏幕底部中间有个发射装置手里拿的是水弹发射器纯卡通上方飘着圆环靶和气球鼠标移动控制准星按下鼠标发射一颗水弹打中靶子加分。整个画面是明快的卡通风格没有真实武器形象这点在做课程的时候要把握住孩子做出来的东西是要给家长看的。准星最简单——移到 [鼠标指针 v]一行搞定。稍微讲究一点可以加个“平滑跟随”让准星慢慢追上鼠标手感更柔和重复执行 移到 x: ((x位置) (((鼠标的 x 坐标) - (x位置)) * 0.3)) y: ((y位置) (((鼠标的 y 坐标) - (y位置)) * 0.3))0.3 是插值系数越小越“粘”越大越跟手。0.3 我觉得是比较合适的既有阻尼感又不会觉得迟钝。发射方向的计算是重点。有两种写法我推荐第一种。写法一直接用“面向 [准星]”积木当作为克隆体启动时 显示 面向 [准星 v] 重复执行 25 次 移动 18 步 ...这个积木会让角色朝向另一个角色简洁孩子一拖就会。但有个限制如果准星有克隆体它只认原角色隐藏的那个的位置。好在我们准星只有一个没有克隆所以没问题。写法二自己算角度。这里就有个大坑了。Scratch 的[atan v] of (x)积木返回的是 arctan值域只有 -90° 到 90°也就是它分不清第一象限和第三象限。如果你直接写面向 (([atan v] of (((准星y) - (y位置)) / ((准星x) - (x位置)))) - 90) 方向那么当准星在发射器左上方时算出来的角度是错的弹丸会朝右上方飞。正确做法要根据“准星x 是否小于 发射器x”补一个 180° 的修正。另外 Scratch 的方向约定是 0 朝上、90 朝右跟数学上的 0 朝右差了 90°也要减掉。这两件事叠在一起很多成年人第一次写也会错。我的建议是先用“面向”积木跑通后面如果要做“发射器旋转跟随”的效果再去推公式。教学上先出结果再讲原理孩子的接受度会高很多。3.2 克隆体的生与死这里最容易做崩弹丸和靶子都用克隆体。Scratch 有一个硬上限一个角色最多同时存在 300 个克隆体。听起来很多但你按住鼠标不松手试试几秒钟就能撞到上限症状是“打不出子弹了”而且不会有任何报错提示非常迷惑。所以必须自己做配额管理。我用一个“当前弹丸数”变量当按下 [鼠标 v] 键? 如果 (当前弹丸数) (12) 那么 把 [当前弹丸数] 增加 1 克隆 [自己 v]克隆体销毁之前把计数减回来当作为克隆体启动时 显示 重复执行 25 次 移动 18 步 如果 碰到 [边缘 v]? 或 碰到 [靶子 v]? 那么 删除此克隆体 重复执行 1 次 // 走完 25 次还没命中超时销毁 删除此克隆体这里有个非常容易漏的点“删除此克隆体”之后后面的脚本就不执行了所以你在删除之后写的“把当前弹丸数减少 1”根本不会跑。必须写在删除之前如果 到达销毁条件 那么 把 [当前弹丸数] 增加 -1 删除此克隆体这个坑我踩过症状是打了几发之后就再也打不出子弹变量只增不减一直是 12。找了一下午。同样的坑在靶子上也有。靶子的生命周期是这样的当作为克隆体启动时 显示 把 x 设为 (在 (-200) 到 (200) 之间取随机数) 把 y 设为 180 把 [下落速度] 设为 (在 (2) 到 (4) 之间取随机数) 重复执行 120 次 把 y 增加 (0 - (下落速度)) 如果 y位置) (-170) 那么 把 [连击] 设为 0 把 [靶子数] 增加 -1 删除此克隆体 把 [连击] 设为 0 把 [靶子数] 增加 -1 删除此克隆体这里用了“重复执行 120 次”作为超时保险防止某个靶子因为坐标异常一直卡在屏幕上不消失。这种“双重保险”在克隆体项目里是必须的因为你永远不知道哪个边界条件会让克隆体变成幽灵。还有一个更隐蔽的问题隐藏的克隆体不参与碰撞检测。如果你的靶子克隆体启动后忘了“显示”它就在那儿飘着但你打不到。同理子弹如果一直隐藏也打不到东西。这个规则在 Scratch 里没有明说但实测就是这样。3.3 命中判定的三种写法我为什么选“碰到”Scratch 里做命中判定大概三条路。第一种碰到角色。子弹克隆体里写如果 碰到 [靶子 v]? 那么。这个积木检测的是子弹和靶子角色所有克隆体的像素级碰撞。像素级的意思是造型里的透明区域不算所以打靶子边上的空隙不会误判这个体验是对的。而且它不需要额外的循环和列表写起来最省事。第二种距离判定。理论上更可控但 Scratch 里有个陷阱(到 [靶子 v] 的距离)这个积木对克隆体角色而言取的是到原角色的位置距离不是到各个克隆体。靶子原角色通常藏在屏幕外所以这个距离永远是几百判定永远不成立。要用距离判定必须自己维护一个“靶子坐标列表”用列表存每个克隆体的 x、y然后子弹去遍历算平方和。这个做法当然能做但对小学阶段的孩子来说太重了。第三种广播加坐标比对。靶子自己判断有没有被击中比如用“碰到 弹丸”反过来写命中后广播。逻辑上可行但这样一来每个靶子克隆体每帧都要跑一次碰撞检测8 个靶子就是 8 次比子弹那边检测贵得多子弹只有 12 颗而且每颗很快就消失。性能上不划算。所以我的选择是子弹那边检测靶子被动等待。子弹数量少、生命周期短检测成本低。这个“谁动谁检测”的原则在游戏开发里很通用值得跟孩子讲一句。命中之后靶子怎么知道用一个广播当接收到 [命中 v] 把 [分数] 增加 (1 (连击)) 把 [连击] 增加 1 把 [靶子数] 增加 -1 删除此克隆体但广播是发给所有角色的所有靶子都会收到都会执行“删除此克隆体”——结果是一枪打掉全部靶子。这个 bug 非常经典。解决办法有两个一是在广播里带上坐标信息用列表传参靶子收到后判断自己的坐标是否接近二是干脆不用广播让子弹自己去“通知”它碰到的那一个。我实际用的是第一个思路的简化版子弹命中后把“命中点x”“命中点y”写入两个全局变量然后广播。靶子收到广播后判断自己离命中点够不够近比如小于 30 像素够近才销毁自己当接收到 [命中 v] 如果 (((x位置) - (命中点x)) * ((x位置) - (命中点x)) ((y位置) - (命中点y)) * ((y位置) - (命中点y))) (900) 那么 ... 销毁 ...900 是 30 的平方。用平方比较可以省掉开根号虽然 Scratch 跑这点运算毫不在意但这个写法是个好习惯。实测下来这个方案稳定不会误杀。3.4 计分、连击和难度爬坡的设计分数好做加一就行。但“连击”这个东西能明显提升可玩性而且实现起来特别简单命中加 1靶子掉出屏幕清零得分时用1 连击而不是 1。孩子第一次看到连击数字跳起来的时候那种兴奋比单纯加分强烈得多。难点在“难度爬坡”。一款游戏如果从头到尾一个速度玩两分钟就腻。我用的方案是按分数分段分数区间靶子生成间隔下落速度范围同时存在上限0 - 91.2 秒2 - 3410 - 240.9 秒2.5 - 4625 - 490.7 秒3 - 5750 以上0.5 秒4 - 68这张表是根据试玩调出来的。第一版我把 50 分以上的生成间隔设成 0.3 秒结果靶子多到屏幕糊住根本没法瞄准孩子直接放弃。0.5 秒是“忙但不慌”的临界点。生成间隔用“等待”积木实现最简单但要小心如果你在“重复执行”里写“等待 1.2 秒 → 克隆自己”那么整个循环的节奏就是 1.2 秒一次改速度的时候只要改一个数。但如果你用计时器做精确控制会更平滑。我一般先用“等待”跑通再说。4. 从空舞台到能玩完整的五步走4.1 第一步素材、舞台和变量一次配齐素材来源我一般给孩子三个选项自己用内置画笔画最锻炼造型能力、用内置素材库里的球和圆环最快、从网上下载再导入要注意版权我一般要求注明来源。山地足球的地形我用画笔程序生成不依赖任何外部图片好处是文件小、跨设备打开不会丢资源。变量面板一次性建好别边做边加x速度、y速度、重力、进球数 → 仅适用于足球 球员y速度、朝向 → 仅适用于球员 分数、连击、靶子数、弹丸数 → 适用于所有角色 命中点x、命中点y、游戏状态 → 适用于所有角色 地形表 → 列表适用于所有角色顺序上先做“足球”再做“射击”。理由前面说过足球难先啃射击爽收尾。4.2 第二步让球先动起来不加任何地形这一步的目标只有一个球能跳、能落、能滚。地面就是一个固定高度 y -120。当绿旗被点击 把 [x速度] 设为 0 把 [y速度] 设为 0 把 [重力] 设为 0.6 重复执行 把 [y速度] 增加 (0 - (重力)) 把 y 增加 (y速度) 把 x 增加 (x速度) 如果 (y位置) (-120) 那么 把 y 设为 -120 把 [y速度] 设为 ((0 - (y速度)) * (0.55)) 把 [x速度] 设为 ((x速度) * (0.98)) 如果 (x位置) (-230) 那么 把 x 设为 -230 把 [x速度] 设为 ((0 - (x速度)) * (0.8))0.55 是反弹系数0.98 是地面摩擦。这两个数可以让孩子自己试把 0.55 改成 1球会永远弹下去改成 0.1球像铅块一样砸下去不动。这个“拧参数看变化”的过程比讲十遍能量守恒都有用。我通常让他们把系数从 0 到 1 每隔 0.1 试一次记录球弹跳的次数画成一张表——这就是最原始的科学实验。顺手把滚动加上如果 (y位置) (-118) 那么 右转 ((x速度) * (4.8)) 度4.3 第三步换成真实地形调碰撞把固定地面换成列表查表。核心就三件事算当前地面高度、算左右两侧的坡度、按坡度反弹。定义 取样 (sx) 如果 (sx) (-240) 那么 把 [取样结果] 设为 (第 1 项) 否则 如果 (sx) (240) 那么 把 [取样结果] 设为 (第 241 项) 否则 把 [索引] 设为 ((四舍五入 (((sx) 240) / 2)) 1) 把 [取样结果] 设为 (第 (索引) 项)注意 Scratch 的自定义积木不能“返回值”只能把结果写进一个变量这是它和普通编程语言最大的不同。孩子第一次接触这个概念会懵我的解释是“这个积木不会给你答案它会帮你把答案写在黑板变量上你再去黑板上看。”反弹那段我给你完整的把 [地面h] 设为 (取样 (x位置)) 把 [左h] 设为 (取样 ((x位置) - 6)) 把 [右h] 设为 (取样 ((x位置) 6)) 把 [斜率] 设为 (((右h) - (左h)) / 12) 把 [角度] 设为 ([atan v] of (斜率)) 如果 ((y位置) - 12) (地面h) 那么 把 y 设为 ((地面h) 12) 把 [vn] 设为 (((0 - (x速度)) * ([sin v] of (角度))) ((y速度) * ([cos v] of (角度)))) 把 [vt] 设为 (((x速度) * ([cos v] of (角度))) ((y速度) * ([sin v] of (角度)))) 把 [vn] 设为 ((vn) * (-0.55)) 把 [vt] 设为 ((vt) * 0.92)) 把 [x速度] 设为 (((0 - (vn)) * ([sin v] of (角度))) ((vt) * ([cos v] of (角度)))) 把 [y速度] 设为 (((vn) * ([cos v] of (角度))) ((vt) * ([sin v] of (角度))))这段是法线分解。原理不复杂把速度拆成“垂直于坡面”和“沿着坡面”两个分量垂直的反弹并衰减沿坡面的保留并加一点摩擦再合成回去。好处是球在陡坡上会明显地“往下溜”在缓坡上会平平地弹出视觉上很像真的。但是说实话对大部分孩子来说这段太难。我给的分层方案是能理解的就上法线反弹理解不了的用简化版——碰到地面就把 y速度 反向乘 0.6同时判断如果坡度大于 20° 就把 x速度 也加上一个“往下坡方向”的加速度。简化版的效果打七折但能跑而且孩子知道自己在干什么。调参的时候有个必试清单把球放在最陡的坡上让它自由滚看能不能滚到底把球从最高处丢下去看会不会陷进地里让球高速撞墙看会不会穿过去。这三个测试我叫它“三连测”每次改完参数都要跑一遍。4.4 第四步接上射击模块的公共接口两个玩法并存最怕的是互相干扰。我定的规则是所有跨模块的通信只走全局变量和广播角色之间不直接读写对方的私有变量。公共接口就四个接口类型谁写谁读游戏状态全局变量主控 / 按钮所有角色开始射击广播主控发射器、靶子、计分板结束射击广播主控所有克隆体清理用返回菜单广播主控所有角色发射器的初始化脚本当接收到 [开始射击 v] 把 x 设为 0 把 y 设为 -150 显示 把 [分数] 设为 0 把 [连击] 设为 0 把 [靶子数] 设为 0 把 [弹丸数] 设为 0 计时器归零这里“计时器归零”很关键因为靶子的生成节奏我用的是计时器控制。如果不归零进入射击关的一瞬间计时器可能已经是 50 秒了第一批靶子会瞬间刷出来一堆。清理脚本同理收到“结束射击”广播后所有克隆体删除自己当接收到 [结束射击 v] 如果 (是克隆体?) [真] 那么 删除此克隆体Scratch 没有“是克隆体”这个积木怎么判断一个常用的技巧是克隆的时候给克隆体设一个私有变量“我是克隆体 1”原角色是 0。或者更简单——原角色本身隐藏克隆体启动时显示那么“显示”这个动作只有克隆体做过。不过 Scratch 里没法查询“我是不是克隆体”。所以还是老老实实用变量标记。4.5 第五步联调和性能自检联调阶段最容易暴露的问题不是逻辑错误是帧率。Scratch 3.0 的帧率不是固定的场景复杂会掉帧而掉帧会让所有“每帧移动 N 步”的物理计算变得不准——原本 1 秒落地的球掉帧之后要 1.5 秒。自检的土办法在舞台角落放一个“帧率计”每帧把“帧计数”加 1每秒算一次差。低于 25 就该优化了。我遇到的几个典型掉帧元凶地形用“碰到颜色”而不是查表检测成本极高。靶子克隆体上限设成 20屏幕上同时存在十几个每个都在跑碰撞。用了“图章”每帧盖章画轨迹图层越叠越厚。背景音乐同时播放多个克隆体的音效音频通道打满。优化顺序我一般是先砍克隆体数量再换检测方式最后才动画面效果。因为画面是给孩子看的砍了会心疼克隆体是看不见的砍了没人发现。5. 踩坑实录和排查清单5.1 球陷进地形里出不来这是最高频的问题症状是球卡在坡里不动或者疯狂抖动。原因一位置修正不彻底。有的写法是“碰到地面就把 y速度 反向”但没把球的位置推出来。球的位置还在地面以下下一帧又“碰到”又反向于是原地抽搐。正确做法是先纠正位置把球顶到地面之上再改速度。原因二一步跨太远。球的速度是 20 像素/帧地形采样步长是 2 像素一帧之内球可能直接“穿过”一个尖角落到了地形内部。这个叫隧穿。解决办法是限制最大速度或者把一帧的移动拆成几个小步比如每帧移动 3 次每次移动 1/3 距离。我一般把速度上限卡在 18配合 2 像素的采样精度基本不会穿。原因三斜坡的采样点算错了。如果索引换算时忘了 1或者 (x240)/2 用了向下取整而不是四舍五入采样点会整体偏移一列球在坡顶附近就会判断错误。排查方法很简单在球上显示“地面h”这个变量肉眼对比球底和地面线的位置差得明显就是公式错了。5.2 克隆体越积越多导致卡顿前面说过 300 的上限和配额管理。但还有一种更阴的情况克隆体删除了但计数没减。你以为是配额满了其实是计数虚高。排查套路在舞台上用“说”积木实时显示“靶子数”和“弹丸数”然后什么都不做观察这两个数字。正常情况下弹丸数应该在 0 到 12 之间来回跳靶子数在 0 到 8 之间。如果弹丸数一直往上涨不回落说明有某条分支删除了克隆体但没减计数。把“减计数”写在“删除克隆体”前面是唯一的正确姿势。还有一种情况是克隆体自己又克隆自己形成指数增长。Scratch 里“当作为克隆体启动时”这个帽子积木下面如果又写了“克隆自己”那就是灾难。我见过一个孩子的作品跑十秒之后整个编辑器卡死最后发现是子弹克隆体里藏了一句“克隆自己”。5.3 命中判定失灵的各种花样我把遇到过的整理成一张表遇到对不上号的就挨个试。现象可能原因处理子弹穿过靶子不判定靶子克隆体没显示在克隆体启动时加“显示”一枪打掉全部靶子用了广播且没有坐标过滤加“命中点”距离判断打中边缘不判定造型有透明边距编辑造型裁掉透明区域明明没碰到却加分造型太大 / 旋转中心偏移检查造型尺寸和旋转中心子弹打在发射器上就没了发射器也在“碰到”检测范围内发射器加忽略判断或用颜色区分其中“造型有透明边距”这条最坑。Scratch 的碰撞是像素级的透明像素不算所以如果靶子造型是“一个大圆环 周围一圈透明留白”那留白部分真的打不中。解决办法在造型编辑器里用裁剪工具把透明边去掉。这个操作孩子不太会得教一次。5.4 常见问题速查表问题快速定位方法修复球不动显示 x速度、y速度检查重力是否被设成 0球飞出屏幕不回来显示 x位置检查边界反弹是否被注释掉玩家跳不起来显示“y位置 - 地面高度”检查采样索引靶子不生成显示“靶子数”检查“靶子数 上限”的判定分数不涨显示“命中点x/y”检查广播是否发出、靶子是否收到切换关卡后状态错乱显示“游戏状态”检查清理脚本是否执行音效卡顿关闭所有克隆体音效音效只让原角色播放5.5 给孩子的调试习惯比技术更重要最后说点软的。这些年带下来我发现孩子卡住的时候 90% 不是不会积木是不知道该看哪里。所以我强制要求三件事。第一变量要“可视化”。重要的变量前面打勾让它在舞台上显示出来。球飞出去了看一眼 y速度 是正还是负就知道是重力方向搞反了还是别的。这个动作要养成肌肉记忆。第二一次只改一个东西。孩子改 bug 的时候喜欢一次改五处改完发现好了也不知道是哪一处修好的改完发现更糟也不知道是哪一处弄坏的。我要求他们改一处、跑一次、记一句。这个习惯带到大一点的项目里价值翻十倍。第三先备份再折腾。Scratch 的“保存副本”功能要教会。调物理参数的时候调出一个满意的版本立刻存副本再继续折腾。不然调着调着发现“刚才那个手感更好”已经回不去了。我个人在实际操作里的体会是这两个 Demo 看上去是“做游戏”其实练的是“拆问题”。山地足球里把“球怎么动”拆成“速度怎么变”和“位置怎么变”两步打靶里把“怎么打中”拆成“谁负责检测”和“谁负责销毁”两个角色。孩子能自己完成这个拆分比做出一个多漂亮的成品重要得多。至于参数——重力设 0.6 还是 0.8反弹系数 0.55 还是 0.6其实没有标准答案。我一般让他们做一个“参数实验记录表”把每次改的数字和观察到的现象写下来三五行就行。写着写着他们就会自己发现“原来那个数字是有原因的”那一刻比任何讲解都值。
企业数字化 ERP 产品动态
相关推荐
智能优化算法改进DELM在工业预测中的应用 1. 项目背景与核心价值在工业预测和数据分析领域,深度极限学习机(DELM)因其独特的网络结构和快速训练特性正获得越来越多的关注。但传统DELM存在两个致命缺陷:随机初始化的权重容易导致模型收敛不稳定,以及缺乏有效的参数优化机制使得模型容易… · 2026/11/5 2:57:21
Blender视图导航改3ds Max手感:键位、轴心与冲突排查 我从三维软件的视图导航里挑了个最不起眼的动作来折腾:按住鼠标中键拖一下。在 3ds Max 里,这一下是把视图平移,画面像被人推着走;到了 Blender,同一个动作直接把场景转了起来,镜头绕着物体打转。做建筑的、… · 2026/11/4 16:45:04
智能优化算法与深度极限学习机的融合应用 1. 深度极限学习机与智能优化算法融合背景深度极限学习机(Deep Extreme Learning Machine, DELM)作为传统极限学习机(ELM)的扩展版本,近年来在时间序列预测、图像分类和回归分析等领域展现出独特优势。与传统深度学习模… · 2026/11/4 13:14:52
EasyU优启通PE维护盘实战指南:制作、启动与核心操作全解析 1. 为什么一个PE维护盘值得单独拿出来聊装系统、修引导、救数据、清病毒,这些活儿但凡干过几次的人都知道,真正决定效率的往往不是你手速多快,而是手边那套PE环境靠不靠谱。EasyU(圈内习惯叫优启通)就是在这个场景下被… · 2026/11/5 16:49:35
Rust原来很简单二:所有权系统 Part 2 核心篇 —— 所有权系统
本篇覆盖 第 6 ~ 10 章:所有权、借用与引用、生命周期、字符串与切片、内存模型。 ⚠️ 这是全书最重要的一篇。 把它吃透,Rust 从此“很简单”;跳过它,你会处处碰壁。请慢下来,动手敲每一段代码。 第 6 章 所有权:Rust 的第一性原理
6… · 2026/11/5 16:26:08
基于Hadoop+Spark的民宿智能推荐系统设计与实现 1. 项目概述:大数据时代的民宿智能推荐这个毕业设计项目融合了当前大数据领域最主流的技术栈,构建了一个完整的民宿推荐系统解决方案。作为一名长期从事大数据系统开发的工程师,我认为这个选题非常贴合实际应用场景——随着旅游业的复苏和共享… · 2026/11/5 14:59:25
ADS122U04IPWR+STM32F427ZI工业级温度测量信号链设计 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/5 14:10:41
UART接口24位ADC搭配RS485,解决远距离温度监控难题 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/5 14:10:41
UART接口24位ADC搭配PIC18F46K22的RTD与热电偶测温实践 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/5 14:10:41
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/5 10:27:59
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/5 10:28:03
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/5 10:28:07
多智能体协作系统架构设计与实践优化 1. 多智能体协作系统的时代背景2017年Transformer架构的诞生彻底改变了人工智能的发展轨迹。当行业还沉浸在单一大模型带来的性能突破时,Anthropic的研究团队已经将目光投向了更前沿的领域——如何让多个智能体通过协作产生超越单体能力的系统智能。这就像交响乐团中… · 2026/11/5 0:00:39
大模型系统性入门实战指南:从原理到部署微调的全链路路线 提到大模型的系统性入门,我先说个结论:这事真不难,难的是资料太散、路线太多,今天这个框架明天那个工具,新人很容易在“收藏夹吃灰”和“教程看到一半放弃”之间反复横跳。市面上关于大模型的内容少说有几十万篇&#… · 2026/11/5 0:00:39
鸿蒙文件系统沙箱机制解析与开发实践 1. 鸿蒙文件系统沙箱机制深度解析作为一名从Android转向鸿蒙开发的工程师,我深刻体会到文件系统访问是迁移过程中最先遇到的"拦路虎"。在Android上运行良好的文件读写代码,在鸿蒙真机环境却频频出现文件写入后神秘消失或直接抛出权限拒绝错误的… · 2026/11/5 0:00:39
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/5 10:27:59
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/5 10:28:03
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/5 10:28:07