复刻诺基亚Bounce游戏:解决数组场景下函数重叠与跳跃失效问题
问题描述
我正在用p5.js复刻诺基亚Bounce游戏,编写了collision函数处理小球与方块/墙体的碰撞,让小球碰到障碍物时停止对应方向的移动。但目前遇到两个问题:
- 由于在
draw函数中多次调用collision,出现了函数重叠导致的逻辑混乱; - 小球站在高于地面的方块上时无法触发跳跃。
以下是相关代码:
let cX = 150, cY = 250, size = 50; // circle let velY = 0; let gravity = 0.3; let blockX = new Array (999); let blockY = new Array (999); function draw() { background(230); velY += gravity; // ball falls faster with time cY += velY; if (cX - size/2 < 0) // ball touches left wall { cX = 0 + size/2; } if (cX + size/2 > width) // ball touches right wall { cX = width - size/2; } // creates object for (i = 0; i < 13; i++) { blockX[i] = size * i; blockY[i] = size * 7; square(blockX[i], blockY[i], size); collision(); } for (i = 0; i < 4; i++) { blockX[i] = size * 7; blockY[i] = size * (i+3); square(blockX[i], blockY[i], size); collision(); } for (i = 0; i < 6; i++) { blockX[i] = 0; blockY[i] = size * (i+1); square(blockX[i], blockY[i], size); collision(); } if (keyIsDown(LEFT_ARROW)) // move left { cX += -3; } if (keyIsDown(RIGHT_ARROW)) // move right { cX += 3; } if (keyIsDown(UP_ARROW) && cY + size/2 == blockY[i]) // ball touches ground { velY = -12; } } function collision() { if (cY + size/2 > blockY[i] && cY < blockY[i] && blockX[i]-5 <= cX && cX <= blockX[i]+5 + size) // ball touches top of block { cY = blockY[i] - size/2; velY = -0.4 * velY; } if (cX - size/2 < blockX[i] + size && cX - size/2 > blockX[i] + size/2 && blockY[i] <= cY && cY <= blockY[i] + size) // ball touches right of block { cX = blockX[i] + size * 3/2; } if (cY - size/2 < blockY[i] + size && cY > blockY[i] + size/2 && blockX[i]-5 <= cX && cX <= blockX[i]+5 + size) // ball touches bottom of block { cY = blockY[i] + size * 3/2; } if (cX + size/2 > blockX[i] && cX + size/2 < blockX[i] + size/2 && blockY[i] <= cY && cY <= blockY[i] + size) // ball touches left of block { cX = blockX[i] - size/2; } }
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.5.0/p5.min.js"></script>
修复方案
核心问题分析
- 全局变量污染:多个
for循环共用全局变量i,导致collision函数中的i始终指向最后一个循环的索引,而非当前检测的方块; - 重复碰撞检测:每绘制一个方块就调用一次
collision,小球位置被多次修改,引发逻辑重叠; - 跳跃判断精度问题:用
==精确比较坐标,浮点运算误差会导致判断失效,且依赖全局i无法正确识别脚下的方块。
修改后的完整代码
let cX = 150, cY = 250, size = 50; // 小球参数 let velY = 0; let gravity = 0.3; let isGrounded = false; // 标记小球是否处于可跳跃状态 // 用数组存储所有方块,每个元素为{x, y}对象 let blocks = []; function setup() { createCanvas(650, 400); // 初始化所有方块数据 // 底部横向方块 for (let i = 0; i < 13; i++) { blocks.push({x: size * i, y: size * 7}); } // 右侧纵向方块 for (let i = 0; i < 4; i++) { blocks.push({x: size * 7, y: size * (i+3)}); } // 左侧纵向方块 for (let i = 0; i < 6; i++) { blocks.push({x: 0, y: size * (i+1)}); } } function draw() { background(230); isGrounded = false; // 每帧重置落地状态 // 应用重力 velY += gravity; cY += velY; // 左右墙体碰撞 if (cX - size/2 < 0) { cX = 0 + size/2; } if (cX + size/2 > width) { cX = width - size/2; } // 绘制所有方块 blocks.forEach(block => { square(block.x, block.y, size); }); // 统一检测所有方块的碰撞 blocks.forEach(block => { collision(block); }); // 移动控制 if (keyIsDown(LEFT_ARROW)) { cX += -3; } if (keyIsDown(RIGHT_ARROW)) { cX += 3; } // 跳跃控制:仅在落地状态下触发 if (keyIsDown(UP_ARROW) && isGrounded) { velY = -12; isGrounded = false; // 起跳后取消落地标记 } // 绘制小球 ellipse(cX, cY, size); } // 碰撞检测函数,接收单个方块对象作为参数 function collision(block) { // 提前计算碰撞边界,简化逻辑 const ballTop = cY - size/2; const ballBottom = cY + size/2; const ballLeft = cX - size/2; const ballRight = cX + size/2; const blockTop = block.y; const blockBottom = block.y + size; const blockLeft = block.x; const blockRight = block.x + size; // 碰到方块顶部(落地) if (ballBottom > blockTop && ballBottom < blockTop + 5 && cX > blockLeft - 5 && cX < blockRight + 5) { cY = blockTop - size/2; velY = 0; // 重置垂直速度,如需弹跳可改为velY = -0.4 * velY isGrounded = true; } // 碰到方块右侧 if (ballLeft < blockRight && ballLeft > blockRight - 5 && cY > blockTop - 5 && cY < blockBottom + 5) { cX = blockRight + size/2; } // 碰到方块底部 if (ballTop < blockBottom && ballTop > blockBottom - 5 && cX > blockLeft - 5 && cX < blockRight + 5) { cY = blockBottom + size/2; velY = 0; } // 碰到方块左侧 if (ballRight > blockLeft && ballRight < blockLeft + 5 && cY > blockTop - 5 && cY < blockBottom + 5) { cX = blockLeft - size/2; } }
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.5.0/p5.min.js"></script>
关键修改说明
- 重构方块数据结构:用数组存储所有方块的坐标对象,避免覆盖全局数组元素,同时消除对全局
i的依赖; - 统一碰撞检测时机:先绘制所有方块,再遍历数组做一次碰撞检测,避免重复修改小球位置;
- 落地状态标记:用
isGrounded变量跟踪小球是否站在可跳跃表面,解决跳跃失效问题; - 碰撞精度优化:用小范围区间判断(如
> blockTop && < blockTop +5)替代精确等于,避免浮点运算误差导致的检测失效; - 函数参数化:
collision函数接收单个方块对象,逻辑独立清晰,避免全局变量污染。
内容的提问来源于stack exchange,提问作者Mbocpe
相关产品推荐
相关产品推荐

