我正在制作一款基于HTML5 Canvas的平台游戏,我遇到了一些碰撞问题。我一直在尝试许多碰撞算法,但我似乎没有得到任何工作。
我的问题:我怎样才能让方块/深灰色方块阻止我的球穿过它?
关于我的代码的一些信息:
- 动态生成地图
- 我已经尝试了很多次碰撞算法
- 碰撞处理在
canMoveHere
函数中完成,并将true
或false
返回给moveAll
函数,其中确定球是否可以移动到下一个正方形
jsfiddle.net上的代码
我的代码从Google Drive下载
我的完整代码:
<html>
<head>
<canvas id ="gameCanvas" width = "500" height = "500" style = "border:1px solid black;"></canvas>
<script>
var canvas, canvasContext;
var framesPerSecond = 30;
var gravity = .2;
var leftKey = false;
var rightKey = false;
const TILE_H = 25;
const TILE_W = 25;
var map = [
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
];
var ball = {
`x:250`,
`y:250`,
`radius:10`,
color:"#ff9966",
`velocityX:5`,
`velocityY:1`,
`terminalVel:8`,
draw:function()
{
canvasContext.beginPath();
canvasContext.fillStyle = this.color;
canvasContext.arc(this.x, `this.y`, this.radius, 0, Math.PI * 2, false);
canvasContext.fill();
canvasContext.closePath();
}
};
document.addEventListener("keydown", function(evt)
{
if(evt.keyCode == 37)
{
leftKey = true;
}
if(evt.keyCode == 39)
{
rightKey = true;
}
});
document.addEventListener("keyup", function(evt)
{
if(evt.keyCode == 37)
{
leftKey = false;
}
if(evt.keyCode == 39)
{
rightKey = false;
}
});
window.onload = function()
{
canvas = document.getElementById("gameCanvas");
canvasContext = canvas.getContext("2d");
setInterval(function()
{
drawAll();
moveAll();
}, 1000/framesPerSecond);
}
var renderMap = function()
{
for(var eachRow=0; eachRow<20; eachRow++)
{
for(var eachCol=0; eachCol<20; eachCol++)
{
if(map[eachRow][eachCol] == 0)
{
canvasContext.fillStyle = "#a6a6a6";
canvasContext.fillRect(TILE_W*eachCol, TILE_H*eachRow, TILE_W, TILE_H);
}
if(map[eachRow][eachCol] == 1)
{
canvasContext.fillStyle = "#666666";
canvasContext.fillRect(TILE_W*eachCol, TILE_H*eachRow, TILE_W, TILE_H);
}
}
}
}
var drawAll = function()
{
canvasContext.clearRect(0, 0, canvas.width, canvas.height);
renderMap();
ball.draw();
}
var canMoveHere = function(col, row)
{
if(map[row][col] == 1)
{
return false;
}
else
{
return true;
}
}
var moveAll = function()
{
nextBallX = `ball.x` + ball.velocityX;
nextBallY = `ball.y` + ball.velocityY;
nextBallCol = Math.floor(nextBallX / TILE_W);
nextBallRow = Math.floor(nextBallY / TILE_H);
var canMove = canMoveHere(nextBallCol, nextBallRow);
ball.velocityY += gravity;
`ball.y` += ball.velocityY;
if(ball.velocityY >= ball.terminalVel)
{
ball.velocityY = ball.terminalVel;
}
if(canMove === false)
{
ball.velocityY *= -1;
}
if(leftKey)
{
`ball.x` -= ball.velocityX;
}
if(rightKey)
{
`ball.x` += ball.velocityX;
}
}
</script>
</head>
<body>
</body>
</html>
你不能只是说"如果球能或不能在特定的贴图上移动",你必须告诉游戏如何在碰到障碍物时做出反应,这取决于两个因素:当前球的方向和障碍物的形状。
我修改了你的canMoveHere
函数来评估:
var directionChange = function(nextCol, nextRow)
{
var currentRow = Math.floor(ball.y / TILE_H);
var currentCol = Math.floor(ball.x / TILE_W);
var resultDirection = { x: 1, y: 1 };
if (map[nextRow][nextCol] == 1) {
if (map[nextRow][currentCol] == 1 &&
map[currentRow][nextCol] == 0) {
// horizontal obstacle
resultDirection.x = 1;
resultDirection.y = -1;
} else if (map[nextRow][currentCol] == 0 &&
map[currentRow][nextCol] == 1) {
// vertical obstacle
resultDirection.x = -1;
resultDirection.y = 1;
} else {
// corner obstacle
resultDirection.x = -1;
resultDirection.y = -1;
}
}
return resultDirection;
}
此时,我们也相应地修改了moveAll
函数
var moveAll = function()
{
nextBallX = ball.x + ball.velocityX;
nextBallY = ball.y + ball.velocityY;
nextBallCol = Math.floor(nextBallX / TILE_W);
nextBallRow = Math.floor(nextBallY / TILE_H);
let dir = directionChange(nextBallCol, nextBallRow);
ball.velocityY *= dir.y;
ball.velocityX *= dir.x;
ball.velocityY += gravity;
if(ball.velocityY >= ball.terminalVel)
{
ball.velocityY = ball.terminalVel;
}
ball.y += ball.velocityY;
if(leftKey) {
ball.x -= ball.velocityX;
}
if(rightKey) {
ball.x += ball.velocityX;
}
}
现在你只需要找到一种方法来让键平稳地驱动球,因为使用正值/负值改变速度会影响球的实际方向,因为当值为负值时,它会向与按下的键相反的方向移动;
我尝试使用绝对值
if(leftKey) {
ball.x -= Math.abs(ball.velocityX);
}
if(rightKey) {
ball.x += Math.abs(ball.velocityX);
}
但是一旦球碰到障碍物,按下的键将继续推动球对抗障碍物,再次改变速度,并且-对于它现在的建造方式-球将穿过墙壁。
虽然这不是一个没有错误的解决方案,但我希望这能帮助你解决主要问题。