javascript - 为什么乒乓球会卡在底部?

标签 javascript html canvas html5-canvas

我最近做了一个 JS Pong 游戏。它运作良好,但球很少卡在底部或顶部。看起来像是穿墙到一半,不断弹跳。 Video of the issue happening.你可以试试游戏here .我不知道为什么会出现这个问题,因为逻辑似乎是正确的并且在 90% 的时间里都能正常工作。以下是我的程序的两个主要功能:

function moveAll() {
  if (showingWinScreen) {
    return;
  }
  computerMovement();
  ballX += ballSpeedX;
  ballY += ballSpeedY;
  if (ballY <= 10) {
    ballSpeedY = -ballSpeedY;
  } else if (ballY >= HEIGHT - 10) {
    ballSpeedY = -ballSpeedY;
  }
  if (ballX >= WIDTH - 10) {
    if ((ballY > paddleY) && (ballY < paddleY + 100)) {
      ballSpeedX = -ballSpeedX;
      var deltaY = ballY - paddleY - 50;
      ballSpeedY = deltaY / 5;
    } else {
      player1Score++;
      ballReset();
    }
  } else if (ballX <= 10) {
    if ((ballY > mouseY - 50) && (ballY < mouseY + 50)) {
      ballSpeedX = -ballSpeedX;
      deltaY = ballY - mouseY;
      ballSpeedY = deltaY / 6;
    } else {
      player2Score++;
      ballReset();
    }
  }
}

function drawAll() {
  if (showingWinScreen) {
    colorRect(0, 0, WIDTH, HEIGHT, "black");
    canvas.fillStyle = "yellow";
    canvas.fillText("Click to continue!", 300, 300);
    if (player1Score == WINNING_SCORE) {
      canvas.fillText("You won!", 360, 500);
    } else if (player2Score == WINNING_SCORE) {
      canvas.fillText("The computer beat you!", 280, 500);
    }
    return;
  }
  colorRect(0, 0, WIDTH, HEIGHT, "black");
  drawNet();
  makeCircle(ballX, ballY, 10, 0, Math.PI * 2, "red");
  colorRect(790, paddleY, 10, 100, "cyan");
  colorRect(0, mouseY - 50, 10, 100, "yellow");
  canvas.fillStyle = "white";
  canvas.fillText(player1Score + "    " + player2Score, 360, 100);
}

感谢您的帮助!

最佳答案

我认为只有一种情况会发生这种情况:当您在碰撞框架中降低速度时。

当速度保持不变时,无论如何,你的球总是会弹回上一帧的位置:

var cvs = document.querySelector("canvas");
var ctx = cvs.getContext("2d");
var balls = [
  Ball(50, 50, 0, 5, 5, "red"),
  Ball(100, 50, 0, 5, 10, "blue"),
  Ball(150, 50, 0, 5, 15, "green"),
  Ball(200, 50, 0, 5, 20, "yellow")
];

var next = () => {
  updateFrame(balls);
  drawFrame(balls);
}

var loop = () => {
  requestAnimationFrame(() => {
    next();
    loop();
  });
}

next();

function Ball(x, y, vx, vy, r, color) {
  return {
    x: x,
    y: y,
    vx: vx,
    vy: vy,
    r: r,
    color: color
  }
};

function updateBall(b) {
  b.x += b.vx;
  b.y += b.vy;
  
  if (b.y <= b.r ||
      b.y >= cvs.height - b.r) {
     b.vy *= -1; 
  }
};

function drawBall(b) {
  ctx.beginPath();
  ctx.fillStyle = b.color;
  ctx.arc(b.x, b.y, b.r, 0, 2 * Math.PI, false);
  ctx.fill();
}

function updateFrame(balls) {
   balls.forEach(updateBall); 
}

function drawFrame(balls) {
  ctx.clearRect(0, 0, cvs.width, cvs.height);
  balls.forEach(drawBall);
};
<canvas width="300" height="150" style="background: #454545"></canvas>
<button onclick="next()">next</button>
<button onclick="loop()">run</button>

但是当速度改变时,事情就卡住了:

