Проблема в том, что checkSnakeForFood()
не работает так, как вы ожидаете, потому что newHead
еще не был добавлен в тело змеи к тому времени, когда вы проверяете столкновение с головой пищи.
Проверяется только змеиный хвост, и процедура checkSnakeForFood()
сообщает, что текущее размещение еды (прямо под newHead
) не является столкновением, поэтому тело петли while
не выполняется, и еда не движется , Вот диаграмма того, что происходит:
0123456789
0+---------
1|.........
2|.F######.
3|.......##
^^^^^^^
|
only these segments get
tested for collision
newHead
и food
имеют одинаковое местоположение, [2, 2]
, обозначаемое как F
. Они сталкиваются, поэтому if (headX == food.x && headY == food.y)
верно. Однако, когда на следующей строке вызывается checkSnakeForFood()
, в цикле учитываются только элементы хвоста #
for (let i = 0; i < snake.length; i++)
. checkSnakeForFood()
затем возвращает true, и цикл while
никогда не перемещает пищу.
Вы можете решить эту проблему, unshift
набрав newHead
перед попыткой вызвать checkSnakeForFood()
, следующим образом:
//create new head
let newHead = {
x: headX,
y: headY
}
//add head to snake, *before* checking for collision with the food
snake.unshift(newHead);
//if snake eats food -do this
if (headX == food.x && headY == food.y) {
//create new food position
while (!checkSnakeForFood()) { // <-- now this will successfully detect
// that the snake head is touching the food
Теперь столкновение головы с едой будет регистрироваться правильно, и еда будет перемещаться до тех пор, пока он не столкнется с хвостом змеи или :
0123456789
0+---------
1|.........
2|.F######.
3|.......##
^^^^^^^^
|
all segments are correctly
tested for collision
Однако я рекомендую рефакторинг, чтобы избежать подобных ошибок. Вы можете сделать snake
объектом с функциями-членами для столкновения и перемещения и свойствами для его direction
и tail
. Ваша функция draw
очень перегружена и отвечает гораздо больше, чем обещает ее название.
Отделение newHead
похоже на подверженный ошибкам подход; манипулирование головой непосредственно на массиве избавит вас от путаницы.
А пока вот обновленный код, который заставит вас снова двигаться (не забудьте щелкнуть ссылку Full page
во фрагменте, чтобы правильно играть в игру:
const canvas = document.querySelector('#canvas');
const ctx = canvas.getContext('2d');
//set canvas dimension equal to css dimension
canvas.width = 768;
canvas.height = 512;
//now put those dimensions into variables
const cvsW = canvas.width;
const cvsH = canvas.height;
//create snake unit
const unit = 16;
//create snake array
let snake = [{
x: cvsW / 2,
y: cvsH / 2
}];
//delcare global variable to hold users direction
let direction;
//create food object
let food = {
x: Math.floor(Math.random() * ((cvsW / unit) - 1) + 1) * unit,
y: Math.floor(Math.random() * ((cvsH / unit) - 1) + 1) * unit
}
//read user's direction
document.addEventListener('keydown', changeDirection);
function changeDirection(e) {
//set direction
if (e.keyCode == 37 && direction != 'right') direction = 'left';
else if (e.keyCode == 38 && direction != 'down') direction = 'up';
else if (e.keyCode == 39 && direction != 'left') direction = 'right';
else if (e.keyCode == 40 && direction != 'up') direction = 'down';
}
function draw() {
//refresh canvas
ctx.clearRect(0, 0, cvsW, cvsH);
//draw snake
for (let i = 0; i < snake.length; i++) {
ctx.fillStyle = 'limegreen';
ctx.fillRect(snake[i].x, snake[i].y, unit, unit);
}
//grab head position
let headX = snake[0].x;
let headY = snake[0].y;
//posistion food on board
ctx.fillStyle = 'red';
ctx.fillRect(food.x, food.y, unit, unit);
//send the snake in chosen direction
if (direction == 'left') headX -= unit;
else if (direction == 'up') headY -= unit;
else if (direction == 'right') headX += unit;
else if (direction == 'down') headY += unit;
// //check if snake hit wall
// if(headX < 0 || headY < 0 || headX > (cvsW-unit) || headY > (cvsH-unit)) {
// clearInterval(runGame);
// }
if (headX < 0) headX = cvsW - unit;
else if (headX > cvsW - unit) headX = 0;
else if (headY < 0) headY = cvsH - unit;
else if (headY > cvsH - unit) headY = 0;
// for(let i = 0; i < snake.length; i++) {
// if(headX == snake[i].x && headY == snake[i].y) {
// clearInterval(game);
// }
// }
//create new head
let newHead = {
x: headX,
y: headY
}
//add head to snake
snake.unshift(newHead);
//if snake eats food -do this
if (headX == food.x && headY == food.y) {
//create new food position
while (!checkSnakeForFood()) {
food = {
x: Math.floor(Math.random() * ((cvsW / unit) - 1) + 1) * unit,
y: Math.floor(Math.random() * ((cvsH / unit) - 1) + 1) * unit
}
}
//add 3 units to the snake
for (let i = 30; i > 0; i--) {
snake.unshift(newHead);
}
} else {
//remove tail
snake.pop();
}
}
//run game engine
let runGame = setInterval(draw, 40);
function checkSnakeForFood() {
for (let i = 0; i < snake.length; i++) {
if (snake[i].x === food.x && snake[i].y === food.y) return false;
}
return true;
}
[ 114] <canvas id="canvas"></canvas>
Для переопределения этого метода, необходимо было бы разделить Zend_Db_Table_Abstract на подклассы. Как так:
<?php
abstract class My_Db_Table_Abstract extends Zend_Db_Table_Abstract
{
...
public function fetchAll($where, $order)
{
...
}
...
}
Затем удостоверьтесь, что Ваши модели расширяют My_Db_Table_Abstract вместо этого. Таким образом, Вы всегда наследуете свой переопределенный fetchAll метод.
Да. Просто определите новый fetchAll () метод в Вашей модели с той же конструкцией как метод Zend_db_table_abstract (т.е. тот же вход / вывод) затем в конце Вашего вызова метода родительский метод:
родитель:: fetchAll ($params)
Andrew