Javascript Реализация Змеи - PullRequest
       9

Javascript Реализация Змеи

0 голосов
/ 19 апреля 2020

В попытке начать изучение Javascript я решил реализовать известную игру «Змея». Я старался как можно меньше следовать учебникам, чтобы лучше понять, что я делаю.

До сих пор я заставлял свою змею двигаться, есть пищу и «расти», однако при выращивании клетки не следуют за основной клеткой змеи. Я уже некоторое время пытался устранить неисправности, и некоторые возможные решения, которые я придумала, включают: начиная с нескольких ячеек, а не 1, сохраняя положение предыдущего расположения основной ячейки и помещая туда новую ячейку, или даже изменяя реализация полностью.

Я не уверен, какой путь выбрать, и мне кажется, что я близок к тому, чтобы заставить его работать, но просто не совсем. В настоящее время ячейки создаются, но перемещаются в родительскую ячейку и следуют за той же ячейкой.

const canvas = document.getElementById("canvas");
const context = canvas.getContext("2d");

const HEIGHT = 400;
const WIDTH = 400;
const SCALE = 20;

window.addEventListener("keydown", (event) => {
    const direction = event.key.replace("Arrow", "");
    snake.update(direction);
});

function setup() {
    canvas.height = HEIGHT;
    canvas.width = WIDTH;
    food.createFood();

    window.setInterval(() => {
        context.clearRect(0, 0, WIDTH, HEIGHT);
        snake.move(snake.xSpeed, snake.ySpeed);
        food.drawFood();
        snake.draw();
        snake.shiftCells();
    }, 200);
}

let snake = {
    cells: [{ x: 200, y: 200 }],
    position: {
        x: 200,
        y: 200,
    },
    xSpeed: SCALE,
    ySpeed: 0,
    length: 1,
    draw: () => {
        for (cell of snake.cells) {
            context.fillStyle = "#C2F970";
            context.fillRect(cell.x, cell.y, SCALE, SCALE);
        }
    },
    update: (direction) => {
        switch (direction) {
            case "Up":
                snake.ySpeed = -1 * SCALE;
                snake.xSpeed = 0;
                break;
            case "Down":
                snake.ySpeed = 1 * SCALE;
                snake.xSpeed = 0;
                break;

            case "Left":
                snake.xSpeed = -1 * SCALE;
                snake.ySpeed = 0;
                break;

            case "Right":
                snake.xSpeed = 1 * SCALE;
                snake.ySpeed = 0;
                break;
        }
    },
    move: (xDist, yDist) => {
        if (snake.cells[0].x == food.position.x && snake.cells[0].y == food.position.y) {
            snake.eat();
        }

        // x-position
        if (snake.cells[0].x + xDist < 0) {
            snake.cells[0].x = WIDTH;
        } else if (snake.cells[0].x + xDist > WIDTH) {
            snake.cells[0].x = 0;
        } else {
            snake.cells[0].x += xDist;
        }

        // y-position
        if (snake.cells[0].y + yDist < 0) {
            snake.cells[0].y = HEIGHT;
        } else if (snake.cells[0].y + yDist > HEIGHT) {
            snake.cells[0].y = 0;
        } else {
            snake.cells[0].y += yDist;
        }
    },
    shiftCells: () => {
        for (let i = length; i > 1; i--) {
            snake.cells[i] = snake.cells[i - 1];
        }
    },
    eat: () => {
        snake.grow();
        food.createFood();
    },
    grow: () => {
        snake.cells.push({
            x: snake.cells[snake.length - 1].x,
            y: snake.cells[snake.length - 1].y,
        });
        snake.cells[0].x += snake.xSpeed;
        snake.cells[0].y += snake.ySpeed;
        length++;
    },
};

let food = {
    position: {
        x: 0,
        y: 0,
    },
    createFood: () => {
        food.position.x = SCALE * Math.floor(Math.random() * (WIDTH / SCALE));
        food.position.y = SCALE * Math.floor(Math.random() * (HEIGHT / SCALE));
    },
    drawFood: () => {
        context.fillStyle = "#D3FCD5";
        context.fillRect(food.position.x, food.position.y, SCALE, SCALE);
    },
};

setup();

Буду очень признателен за любые отзывы о том, как заставить ячейки следовать должным образом, и даже любые советы о том, как более правильно структурировать мой код (например, следует ли реализовать Snake в другом файле? класс?).

1 Ответ

1 голос
/ 20 апреля 2020

Во-первых, отличные вещи в том, как аккуратно выложили свой код. У вас явно есть большой потенциал в этом, но, тем не менее, есть несколько вещей, которые вы можете улучшить.

