Click here to Skip to main content
15,879,535 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
I recently decided to attempt to make my first ever OOP project. I like video games, so I decided to go with snake. So far, I have gotten the snake movement and apple almost completely finished. However, the one problem is that the player can trick the game into letting the snake move the opposite direction immediately by pressing two keys very quickly in sequence (ex: snake is moving right and player inputs up and left quickly). Can someone help me out here? Any help is very appreciated! Let me know if any clarification is needed!

HTML
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Snake Game</title>
    <link rel="stylesheet" href="snake.css">
</head>
<body>
    <canvas id="canvas"></canvas>
    <script src="snake.js"></script>
</body>
</html>


CSS
#canvas {
    position: absolute;
    background-color: black;
    width: 100%;
    height: 100%;
    top: 0;
    left: 0;
}

@font-face {
    font-family: 'Joystix';
    font-style: normal;
    font-weight: 400;
    src: local('Joystix'), url('https://fonts.cdnfonts.com/s/7419/joystix.woff') format('woff');
}


JavaScript
const canvas = document.querySelector('#canvas');
const ctx = canvas.getContext("2d");
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
const snakeWidth = 20;
const snakeHeight = 20;
const blockSize = snakeWidth;
let snakeX = Math.floor(canvas.width / blockSize / 2) * 20;
let snakeY = Math.floor(canvas.height / blockSize / 2) * 20;
let speedX = 0;
let speedY = 0;
const snakeArray = [];
let prevKey = '';
const posHistory = [];
const canvasArea = canvas.width * canvas.height;
const rangeX = Math.trunc(canvas.width / snakeWidth);
const rangeY = Math.trunc(canvas.height / snakeHeight);
let randX = Math.floor((Math.random() * rangeX)) * snakeWidth;
let randY = Math.floor((Math.random() * rangeY)) * snakeHeight;
let time = 0;
const perimeter = [];
let stop = false;
let start = true;

// load Joystix font in
let joystix = new FontFace("Joystix", "url(https://fonts.cdnfonts.com/s/7419/joystix.woff)");

joystix.load().then((font) => {
    document.fonts.add(font);
  
    console.log('Font loaded');
  });

// Makes sure canvas doesn't get distorted
canvas.addEventListener("resize", () => {
    canvas.width = window.innerWidth;
    canvas.height = window.innerHeight;
    snakeArray[0].draw();
    apple.drawApple();
});

// Snake 
class Snake {
    constructor() {
        this.width = snakeWidth;
        this.height = snakeHeight;
        this.x = snakeX;
        this.y = snakeY;
        this.speedX = speedX;
        this.speedY = speedY;
    }

    updateHead() {
        posHistory.push([this.x, this.y]);
        this.x += (blockSize * speedX);
        this.y += blockSize * speedY;
        if (posHistory.length >= canvasArea)
            posHistory.pop();
    }

    updateTail(index) {
        this.x = posHistory[posHistory.length - index][0];
        this.y = posHistory[posHistory.length - index][1];
    }

    snakeCollision() {
        for (let i = 1; i < snakeArray.length; i++) {
                if (start === false) {
                    if (this.x === posHistory[posHistory.length - i][0] && this.y === posHistory[posHistory.length - i][1]) {
                        gameOver();
                    }
                }
        }
    }

    draw() {
        ctx.fillStyle = 'green';
        ctx.fillRect(this.x, this.y, this.width, this.height);
    }
}
snakeArray.push(new Snake());

class Apple {
    constructor() {
        this.width = snakeWidth;
        this.height = snakeHeight;
        this.x = randX;
        this.y = randY;
    }

    drawApple() {
        ctx.fillStyle = 'red';
        ctx.fillRect(this.x, this.y, this.width, this.height);
    }

    appleCollision() {
        for (let i = 0; i < snakeArray.length; i++) {
            if (Math.abs(this.x - snakeArray[i].x) <= blockSize - 1 && Math.abs(this.y - snakeArray[i].y) <= blockSize - 1) {
                start = false;
                snakeArray.push(new Snake());
                randX = Math.floor((Math.random() * rangeX)) * snakeWidth;
                randY = Math.floor((Math.random() * rangeY)) * snakeHeight;
                this.x = randX;
                this.y = randY;
;            }
        }
    }
}
const apple = new Apple(); 

// Controls snake
document.addEventListener("keydown", (event) => {
    switch (event.key) {
        case 'ArrowUp': 
            // If not going down
            if (prevKey !== 'down') {
                speedX = 0;
                speedY = -1;
                prevKey = 'up';
            }
            break;
        case 'ArrowRight':
            // If not going left
            if (prevKey !== 'left') {
                speedX = 1;
                speedY = 0;
                prevKey = 'right';
            }
            break;
        case 'ArrowDown':
            // If not going up
            if (prevKey !== 'up') {
                speedX = 0;
                speedY = 1;
                prevKey = 'down';
            }
            break;
        case 'ArrowLeft':
            // If not going right
            if (prevKey !== 'right') {
                speedX = -1;
                speedY = 0; 
                prevKey = 'left';
            }
            break;   
    }
});

function handleSnake() {   
    snakeArray[0].updateHead();
    for (let i = 1; i < snakeArray.length; i++) {
        snakeArray[i].updateTail(i);
    }

    for (let j = 0; j < snakeArray.length; j++) {
        snakeArray[j].draw();
    }

    snakeArray[0].snakeCollision();
}

function handleApple() {
    apple.appleCollision();
    apple.drawApple();
}

function border() {
    if (snakeArray[0].x < 0 || snakeArray[0].x > canvas.width || snakeArray[0].y < 0 || snakeArray[0].y > canvas.height)
        gameOver();
}

function gameOver() {
    ctx.font = "30px joystix";
    ctx.fillStyle = "white";
    ctx.textAlign = "center";
    ctx.fillText("GAME OVER", canvas.width / 2, canvas.height / 2);
    stop = true;
}

function animate() {
    if (time % 10 === 0) {
        ctx.clearRect(0, 0, canvas.width, canvas.height);
        handleSnake();
        handleApple();
        border();
    }
    time++;
    
    if (stop === false)
        requestAnimationFrame(animate);
}
animate();


What I have tried:

I have tried using setTimeout() to prevent the inputs from going through so quickly, but either I implemented it incorrectly, or it was the wrong approach.
Posted
Updated 9-May-23 18:24pm
v2
Comments
[no name] 10-May-23 20:22pm    
Nothing to do with "time": up + left is the "opposite" of "right". Or put another way, it's actually "2 lefts" ... and I think you're mixing absolute and relative "turns". There is no "up or down"; just left and right turns and forward and (maybe) backward movement in relative terms (in this "world view").

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900