JS蛇游戏中的碰撞检测错误

时间:2017-03-22 16:45:54

标签: javascript canvas collision-detection

我正试图写一个Snake的变种,蛇在墙上“反弹”。

它大部分时间都有效,但偶尔蛇会“逃脱”,我无法弄明白为什么。最初我将collison检测函数中的不等式设置为严格<>,我认为这是问题的原因,但我已将它们更改为<=和{{1问题仍然存在。

任何人都可以解释为什么会这样吗? (在蛇逃脱之前,你通常需要玩一分钟左右......)

>=

3 个答案:

答案 0 :(得分:1)

如果在碰到墙壁之前改变方向远离墙壁,则会出现问题。

例如,假设蛇的头部刚刚移动到x = 0并向左移动,用户在下一个更新帧之前键入右箭头。现在snakeVelocity.i设置为距离墙1。

然后测试墙

if (obj.x >= WIDTH / SEGMENT_WIDTH || obj.x <= 0) {
    snakeVelocity.i *= -1; // negate the direction
                           // but the direction is already away from the
                           // wall due to user input. This will turn it back
                           // onto the wall
}

上下相同。

你需要知道碰撞测试知道蛇的方向,然后根据该测试判断该移动是否会导致碰撞。

更改测试功能,以便在允许按其当前状态指示移动时找到蛇头的下一个位置。只有当这一举动导致头部超出游戏范围时才会改变方向。

function snakeWallCollision(head) {
    var x = head.x + snakeVelocity.i; // find out where the head will be 
    var y = head.y + snakeVelocity.j; // next frame
    if (x > WIDTH / SEGMENT_WIDTH || x < 0) {
        snakeVelocity.i *= -1;
        return true;
    }
    if (y > HEIGHT / SEGMENT_WIDTH || y < 0) {
        snakeVelocity.j *= -1;
        return true;
    }
    return false;
}

答案 1 :(得分:0)

如果您更换这些线会发生什么:

newHead.x = head.x + snakeVelocity.i;
newHead.y = head.y +  + snakeVelocity.j;

// check for wall collision, which also updates velocity if needed
snakeWallCollision(head);

答案 2 :(得分:0)

问题似乎是,如果用户按下与您的collision check同步的按键,则指示会发生两次变化,并且蛇会越过墙壁。

也许这个可以帮助你(我添加了一个测试变量USER_ACTION来检查用户是否按下了密钥,如果是,请不要执行collision check):

var ctx = document.getElementById('canvas').getContext('2d');
ctx.font = '30px Arial';

var HEIGHT = 500;
var WIDTH = 500;
var SEGMENT_WIDTH = 30;
var USER_ACTION = false;

var snakeVelocity = {
    i: 1,
    j: 0
};
var snakeArray = createSnake();

function createSnake() {
    var snakeArray = [];
    var length = 5; // Initial length of snake
    for (var i = 0; i < length; i++) {
        snakeArray.push({
            x: i + 1,
            y: 1
        });
    }
    return snakeArray;
}

function moveSnake(arr) {
    var head = arr.slice(-1)[0];
    var tail = arr[0];
    var newHead = arr.shift();

    // check for wall collision, which also updates velocity if needed
    snakeWallCollision(head);

    newHead.x = head.x + snakeVelocity.i;
    newHead.y = head.y + +snakeVelocity.j;

    arr.push(newHead);
    return arr;
}

function snakeWallCollision(obj) {
    if (!USER_ACTION) {
        var collision = false;
        if (obj.x >= WIDTH / SEGMENT_WIDTH || obj.x <= 0) {
            snakeVelocity.i *= -1;
            collision = true;
        }
        if (obj.y >= HEIGHT / SEGMENT_WIDTH || obj.y <= 0) {
            snakeVelocity.j *= -1;
            collision = true;
        }
    } else {
        USER_ACTION = false;
    }
    return collision;
}

function drawSnake() {
    console.log(snakeArray[0]);
    for (var i = 0; i < snakeArray.length; i++) {
        var segment = snakeArray[i];
        ctx.fillText('S', segment.x * SEGMENT_WIDTH, segment.y * SEGMENT_WIDTH + 30);
    }
}

function update() {
    ctx.clearRect(0, 0, WIDTH, HEIGHT);
    moveSnake(snakeArray);
    drawSnake();
}

function checkKey(e) {
    USER_ACTION = true;
    e = e || window.event;

    if ([38, 40, 37, 39].includes(e.keyCode)) {
        e.preventDefault();
    }

    if (e.keyCode == '38') {
        snakeVelocity = {
            i: 0,
            j: -1
        };
    } else if (e.keyCode == '40') {
        snakeVelocity = {
            i: 0,
            j: 1
        };
    } else if (e.keyCode == '37') {
        snakeVelocity = {
            i: -1,
            j: 0
        };
    } else if (e.keyCode == '39') {
        snakeVelocity = {
            i: 1,
            j: 0
        };
    }
}
document.onkeydown = checkKey;
setInterval(update, 1000 / 20);
drawSnake();