Javascript Canvas Game - How To Know When Object Crosses Its Own Path?
Solution 1:
Array as a Queue.
The way this game is traditionally done is using a special array type called a queue. Like a real queue you have items added at one end and removed at the other, or first in first out.
Javascript does not have a special array type for a queue but it has all the functions needed to implement a queue.
Push and shift
Array.push(item); // pushes an item onto the top of the array
Array.shift(); // removes an item from the start of the queue
So for the snake game imagine the head as the end of the queue. Every time it moves one forward you place another item on the queue. At the other end you remove an item if the length of the array is longer than the snake length;
var snake = [];
var snakeLength = 10;
function snakeMove(x,y){ // position to move head
checkForHit(x,y); // see below
snake.push({x:x,y:y}); // put another headpiece on the queue
if(snake.length > snakeLength){ // is the length longer than it should be
snake.shift(); // remove a tail item
}
}
To draw the snake you just iterate each part drawing it at the X,y position
To test if the snake has run its self over use the following function
function checkForHit(x,y){
for(var i = 0; i < snake.length; i++){
if(snake[i].x === x && snake[i].y === y){
// Snake has hit its self
}
}
}
When the snake eats something it traditionally grows in length. this is easily done by simply increasing the length variable. snakeLength += 1 makes the queue longer.
And a demo as i have not played the game in so long why not.
"use strict";
var score = 0;
var canvas = document.createElement("canvas");
var scoreE = document.createElement("div");
scoreE.style.color = "white";
scoreE.style.font = "16px arial";
scoreE.style.position = "absolute";
scoreE.style.top = "10px";
scoreE.style.left = "10px";
scoreE.style.width = "600px";
scoreE.style.textAlign = "center";
scoreE.textContent = "Click canvas area to get focus";
canvas.width = 600;
canvas.height = 200;
var ctx = this.canvas.getContext("2d");
document.body.appendChild(canvas);
document.body.appendChild(scoreE);
var lastKeyDown = 0;
window.addEventListener('keydown', function(e) {
lastKeyDown = e.keyCode;
e.preventDefault()
})
var snakePartSize = 8;
var playWidth = canvas.width /snakePartSize;
var playHeight = canvas.height /snakePartSize;
var snake = [];
var snakeLength = 10;
var snakePosX = 0;
var snakePosY = 0;
var snakeDirX = 0;
var snakeDirY = 0;
var snakeSpeed = 16; // number of frame between moves
var gameOver = true;
var instDrawn = false;
var food = [];
var foodFreq = 60;
var yum = 0;
var yumCol = ["red","orange","yellow"];
function startSnake(){
ctx.fillStyle = "black";
ctx.fillRect(0,0,canvas.width,canvas.height);
snakePosX = Math.floor(playWidth / 2);
snakePosY = Math.floor(playHeight / 2);
snakeDirX = 0;
snakeDirY = 0;
snakeLength = 10;
snake = [];
snakeSpeed = 16;
move(snakePosX,snakePosY); // set first pos
food = [];
score = 0;
}
function testHit(x,y){
if(x < 0 || y < 0 || y >= playHeight || x >= playWidth ){
return true;
}
for(var i = 0; i < snake.length; i ++){
if(snake[i].x === x && snake[i].y === y){
return true;
}
}
}
function testFood(x,y){
for(var i = 0; i < food.length; i ++){
if(food[i].x === x && food[i].y === y){
food.splice(i,1);
i --;
yum = 4;
score += 100;
snakeLength += 1;
if(snakeLength % 4 === 0){
snakeSpeed -= snakeSpeed > 1 ? 1:0;
}
}
}
}
function addFood(){
var x = Math.floor(Math.random() * playWidth );
var y = Math.floor(Math.random() * playHeight );
if(!testHit(x,y)){
food.push({x:x,y:y});
drawFood();
}
}
function move(x,y){
if(testHit(x,y)){
gameOver = true;
return;
}
testFood(x,y);
snake.push({x : x, y : y});
drawSnakeHead();
if(snake.length > snakeLength){
drawSnakeTail();
snake.shift();
}
}
function drawYum(){
for(var i = 0; i < snake.length; i ++){
ctx.fillStyle = yumCol[yum];
ctx.fillRect(snake[i].x*snakePartSize, snake[i].y*snakePartSize, snakePartSize, snakePartSize);
}
}
function drawFood(){
var f = food[food.length-1];
ctx.fillStyle = "green";
ctx.fillRect(f.x*snakePartSize, f.y*snakePartSize, snakePartSize, snakePartSize);
}
function drawSnakeHead(){
var head = snake[snake.length-1];
ctx.fillStyle = "red";
ctx.fillRect(head.x*snakePartSize, head.y*snakePartSize, snakePartSize, snakePartSize);
}
function drawSnakeTail(){
var head = snake[0];
ctx.fillStyle = "black";
ctx.fillRect(head.x*snakePartSize, head.y*snakePartSize, snakePartSize, snakePartSize);
}
var counter = 0;
function update(){
counter += 1;
if(!gameOver){
if(snakeDirX === 0){
if(lastKeyDown === 37){ // left
snakeDirX = -1;
snakeDirY = 0;
}
if(lastKeyDown === 39){ // right
snakeDirX = 1;
snakeDirY = 0;
}
}
if(snakeDirY === 0){
if(lastKeyDown === 38){ // up
snakeDirY = -1;
snakeDirX = 0;
}
if(lastKeyDown === 40){ // down
snakeDirY = 1;
snakeDirX = 0;
}
}
lastKeyDown = 0;
if(counter % foodFreq ===0){
addFood();
}
if(counter % snakeSpeed === 0){
snakePosX += snakeDirX;
snakePosY += snakeDirY;
score += 1;
move(snakePosX ,snakePosY);
}
if((counter % 2 === 0) && yum > 0){
yum -= 1;
drawYum();
}
scoreE.textContent = "Score : "+ score;
}
if(gameOver){
if(!instDrawn){
instDrawn = true;
ctx.fillStyle = "white";
ctx.font = "32px arial";
ctx.textAlign = "center";
ctx.fillText("GAME OVER",canvas.width /2, canvas.height /2);
ctx.font = "16px arial";
ctx.fillText("Press a direction key to start.",canvas.width /2, canvas.height /2+32);
}
if(lastKeyDown >= 37 && lastKeyDown <= 40){
gameOver = false;
instDrawn = false;
counter = -1;
startSnake();
}
}
requestAnimationFrame(update);
}
requestAnimationFrame(update);
startSnake();
Post a Comment for "Javascript Canvas Game - How To Know When Object Crosses Its Own Path?"