var cvs = document.querySelector("canvas");
var ctx = cvs.getContext("2d");
var balls = [
  Ball(50, 50, 0, 10, 5, "red"),
  Ball(100, 50, 0, 10, 10, "blue"),
  Ball(150, 50, 0, 10, 15, "green"),
  Ball(200, 50, 0, 10, 20, "yellow")
];

var next = () => {
  updateFrame(balls);
  drawFrame(balls);
}

var loop = () => {
  requestAnimationFrame(() => {
    next();
    loop();
  });
}

next();

function Ball(x, y, vx, vy, r, color) {
  return {
    x: x,
    y: y,
    vx: vx,
    vy: vy,
    r: r,
    color: color
  }
};

function updateBall(b) {
  b.x += b.vx;
  b.y += b.vy;
  
  if (b.y <= b.r ||
      b.y >= cvs.height - b.r) {
     b.vy *= -0.5; 
  }
};

function drawBall(b) {
  ctx.beginPath();
  ctx.fillStyle = b.color;
  ctx.arc(b.x, b.y, b.r, 0, 2 * Math.PI, false);
  ctx.fill();
}

function updateFrame(balls) {
   balls.forEach(updateBall); 
}

function drawFrame(balls) {
  ctx.clearRect(0, 0, cvs.width, cvs.height);
  balls.forEach(drawBall);
};
<canvas width="300" height="150" style="background: #454545"></canvas>
<button onclick="next()">next</button>
<button onclick="loop()">run</button>

在您的情况下,我认为这只有在桨板碰撞和墙壁碰撞同时发生时才会发生。

一个快速实现的解决方案是在平移球位置之前检查新位置是否有效。如果你不想要精确的位置,你可以把球放在碰撞点。请注意,这会产生稍微偏离的框架。

例如:

var newY = ballY + ballSpeedY;

// Top wall
if(newY <= 10) {
  ballY = 10;
  ballSpeedY = -ballSpeedY;  
}
// Bottom wall
else if(newY >= HEIGHT-10){
  ballY = HEIGHT - 10;
  ballSpeedY = -ballSpeedY;   
}
// No collision
else {
  ballY = newY;
}

更新:对可能发生的事情的更详细描述

假设您的球与 Canvas 的顶部边界发生碰撞,并且与您的 Racket 发生碰撞在同一帧

首先,您将球移动到碰撞位置:ballY += ballSpeedY;说出你的 ballY是 4,而你的 ballSpeedY是-5,你会将球定位到-1 , 在墙内。

如果这是唯一的碰撞,你应该没问题。你翻转速度(ballSpeedY = -ballSpeedY),所以在下一帧中,你的球应该回到-1 + 5 = 4。 , 所以 ballY将是 4再次,你的球将移向 4 + 5 = 9在下一帧中。

现在出现了一个问题,当在-1定位框架,你也与桨碰撞!当 Racket 击中球时,您修改球速:ballSpeedY = deltaY / 5; .如果结果是 < 1 ,你的球将无法在下一帧中离开墙。而不是 -1 + 5 = 4 ,例如,您的球将移动到:-1 + 0.5 = -0.5 .

现在,您的球将无法重新开始播放,因为下一帧将再次计算碰撞并翻转速度。 这会导致您在球被卡住时看到有弹性的颤抖效果

一个简单但相当不错的解决方案是只将球的位置更新为有效位置。即:永远不要碰撞坐标。

关于javascript - 为什么乒乓球会卡在底部?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40384171/

相关文章:

javascript - 使用 Javascript 和 CSS 在事件触发时制作动画

html - 响应式设计模式与调整浏览器窗口大小

javascript - 如何使用 Fabric.js 做类似 Visio 的带有连接的绘图?

ajax - 无法将 Canvas 数据从 Javascript AJAX 发送到 PHP 页面

java - 自定义 HTML 代码在 IE 9 中创建空列表

javascript - AngularJS 函数 DI 参数

javascript - 弹出窗口在 ajax 成功处理程序中被阻止

c# - 在 iframe 中加载时清除 url 缓存

javascript - 在 Facebook 上分享数据 uri 图片

javascript - 如何将换行符从 HTML div 复制到 jQuery 中的文本区域?