本帖最后由 驴友花雕 于 2025-9-18 13:42 编辑
ARCADE MakeCode 之贪吃毛毛虫游戏代码解读
这是一个经典的贪吃蛇游戏变体,玩家控制一条毛毛虫吃叶子并增长身体。
代码结构分析
1. 常量和枚举定义
javascript
- const NEXT_SECTION_KEY = "__child_node"; // 用于存储下一节身体的键名
- namespace SpriteKind {
- export const Tail = SpriteKind.create(); // 创建尾部精灵类型
- }
-
- enum Direction {
- Up, // 上
- Down, // 下
- Left, // 左
- Right // 右
- }
-
- const size = 8; // 网格大小(像素)
复制代码
2. 毛毛虫头部初始化
javascript
- const caterpillarHead = sprites.create(img`
- . . 3 3 3 3 . .
- . 3 2 2 2 2 3 .
- 3 2 f 2 2 f 2 3
- 3 2 f 2 2 f 2 3
- 3 2 2 2 2 2 2 3
- 3 2 2 2 2 2 2 3
- . 3 2 2 2 2 3 .
- . . 3 3 3 3 . .
- `, SpriteKind.Player);
-
- caterpillarHead.left = 4 * size; // 初始位置X
- caterpillarHead.top = 12 * size; // 初始位置Y
- caterpillarHead.data = {}; // 存储自定义数据
复制代码
3. 游戏地图和叶子设置
javascript
- tiles.setTilemap(tilemap`level`); // 设置游戏地图
-
- // 普通叶子图像
- const leafImage = img`
- . . . . . f f 7
- . . . f f f 6 f
- . . f 6 7 f f f
- . f 6 7 f 7 7 f
- f f 7 f 7 7 7 f
- f 6 f 7 7 7 f .
- f 6 7 7 f f . .
- f f f f f . . .
- `;
-
- // 闪光叶子图像(用于动画效果)
- const shinyLeafImage = img`
- . . 1 . . f f 7
- . 1 . f f f 6 f
- 1 . f 6 7 f f f
- . f 6 7 f 7 7 f
- f f 7 f 7 7 7 f
- f 6 f 7 7 7 f .
- f 6 7 7 f f . 1
- f f f f f . 1 .
- `;
-
- placeFruit(); // 放置第一个叶子
- info.setScore(0); // 初始化分数
复制代码
4. 游戏状态变量
javascript
- let direction = Direction.Up; // 当前移动方向
- let addSection = true; // 是否需要添加身体节
- let enqueued = false; // 方向改变是否已排队
- let lastIteration = 0; // 上次迭代时间
- let timeout = 500; // 移动间隔时间(毫秒)
复制代码
5. 主游戏循环
javascript
- forever(function () {
- // 边界检查 - 如果碰到边界游戏结束
- if (caterpillarHead.left < 0 || caterpillarHead.right > screen.width
- || caterpillarHead.top < 0 || caterpillarHead.bottom > screen.height) {
- game.over(false);
- }
-
- // 检查是否到了移动时间
- if (!enqueued && game.runtime() - lastIteration < timeout) {
- return;
- }
-
- // 添加身体节或移动身体
- if (addSection) {
- addToBody();
- } else {
- move(caterpillarHead);
- }
-
- // 根据方向移动头部
- switch (direction) {
- case Direction.Up:
- caterpillarHead.y -= size;
- break;
- case Direction.Down:
- caterpillarHead.y += size;
- break;
- case Direction.Left:
- caterpillarHead.x -= size;
- break;
- case Direction.Right:
- caterpillarHead.x += size;
- break;
- }
-
- // 重置状态变量
- enqueued = false;
- lastIteration = game.runtime();
- });
复制代码
6. 添加身体节函数
javascript
- function addToBody() {
- const newSection = sprites.create(img`
- . . f f f f . .
- . f 1 1 1 1 f .
- f 1 1 1 1 1 1 f
- f 1 1 1 1 1 1 f
- f 1 1 1 1 1 1 f
- f 1 1 1 1 1 1 f
- . f 1 1 1 1 f .
- . . f f f f . .
- `, SpriteKind.Tail);
-
- newSection.data = {};
-
- // 随机生成身体节颜色(避免使用特定颜色)
- let newColor: number;
- do {
- newColor = randint(0x1, 0xE);
- } while (newColor === 0x6);
- newSection.image.replace(0x1, newColor);
-
- do {
- newColor = randint(0x1, 0xE);
- } while (newColor === 0x6);
- newSection.image.replace(0xF, newColor);
-
- // 设置身体节位置
- newSection.x = caterpillarHead.x;
- newSection.y = caterpillarHead.y;
-
- // 链接身体节(链表结构)
- newSection.data[NEXT_SECTION_KEY] = caterpillarHead.data[NEXT_SECTION_KEY];
- caterpillarHead.data[NEXT_SECTION_KEY] = newSection;
-
- addSection = false; // 重置添加标志
- }
复制代码
7. 移动身体函数
javascript
- function move(piece: Sprite) {
- const next = piece.data[NEXT_SECTION_KEY];
- if (next) {
- move(next); // 递归移动下一节
- next.x = piece.x; // 设置位置为前一节的位置
- next.y = piece.y;
- }
- }
复制代码
8. 碰撞检测事件
javascript
- // 吃到叶子事件
- sprites.onOverlap(SpriteKind.Player, SpriteKind.Food, function (sprite: Sprite, otherSprite: Sprite) {
- info.changeScoreBy(1); // 增加分数
- otherSprite.destroy(effects.disintegrate); // 销毁叶子
- music.baDing.play(); // 播放音效
- timeout = Math.max(150, timeout - 50); // 加快游戏速度
- addSection = true; // 标记需要添加身体节
- placeFruit(); // 放置新叶子
- });
-
- // 碰到自己身体事件
- sprites.onOverlap(SpriteKind.Player, SpriteKind.Tail, function (sprite: Sprite, otherSprite: Sprite) {
- game.over(false); // 游戏结束
- });
复制代码
9. 方向控制函数
javascript
- // 上方向键
- controller.up.onEvent(ControllerButtonEvent.Pressed, function () {
- setDirection(Direction.Up, Direction.Down, img`
- . . 3 3 3 3 . .
- . 3 2 2 2 2 3 .
- 3 2 f 2 2 f 2 3
- 3 2 f 2 2 f 2 3
- 3 2 2 2 2 2 2 3
- 3 2 2 2 2 2 2 3
- . 3 2 2 2 2 3 .
- . . 3 3 3 3 . .
- `);
- });
-
- // 下方向键
- controller.down.onEvent(ControllerButtonEvent.Pressed, function () {
- setDirection(Direction.Down, Direction.Up, img`
- . . 3 3 3 3 . .
- . 3 2 2 2 2 3 .
- 3 2 2 2 2 2 2 3
- 3 2 2 2 2 2 2 3
- 3 2 f 2 2 f 2 3
- 3 2 f 2 2 f 2 3
- . 3 2 2 2 2 3 .
- . . 3 3 3 3 . .
- `);
- });
-
- // 左方向键
- controller.left.onEvent(ControllerButtonEvent.Pressed, function () {
- setDirection(Direction.Left, Direction.Right, img`
- . . 3 3 3 3 . .
- . 3 2 2 2 2 3 .
- 3 2 f f 2 2 2 3
- 3 2 2 2 2 2 2 3
- 3 2 2 2 2 2 2 3
- 3 2 f f 2 2 2 3
- . 3 2 2 2 2 3 .
- . . 3 3 3 3 . .
- `);
- });
-
- // 右方向键
- controller.right.onEvent(ControllerButtonEvent.Pressed, function () {
- setDirection(Direction.Right, Direction.Left, img`
- . . 3 3 3 3 . .
- . 3 2 2 2 2 3 .
- 3 2 2 2 f f 2 3
- 3 2 2 2 2 2 2 3
- 3 2 2 2 2 2 2 3
- 3 2 2 2 f f 2 3
- . 3 2 2 2 2 3 .
- . . 3 3 3 3 . .
- `);
- });
-
- // 设置方向函数
- function setDirection(targetDir: Direction, oppositeDir: Direction, im: Image) {
- if (!enqueued && direction !== targetDir && direction !== oppositeDir) {
- caterpillarHead.setImage(im); // 更新头部图像
- direction = targetDir; // 设置新方向
- enqueued = true; // 标记方向已改变
- }
- }
复制代码
10. 放置叶子函数
javascript
- function placeFruit() {
- currentLeaf = sprites.create(leafImage, SpriteKind.Food);
- do {
- // 随机位置(网格对齐)
- currentLeaf.left = randint(0, 19) * size;
- currentLeaf.top = randint(0, 14) * size;
- } while (
- // 确保不放在角落或身体上
- (currentLeaf.top === 0 && currentLeaf.right === screen.width)
- || sprites
- .allOfKind(SpriteKind.Tail)
- .some(s => s.overlapsWith(currentLeaf))
- );
- }
复制代码
11. 叶子闪烁动画
javascript
- game.onUpdateInterval(500, function () {
- // 切换叶子图像创建闪烁效果
- if (currentLeaf.image === leafImage) {
- currentLeaf.setImage(shinyLeafImage);
- } else {
- currentLeaf.setImage(leafImage);
- }
- });
复制代码
游戏机制解析
移动机制:毛毛虫以固定时间间隔移动,方向键控制移动方向
身体增长:每吃一个叶子,身体增加一节
碰撞检测:
碰到边界游戏结束
碰到自己身体游戏结束
碰到叶子得分并增长
难度递增:随着分数增加,移动速度加快
视觉效果:叶子有闪烁动画,身体节有随机颜色
这个游戏使用了链表数据结构来管理毛毛虫的身体节,每个身体节都存储指向下一节的引用,实现了高效的移动和碰撞检测。
|