You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

JavaScript Canvas贪吃蛇游戏checkCollision方法调用报错求助

贪吃蛇游戏碰撞检测崩溃问题修复

问题描述

使用JavaScript Canvas开发贪吃蛇游戏,多数功能已实现,但蛇撞到墙体或自身时游戏崩溃,无法正常调用checkCollision方法。

报错信息

Uncaught TypeError: Cannot read properties of undefined (reading 'col')
at Snake.checkCollision (snake.html:148:43)
at core (snake.html:191:27)
at snake.html:196:13
Uncaught TypeError: Cannot read properties of undefined (reading 'col')
at Snake.checkCollision (snake.html:148:43)
at core (snake.html:191:27)
snake.html:129 
Uncaught TypeError: gameOver is not a function
at Snake.move (snake.html:129:21)
at core (snake.html:186:23)

问题分析

  1. 无参调用checkCollision:core函数中调用snake.checkCollision()时未传入head参数,而checkCollision方法依赖该参数访问col和row属性,导致head为undefined触发报错。
  2. 函数定义顺序错误:gameOver函数定义在core()调用之后,当snake.move()中碰撞触发调用gameOver()时,该函数还未被定义,因此抛出gameOver is not a function错误。
  3. 苹果位置判断逻辑错误:Apple.prototype.move中的randomCol !== this.segments判断无效,this指向苹果实例,不存在segments属性,无法避免苹果生成在蛇身体上。

修复方案

  1. 调整gameOver函数位置,移至core()调用之前,确保函数提前声明。
  2. 删除core函数中多余的snake.checkCollision()调用,因为snake.move()内部已经完成碰撞检测并处理游戏结束逻辑。
  3. 修复苹果生成时的位置判断,检查新生成的位置是否与蛇的身体重叠,若重叠则重新生成。

修复后的完整代码

<script>
//Create canvas
const canvas = document.getElementById('canvas'); 
const ctx = canvas.getContext('2d'); 
let width = canvas.width;
let height = canvas.height;
let blockSize = 10; 
let widthInBlocks = width / blockSize; 
let heightInBlocks = height / blockSize; 
let drawBorder = function () { 
    ctx.fillStyle = 'Black';
    ctx.fillRect(0, 0, width, blockSize); 
    ctx.fillRect(0, height - blockSize, width, blockSize); 
    ctx.fillRect(0, 0, blockSize, height); 
    ctx.fillRect(width - blockSize, 0, blockSize, height); 
};
drawBorder();

//Create score 
var score = 0;
let drawScore = function () {
    ctx.clearRect(10, 10, width - 20, 40); 
    ctx.fillStyle = 'Black';
    ctx.textBaseline = 'top'; 
    ctx.textAlign = 'left'; 
    ctx.font = '24px Arial'; 
    ctx.fillText('Score : ' + score, 15, 45); 
};

//Block constructor
const Block = function (col, row) { 
    this.col = col;
    this.row = row;
};
Block.prototype.drawSquare = function (color) { 
    let x = this.col * blockSize; 
    let y = this.row * blockSize;
    ctx.fillStyle = color; 
    ctx.fillRect(x, y, blockSize, blockSize); 
};

//Create food
const circle = function (x, y, radius, color, fill) {
    ctx.fillStyle = color;
    ctx.strokeStyle = color;
    ctx.beginPath();
    ctx.arc(x, y, radius, 0, Math.PI * 2, false);
    if (fill) {
        ctx.fill();
    } else {
        ctx.stroke();
    }
};
Block.prototype.drawCircle = function (color) {
    let x = this.col * blockSize + blockSize / 2; 
    let y = this.row * blockSize + blockSize / 2; 
    ctx.fillStyle = color; 
    circle(x, y, blockSize / 2, color, true); 
};
let Apple = function () {
    this.position = new Block(10, 10);
}; 
Apple.prototype.draw = function () {
    this.position.drawCircle(colorList[Math.floor(Math.random() * 7)]);
};
Apple.prototype.move = function () { 
    let randomCol, randomRow;
    let isOnSnake;
    do {
        isOnSnake = false;
        randomCol = Math.floor(Math.random() * (widthInBlocks - 2)) + 1; 
        randomRow = Math.floor(Math.random() * (heightInBlocks - 2)) + 1;
        // 检查是否在蛇身体上
        for (let i = 0; i < snake.segments.length; i++) {
            if (randomCol === snake.segments[i].col && randomRow === snake.segments[i].row) {
                isOnSnake = true;
                break;
            }
        }
    } while (isOnSnake);
    this.position = new Block(randomCol, randomRow); 
};