Механизм, чтобы заставить рост змеи работать, немного тонок. Ниже приведен рабочий код, но вы, возможно, захотите сначала его проигнорировать и попытаться заставить его работать, учитывая следующие наблюдения:

  • Когда вы получаете доступ к длине змеи, вы используете length not snake.length
  • Ваш l oop для смещения ячеек начинается с индекса, равного длине массива, и заканчивается до того, как он достигает элемента second в массиве. Вы действительно хотите перейти от length - 1 к 1 включительно
  • Вы хотите позвонить shiftCells, прежде чем перемещать первую клетку змеи, в противном случае вы помещаете вторую клетку в new положение первой клетки вместо ее до перемещения змеи
  • Когда вы копируете клетки в хвосте змеи, вы копируете ссылки в ячейки, что приводит ко второй ячейке удерживая ссылку на первый, чтобы оба перемещались в одну и ту же точку на последующих поворотах ... во избежание этого вам лучше просто скопировать координаты, как вы правильно делаете в методе grow
  • Когда змея растет, нужно быть осторожным во время смены. Хвост змеи, если хотите, нужно оставить на один оборот, чтобы он рос. Этого можно достичь, не смещая хвост змеи в повороте, где происходит поедание
  • В вашем методе grow вы увеличиваете первую ячейку в зависимости от скорости. Это не совсем подходящее место, поскольку у вас есть другой код, предназначенный для перемещения первой ячейки и одновременного решения проблемы обтекания

Еще несколько (неполных) замечаний по коду :

  • Вам лучше без свойства snake length, так как эта информация содержится в длине массива cells. Это дублирующая информация, и это означает, что есть риск, что она станет непоследовательной
  • Возможно, вы захотите в ближайшее время изучить идею классов v объектов. Здесь вы используете отдельные объекты без возможности создания уникальных объектов того же типа: это может быть удобно, например, с вашими (x, y) точками

Рабочий код:

const canvas = document.getElementById("canvas");
const context = canvas.getContext("2d");

const HEIGHT = 400;
const WIDTH = 400;
const SCALE = 20;

window.addEventListener("keydown", (event) => {
    const direction = event.key.replace("Arrow", "");
    snake.update(direction);
});

function setup() {
    canvas.height = HEIGHT;
    canvas.width = WIDTH;
    food.createFood();

    window.setInterval(() => {
        context.clearRect(0, 0, WIDTH, HEIGHT);
        snake.move(snake.xSpeed, snake.ySpeed);
        food.drawFood();
        snake.draw();
    }, 200);
}

let snake = {
    cells: [{ x: 200, y: 200 }],
    position: {
        x: 200,
        y: 200,
    },
    xSpeed: SCALE,
    ySpeed: 0,
    length: 1,
    draw: () => {
        snake.cells.forEach( cell => {
            context.fillStyle = "#C2F970";
            context.fillRect( cell.x, cell.y, SCALE, SCALE );
        })
    },
    update: (direction) => {
        switch (direction) {
            case "Up":
                snake.ySpeed = -1 * SCALE;
                snake.xSpeed = 0;
                break;
            case "Down":
                snake.ySpeed = 1 * SCALE;
                snake.xSpeed = 0;
                break;
            case "Left":
                snake.xSpeed = -1 * SCALE;
                snake.ySpeed = 0;
                break;

            case "Right":
                snake.xSpeed = 1 * SCALE;
                snake.ySpeed = 0;
                break;
        }
    },
    move: (xDist, yDist) => {

        if (snake.cells[0].x == food.position.x && snake.cells[0].y == food.position.y) {
            snake.eat();
            snake.shiftCells( snake.length - 1 )
            snake.moveFirstCell( xDist, yDist )
        }
        else {
            snake.shiftCells( snake.length )
            snake.moveFirstCell( xDist, yDist )
        }

        },

    moveFirstCell: ( xDist, yDist ) => { 
        // x-position
        if (snake.cells[0].x + xDist < 0) {
            snake.cells[0].x = WIDTH;
        } else if (snake.cells[0].x + xDist > WIDTH) {
            snake.cells[0].x = 0;
        } else {
            snake.cells[0].x += xDist;
        }

        // y-position
        if (snake.cells[0].y + yDist < 0) {
            snake.cells[0].y = HEIGHT;
        } else if (snake.cells[0].y + yDist > HEIGHT) {
            snake.cells[0].y = 0;
        } else {
            snake.cells[0].y += yDist;
        }

    },
    shiftCells: length => {
        for (let i = length - 1; i >= 1; i--) {
            snake.cells[i].x = snake.cells[i - 1].x;
            snake.cells[i].y = snake.cells[i - 1].y;
        }        
    },
    eat: () => {
        snake.grow();
        food.createFood();
    },
    grow: () => {
        snake.cells.push({
            x: snake.cells[snake.length - 1].x,
            y: snake.cells[snake.length - 1].y,
        });
        snake.length++;
    },
};

let food = {
    position: {
        x: 0,
        y: 0,
    },
    createFood: () => {
        food.position.x = SCALE * Math.floor(Math.random() * (WIDTH / SCALE));
        food.position.y = SCALE * Math.floor(Math.random() * (HEIGHT / SCALE));
    },
    drawFood: () => {
        context.fillStyle = "#D3FCD5";
        context.fillRect(food.position.x, food.position.y, SCALE, SCALE);
    },
};

setup();
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...