【问题标题】:Collision detection bug in JS snake gameJS蛇游戏中的碰撞检测错误
【发布时间】:2017-03-22 16:45:53
【问题描述】:

我正在尝试编写 Snake 的变体,其中蛇从墙上“弹回”。

它大部分时间都有效,但有时蛇会“逃跑”,我不知道为什么。最初我将碰撞检测函数中的不等式设置为严格的<>,我认为这是问题的原因,但我已将它们更改为<=>=,问题仍然存在。

谁能解释一下为什么会这样? (你通常要玩一分钟左右才能让蛇逃跑……)

<canvas id="canvas" width=500 height=500 style="display: block; border: 1px solid green; margin: auto;"></canvas>
<script>
var ctx = document.getElementById('canvas').getContext('2d');
ctx.font = '30px Arial';

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

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) {
    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;
    }
    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) {

    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();
</script>

【问题讨论】:

    标签: javascript canvas collision-detection


    【解决方案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;
    }
    

    【讨论】:

    • 这似乎很好地解决了它。谢谢。
    【解决方案2】:

    如果你交换这些行会发生什么:

    newHead.x = head.x + snakeVelocity.i;
    newHead.y = head.y +  + snakeVelocity.j;
    
    // check for wall collision, which also updates velocity if needed
    snakeWallCollision(head);
    

    【讨论】:

    • 如果你使用snakeWallCollision(newHead);,这看起来应该可以工作,但我认为因为newHead值被提交,蛇仍然逃脱......
    【解决方案3】:

    问题似乎是,如果用户按下与您的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();
    

    【讨论】:

    • 遗憾的是,这个版本的问题仍然存在。还是谢谢。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多