//Setting keycode
const directions = {
    37:'left',
    38:'up',
    39:'right',
    40:'down'
};
$('body').keydown(function (event) { 
    let newDirection = directions[event.keyCode]; 
    if (newDirection !== undefined) { 
        snake.setDirection(newDirection);
    }
});

//Create snake
var colorList = ['Blue','Green','Red','Gold','Silver','Purple','Cyan']
var Snake = function () { 
    this.segments = [ 
        new Block(7,5),
        new Block(6,5),
        new Block(5,5)
    ];
    this.direction = 'right'; 
    this.nextDirection = 'right'; 
};
Snake.prototype.draw = function () { 
    for (let i = 0; i < this.segments.length; i ++) { 
        this.segments[i].drawSquare(colorList[Math.floor(Math.random() * 7)]); 
    };
};
//Setting moving directions
Snake.prototype.move = function () {
    let head = this.segments[0];
    let newHead; 
    this.direction = this.nextDirection;

    if (this.direction === 'right'){
        newHead = new Block(head.col + 1, head.row);
    } else if (this.direction === 'left') {
        newHead = new Block(head.col - 1, head.row);
    } else if (this.direction === 'up') {
        newHead = new Block(head.col, head.row - 1);
    } else if (this.direction === 'down') {
        newHead = new Block(head.col, head.row + 1)
    }

    if (this.checkCollision(newHead)) { 
        gameOver(); 
        return; 
    }

    this.segments.unshift(newHead); 

    if (newHead.equal(apple.position)) { 
        score ++; 
        apple.move();
        aniTime -= 1; 
    } else {
        this.segments.pop(); 
    }
};
//Define collision
Block.prototype.equal = function (otherBlock) { 
    return this.col === otherBlock.col && this.row === otherBlock.row; 
};
Snake.prototype.checkCollision = function (head) {
    var leftCollision = (head.col === 0);
    var topCollision = (head.row === 0); 
    var rightCollision = (head.col === widthInBlocks - 1); 
    var bottomCollision = (head.row === heightInBlocks - 1); 

    var wallCollision = leftCollision || topCollision || 
    rightCollision || bottomCollision; 

    var selfCollision = false; 

    for (let i = 0; i < this.segments.length; i ++) {
        if (head.equal(this.segments[i])) {
            selfCollision = true;
        }
    }
    return wallCollision || selfCollision 
};

Snake.prototype.setDirection = function (newDirection) {
    if (this.direction === 'up' && newDirection === 'down') {
        return; 
    } else if (this.direction === 'right' && newDirection ==='left') { 
        return;
    } else if (this.direction === 'down' && newDirection ==='up') { 
        return;
    } else if (this.direction === 'left' && newDirection ==='right') { 
        return;
    }
    this.nextDirection = newDirection; 
};

//Game over condition
var gameOver = function () {
    clearTimeout(timeOutId);
    ctx.font = '60px Arial';
    ctx.fillStyle = 'Black';
    ctx.textAlign = 'center';
    ctx.textBaseline = 'middle';
    ctx.fillText('Game Over', width / 2, height / 2);
};

//run the game
let snake = new Snake(); 
let apple = new Apple(); 
var aniTime = 100; 
var timeOutId; // 声明全局变量避免未定义
function core () { 
    ctx.clearRect(0, 0, width, height); 
    drawScore(); 
    snake.move(); 
    snake.draw(); 
    apple.draw(); 
    drawBorder();
    timeOutId = setTimeout(core, aniTime);
};
core();
</script>

额外说明

  • 修复了textBaseLine的拼写错误,改为正确的textBaseline,避免文本渲染异常。
  • 声明了全局变量timeOutId,避免gameOver中访问未定义变量。

内容的提问来源于stack exchange,提问作者Panda CHENG

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.03 13:50:43