简介:这份资源是面向Scratch编程学习者与少儿编程教学者的超级马里奥游戏源码,适合已掌握基础积木操作、希望进阶理解完整项目结构的中级学习者。项目共设计至少5个难度递增的关卡,每关包含悬崖、小怪等至少3种陷阱,人物支持键盘左右移动与空格跳跃,触碰陷阱即死亡重来,并设有3条命限制,全部耗尽则从第一关重新开始;此外还加入隐藏道具,拾取后可击毁陷阱,配合背景音乐与跳跃、死亡等音效,整体玩法完整。压缩包为rar格式,共121个文件,其中108个png图片用于角色与场景素材,12个wav音频负责音效与配乐,另有1个json工程文件承载项目逻辑,包体约15.15MB。目前已有6124人学习下载,读者可借此研究关卡组织方式、角色状态切换与音效触发机制,并参考素材管理思路,快速搭建属于自己的横版闯关游戏。
1. 从零拆解一个用 SCRATCH 开发的超级马里奥源码:它到底能跑出什么
很多人第一次看到“SCRATCH 开发的超级马里奥源码”这个标题,脑子里冒出的第一个念头是:Scratch 不是给小孩拖积木玩的吗,怎么可能做出马里奥这种带卷轴、带碰撞、带惯性跳跃的游戏?我当初也是这么想的,直到自己动手把一个能跑通的版本拆开看了一遍,才发现这里面的门道比想象中深得多。Scratch 的舞台坐标、角色克隆、广播机制,配合一套手写的物理变量,确实能还原出接近红白机手感的水管工跳跃。这套源码真正解决的不是“做个游戏”,而是“用最直观的方式把横版卷轴平台游戏的核心机制讲清楚”——重力怎么算、碰撞怎么判、镜头怎么跟、敌人怎么巡逻。它适合两类人:一类是想教学生理解游戏循环的老师或家长,另一类是刚入门游戏开发、想找个能看见每一行逻辑的练手项目的新手。你不需要装引擎,不需要配环境,打开浏览器就能改。
2. 横版卷轴平台游戏在 Scratch 里怎么立住:坐标系、重力与碰撞
2.1 为什么 Scratch 的坐标系天然适合做平台跳跃
Scratch 的舞台是一个 480×360 的矩形,原点在正中央,x 轴向右为正,y 轴向上为正。这个坐标系和大多数 2D 游戏引擎的屏幕坐标系(原点在左上角,y 轴向下)是反的,但恰恰因为 y 轴向上,重力的方向就是负 y,跳跃就是给 y 速度一个正值,物理直觉非常顺。角色位置用 x、y 两个变量表示,大小用“大小”属性控制,方向用“方向”属性控制。做马里奥这种横版游戏,你只需要关心 x 和 y 两个自由度,不需要处理 z 轴,也不需要处理旋转矩阵。舞台的边界是硬限制,角色碰到边缘会停住,这省掉了一部分边界检测的代码。但要注意,Scratch 的“碰到边缘就反弹”积木会反转方向,做平台游戏时不能用它,得自己写边界判断。
2.2 重力、跳跃与惯性:三个必须手写的变量
Scratch 没有内置物理引擎,所有运动都得自己算。核心变量有三个:y速度、重力、跳跃力度。每一帧(也就是每次“重复执行”循环)做三件事:先把y速度减去重力,再把角色的 y 坐标增加y速度,最后检测是否落地。落地时把y速度设为 0,并标记是否在地面为真。跳跃时,如果是否在地面为真,就把y速度设为跳跃力度,同时把是否在地面设为假。水平方向同理,用x速度控制,按下左右键时给x速度一个加速度,松开时给一个摩擦力让它慢慢停下来,这样才有马里奥那种“刹不住车”的惯性感。参数上,我一般把重力设成 1.2 到 1.5,跳跃力度设成 14 到 16,摩擦力设成 0.85 左右。这些数值不是固定的,调的时候看手感,跳得太飘就加大重力,跳得太矮就加大跳跃力度。
2.3 碰撞检测:用“颜色碰到颜色”还是“角色碰到角色”
Scratch 提供两种碰撞检测:碰到颜色和碰到角色。做平台游戏的地面碰撞,常见做法是给地面角色一个纯色(比如黑色),然后让马里奥检测“碰到黑色”。但纯色检测有个坑:如果地面有装饰纹理,颜色不统一,检测就会漏。更稳的做法是用“角色碰到角色”,把地面拆成一个个不可见的碰撞块角色,马里奥检测“碰到碰撞块”。这样地面可以画得花里胡哨,碰撞块只负责逻辑。代价是角色数量变多,克隆体管理要小心。我一般会在地面角色里用克隆体生成一排碰撞块,每个克隆体固定位置,不参与运动,只做碰撞检测。检测到碰撞后,还要判断是从上方落地还是从侧面撞墙:如果马里奥的 y 速度小于 0 且脚底位置高于碰撞块顶部,就是落地;否则就是撞墙,把 x 速度清零。
2.4 镜头跟随:让舞台“滚动”起来的两种方案
横版卷轴的核心是镜头。Scratch 的舞台不会滚动,所以有两种方案:一种是移动所有背景和角色,让马里奥看起来在前进;另一种是移动马里奥,背景不动,但背景足够宽。第一种方案更接近真实卷轴,但实现麻烦,每个角色都要跟着镜头偏移。第二种方案简单,但舞台只有 480 宽,背景画不了太长。我一般用折中方案:把背景做成多个克隆体拼接,马里奥走到舞台右侧三分之一处时,开始移动背景和所有敌人,马里奥自己保持在屏幕上的相对位置不变。具体做法是,当马里奥的 x 坐标大于某个阈值(比如 60),就把背景的 x 坐标减去马里奥的 x 速度,同时把马里奥的 x 坐标减回去。这样视觉上就是镜头在跟,马里奥没动。阈值和速度要匹配,否则会出现抖动。
3. 把源码跑起来:从角色清单到第一帧画面的完整步骤
3.1 角色与变量清单:先搭骨架再填肉
拿到一份 Scratch 马里奥源码,别急着点绿旗。先看角色列表和变量列表,理解每个角色的职责。典型的角色清单包括:马里奥(玩家)、地面碰撞块(克隆体)、砖块、问号块、蘑菇敌人、乌龟敌人、金币、旗杆。变量清单包括:x速度、y速度、重力、跳跃力度、是否在地面、得分、生命、镜头x。全局变量和私有变量的区别很重要:x速度、y速度这些每个角色可能不一样,应该设为私有变量(仅适用于当前角色);得分、生命是全局的,所有角色都能读写。如果源码里把x速度设成了全局变量,多个敌人同时移动时会互相干扰,这是常见的翻车点。
3.2 初始化积木:绿旗点击后必须做的五件事
绿旗点击后的初始化决定了游戏能不能正常开始。我一般会按顺序做五件事:第一,把得分设为 0,生命设为 3;第二,把马里奥移到起点坐标(比如 x=-180, y=-120);第三,把x速度、y速度设为 0,是否在地面设为假;第四,用克隆体生成地面碰撞块和敌人;第五,广播“开始游戏”,让所有角色进入主循环。初始化顺序不能乱,尤其是克隆体生成必须在主循环之前,否则第一帧就会掉下去。克隆体生成用“重复执行”配合“克隆自己”,每个克隆体设置不同的 x 坐标,y 坐标统一。生成数量根据关卡长度定,一般 20 到 30 个碰撞块够用。
3.3 主循环的帧顺序:先算物理还是先判碰撞
主循环里每一帧的顺序很关键。我习惯的顺序是:先读键盘输入,更新x速度;再应用重力,更新y速度;然后移动角色(x 加x速度,y 加y速度);接着做碰撞检测,根据结果修正位置和速度;最后更新镜头和动画。这个顺序不能反,如果先碰撞再移动,角色会卡在地面里。碰撞检测要放在移动之后,因为移动可能导致穿模。修正位置时,如果是从上方落地,就把 y 坐标设为碰撞块顶部加上角色半高;如果是撞墙,就把 x 坐标设为碰撞块边缘减去角色半宽。这些修正量需要根据角色大小微调,角色大小变了,修正量也要跟着变。
3.4 用克隆体管理敌人与金币:数量与回收
敌人和金币用克隆体生成,每个克隆体有自己的x速度和是否被踩状态。敌人的巡逻逻辑很简单:给一个初始方向,碰到墙壁或边缘就反向。金币的碰撞检测用“碰到马里奥”触发,触发后播放音效、增加得分、删除克隆体。克隆体数量要控制,Scratch 对克隆体总数有限制(一般 300 个左右),地面碰撞块、敌人、金币加起来别超。回收机制很重要:敌人被踩后要删除克隆体,金币吃到后也要删除,否则克隆体越积越多,游戏会变卡。删除克隆体用“删除此克隆体”积木,注意不要在克隆体自己的脚本里用“停止全部脚本”,那样会把整个游戏停掉。
当绿旗被点击 将 [得分 v] 设为 0 将 [生命 v] 设为 3 将 [x速度 v] 设为 0 将 [y速度 v] 设为 0 将 [是否在地面 v] 设为 false 移到 x: (-180) y: (-120) 重复执行 (25) 次 克隆 [自己 v] 将 x 增加 (20) 广播 [开始游戏 v]这段初始化脚本的逻辑是:先重置所有状态变量,把马里奥放回起点,然后生成 25 个地面碰撞块克隆体,每个间隔 20 像素,最后广播开始。参数说明:起点坐标根据关卡设计调整,克隆体数量和间隔根据地面宽度调整,广播消息名要和接收脚本一致。
4. 参数调优与手感打磨:让跳跃像马里奥而不是像气球
4.1 重力与跳跃力度的黄金比例
重力太大,跳跃像石头落地;重力太小,跳跃像月球漫步。我试过很多组参数,最后稳定在重力=1.4、跳跃力度=15这一组。这个比例下,跳跃高度大约是 80 像素,滞空时间大约 0.6 秒,接近红白机手感。如果想让跳跃更高,不要只加跳跃力度,那样会飘;应该同时加一点重力,保持滞空时间不变。公式是:跳跃高度 ≈ 跳跃力度² / (2 × 重力)。想让高度翻倍,跳跃力度乘以 1.4,重力也乘以 1.4。这个公式在 Scratch 里够用,因为帧率稳定。
4.2 水平加速度与摩擦力:跑起来要有“推背感”
马里奥的跑动不是匀速,而是有加速度的。按下右键时,x速度每帧增加一个加速度值,我一般设 0.6;松开时,x速度每帧乘以摩擦力 0.85。最大速度用“如果 x速度 > 最大值 则设为最大值”限制,我一般设 6。这样从静止到全速大约需要 10 帧,松开后滑行大约 15 帧,手感比较跟手。如果摩擦力设成 1,就是瞬间停,像踩了胶水;设成 0.95,滑得太远,不好控制。0.85 到 0.9 之间比较合适。
4.3 镜头跟随的阈值与平滑
镜头跟随的阈值我一般设 60,也就是马里奥 x 坐标超过 60 时开始移动背景。阈值太小,镜头一直动,看着晕;阈值太大,马里奥跑到屏幕边缘才动,反应慢。平滑处理可以用“将背景 x 坐标增加 (马里奥 x 速度 × 0.8)”这样的方式,让背景移动稍微滞后一点,视觉上更自然。但滞后不能太多,否则马里奥会跑到背景前面去。0.8 到 1.0 之间调。
4.4 用“计时器”做帧率无关的物理
Scratch 的“重复执行”循环速度受电脑性能影响,性能好的电脑帧率高,物理就快。为了让不同电脑上手感一致,可以用“计时器”做时间步长。每帧读取计时器,算出距离上一帧过了多少秒,然后把速度乘以这个时间差。这样帧率高时每帧移动少,帧率低时每帧移动多,总速度不变。实现上,用两个变量上一帧时间和当前帧时间,每帧更新。这个技巧在 Scratch 里不算常见,但对跨设备一致性很有用。
5. 避坑与排查:源码跑不起来时先看这五处
5.1 马里奥一开局就往下掉,停不住
现象:绿旗点击后,马里奥直接穿过地面掉出舞台。原因:碰撞检测没生效,或者地面碰撞块的克隆体还没生成就开始主循环。解决:检查初始化脚本里克隆体生成是否在广播“开始游戏”之前;检查碰撞检测的条件是不是“碰到碰撞块角色”而不是“碰到颜色”;检查是否在地面变量是否在落地时被正确设为真。
5.2 跳跃时能跳,但落地后跳不起来第二次
现象:第一次跳跃正常,落地后再按跳跃键没反应。原因:是否在地面变量在落地后没有重置为真,或者重置逻辑被其他脚本覆盖。解决:在碰撞检测的落地分支里,明确把是否在地面设为真;检查是否有其他脚本在每帧把是否在地面设为假。常见错误是把是否在地面的初始化放在主循环里,每帧都重置。
5.3 敌人克隆体越跑越多,游戏越来越卡
现象:玩了几十秒后,帧率明显下降,敌人数量暴增。原因:敌人克隆体在碰到边缘或死亡后没有删除,或者生成逻辑放在了主循环里,每帧都在克隆。解决:把克隆体生成放在初始化脚本里,只执行一次;在敌人死亡或离开舞台时用“删除此克隆体”;检查是否有“重复执行”里嵌套了“克隆自己”。
5.4 背景移动时角色和地面错位
现象:镜头跟随开启后,马里奥看起来在地面上滑行,脚和地面不贴合。原因:背景移动量和马里奥 x 坐标修正量不一致,或者移动顺序错了。解决:确保背景移动和马里奥 x 坐标修正在同一帧内完成,且移动量相同;检查是否有其他脚本也在修改背景 x 坐标。常见做法是用一个全局变量镜头x记录镜头偏移,所有需要跟随的角色都减去这个偏移。
5.5 问号块顶出金币后,块的状态没变
现象:马里奥顶了问号块,金币出来了,但问号块还是问号,可以无限顶。原因:问号块没有记录“已被顶过”的状态,或者状态变量是全局的,被其他块共享了。解决:给每个问号块克隆体一个私有变量是否被顶,顶过后设为真,后续检测到是否被顶为真就不再出金币。私有变量在克隆时会被复制,每个克隆体独立,不会互相干扰。
6. 进阶玩法:用列表做关卡数据与存档
6.1 把关卡地形存进列表,而不是硬编码坐标
硬编码坐标的缺点是改关卡要一个个改角色位置,麻烦。更好的做法是用列表存关卡数据。建两个列表:地形x和地形y,每个元素是一块地形的坐标。初始化时遍历列表,为每个坐标生成一个碰撞块克隆体。这样改关卡只需要改列表内容,不用动脚本。列表可以用“导入”功能从文本文件加载,格式就是每行一个坐标对。这个做法在 Scratch 里不算复杂,但能让源码的可维护性提升一个档次。
当绿旗被点击 删除 [地形x v] 的全部项目 删除 [地形y v] 的全部项目 添加 (-200) 到 [地形x v] 添加 (-140) 到 [地形y v] 添加 (-180) 到 [地形x v] 添加 (-140) 到 [地形y v] 重复执行 (长度 of [地形x v]) 次 将 [临时索引 v] 设为 1 克隆 [地面碰撞块 v] 将 x 设为 (第 (临时索引) 项 of [地形x v]) 将 y 设为 (第 (临时索引) 项 of [地形y v]) 将 [临时索引 v] 增加 1这段脚本的逻辑是:先清空列表,再填入地形坐标,然后遍历列表生成碰撞块。参数说明:坐标值根据关卡设计调整,列表长度决定碰撞块数量。注意临时索引要在循环外初始化,循环内递增。
6.2 用列表做简易存档:记录得分与关卡进度
Scratch 没有文件系统,但可以用列表模拟存档。建一个存档数据列表,第一项存得分,第二项存生命,第三项存关卡编号。游戏结束时把数据写进列表,下次开始时读出来。如果想让存档跨设备,可以把列表内容显示在舞台上,让玩家手动抄下来,或者用“询问并等待”让玩家输入。这个做法很土,但在 Scratch 的限制下够用。更高级的做法是用云变量,但云变量需要联网,而且有字符限制,不适合存大量数据。
6.3 用“自制积木”封装物理更新,减少重复代码
Scratch 的自制积木(自定义积木)可以带参数,适合封装物理更新。建一个更新物理积木,参数是重力和摩擦力,里面写通用的速度更新和碰撞检测逻辑。马里奥、敌人、道具都可以调用这个积木,只是参数不同。这样改物理参数只需要改一处,不用每个角色都改。自制积木默认是“运行时不刷新屏幕”,勾选这个选项可以让物理更新在同一帧内完成,避免闪烁。但要注意,勾选后积木里的“等待”积木会失效,所以别在物理更新里用等待。
6.4 用“广播”解耦角色之间的通信
角色之间直接读写变量容易乱,用广播更清晰。比如马里奥踩到敌人,广播“踩到敌人”,敌人收到后播放动画并删除自己,得分脚本收到后加 100 分。广播是异步的,发送后立刻继续执行,不会等接收方处理完。所以如果得分脚本依赖敌人删除后的状态,要小心时序。我一般把得分逻辑直接写在马里奥脚本里,不依赖广播,避免时序问题。广播适合做音效、动画、UI 更新这些不依赖顺序的事情。
6.5 性能优化:减少克隆体、合并角色、用“运行时不刷新屏幕”
Scratch 的性能瓶颈主要在克隆体数量和每帧的积木执行次数。优化手段有三个:第一,减少克隆体,地面碰撞块可以用一个长条角色代替多个小块,只要碰撞检测用“碰到角色”就行;第二,合并角色,把功能相似的角色合并成一个,用私有变量区分类型;第三,给自制积木勾选“运行时不刷新屏幕”,减少屏幕重绘次数。我试过把 30 个碰撞块合并成 3 个长条,帧率从 20 提升到 50 多。代价是碰撞检测的精度下降,但平台游戏对精度要求不高,够用。
6.6 一个我踩过的坑:变量作用域搞混导致敌人集体抽风
最后说一个血泪经验。有一次我把x速度设成了全局变量,结果所有敌人共享同一个速度值。一个敌人撞墙反向,所有敌人都跟着反向,场面像集体抽风。排查了半天才意识到是变量作用域的问题。Scratch 里建变量时,默认是“适用于所有角色”,要手动改成“仅适用于当前角色”。这个选项在变量创建对话框里,很容易忽略。后来我养成了一个习惯:凡是和单个角色状态相关的变量,一律设为私有;只有得分、生命、镜头偏移这种全局状态才用全局变量。这个习惯帮我省了很多后悔药。希望帮到你。
本文还有配套的精品资源,点击获取