HTML5 Canvas -动态地图上的块侧碰撞



我正在制作一款基于HTML5 Canvas的平台游戏,我遇到了一些碰撞问题。我一直在尝试许多碰撞算法,但我似乎没有得到任何工作。


我的问题:我怎样才能让方块/深灰色方块阻止我的球穿过它?


关于我的代码的一些信息:

  • 动态生成地图
  • 我已经尝试了很多次碰撞算法
  • 碰撞处理在canMoveHere函数中完成,并将truefalse返回给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);
}

但是一旦球碰到障碍物,按下的键将继续推动球对抗障碍物,再次改变速度,并且-对于它现在的建造方式-球将穿过墙壁。

虽然这不是一个没有错误的解决方案,但我希望这能帮助你解决主要问题。

最新更新