You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

复刻诺基亚Bounce游戏:解决数组场景下函数重叠与跳跃失效问题

问题描述

我正在用p5.js复刻诺基亚Bounce游戏,编写了collision函数处理小球与方块/墙体的碰撞,让小球碰到障碍物时停止对应方向的移动。但目前遇到两个问题:

  1. 由于在draw函数中多次调用collision,出现了函数重叠导致的逻辑混乱;
  2. 小球站在高于地面的方块上时无法触发跳跃。

以下是相关代码:

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>

修复方案

核心问题分析

  1. 全局变量污染:多个for循环共用全局变量i,导致collision函数中的i始终指向最后一个循环的索引,而非当前检测的方块;
  2. 重复碰撞检测:每绘制一个方块就调用一次collision,小球位置被多次修改,引发逻辑重叠;
  3. 跳跃判断精度问题:用==精确比较坐标,浮点运算误差会导致判断失效,且依赖全局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>

关键修改说明

  1. 重构方块数据结构:用数组存储所有方块的坐标对象,避免覆盖全局数组元素,同时消除对全局i的依赖;
  2. 统一碰撞检测时机:先绘制所有方块,再遍历数组做一次碰撞检测,避免重复修改小球位置;
  3. 落地状态标记:用isGrounded变量跟踪小球是否站在可跳跃表面,解决跳跃失效问题;
  4. 碰撞精度优化:用小范围区间判断(如> blockTop && < blockTop +5)替代精确等于,避免浮点运算误差导致的检测失效;
  5. 函数参数化:collision函数接收单个方块对象,逻辑独立清晰,避免全局变量污染。

内容的提问来源于stack exchange,提问作者Mbocpe

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.06 05:35:34