|
// Usage: |
|
// <canvas id='field' width='600' height='600'>Loading...</div> |
|
// <script src='script.js'></script> |
|
// <script> |
|
// window.onload = () => { let game = new Game({canvasId: 'field'}); }; |
|
// </script> |
|
class Game { |
|
constructor({ canvasId }){ |
|
this.grid = { tiles: 20 , size: 30 }; |
|
this.apple = new Apple(); //{ position: { x: 15 , y: 15 }, calories: 1 }; |
|
this.snake = new Snake(); |
|
this.init(canvasId); |
|
} |
|
|
|
init(canvasId){ |
|
let canvas = document.getElementById(canvasId); |
|
this.context = canvas.getContext('2d'); // GUI |
|
addEventListener('keydown', (e) => { this.userInput(e) } ); // User input |
|
setInterval( () => { this.game() }, 1000 / 8); // Game loop |
|
} |
|
|
|
userInput(event){ |
|
const north = { x: +0, y: -1 }; |
|
const east = { x: +1, y: +0 }; |
|
const south = { x: +0, y: +1 }; |
|
const west = { x: -1, y: +0 }; |
|
const arrows = { left: 37, up: 38, right: 39, down: 40}; |
|
const bearings = { |
|
[arrows.left]: west, |
|
[arrows.up]: north, |
|
[arrows.right]: east, |
|
[arrows.down]: south |
|
} |
|
|
|
let bearing = bearings[event.keyCode]; |
|
this.snake.head(bearing); |
|
} |
|
|
|
game(){ |
|
this.snake.move(); |
|
let ap = this.apple.position; |
|
let sp = this.snake.position; |
|
// console.log(`apple: ${ap.x},${ap.y} snake: ${sp.x},${sp.y} `) |
|
if (this.snake.position.x === this.apple.position.x && |
|
this.snake.position.y === this.apple.position.y) { |
|
this.levelUp(); |
|
} |
|
|
|
this.draw(); |
|
} |
|
|
|
levelUp(){ |
|
let calories = this.apple.calories; |
|
this.snake.eat(this.apple); |
|
delete this.apple; |
|
// optional: randomly increase apple nutritive value: |
|
// calories += ( Math.random(5) > 3 ? 0 : 1 ); |
|
this.apple = Apple.spawn({ max: this.grid.tiles, calories: calories }); |
|
} |
|
|
|
draw() { |
|
// canvas |
|
this.context.fillStyle = "black"; |
|
this.context.fillRect(0, 0, this.grid.tiles * this.grid.size, this.grid.tiles * this.grid.size); |
|
|
|
// snake |
|
for(var i = 0; i < this.snake.trail.length; i++) { |
|
this.drawSquare(this.snake.trail[i], "lime"); |
|
} |
|
|
|
// apple |
|
this.drawSquare(this.apple.position, "red"); |
|
} |
|
|
|
drawSquare(object, color){ |
|
this.context.fillStyle = color; |
|
this.context.fillRect( |
|
object.x * this.grid.size, |
|
object.y * this.grid.size, |
|
this.grid.size - 2, |
|
this.grid.size - 2); |
|
} |
|
} |
|
|
|
class Snake { |
|
constructor(options = {}) { |
|
const defaults = { |
|
x: 10 , y: 10, max: 20, |
|
tail: 3, |
|
velocity: {x: 0, y: 0} |
|
}; |
|
Object.assign(this, defaults, options); |
|
this.initialLength = options.tail || defaults.tail; |
|
this.trail = []; |
|
} |
|
|
|
get position(){ |
|
return { x: this.x, y: this.y }; |
|
} |
|
|
|
head(bearing){ |
|
this.velocity.x = bearing.x; |
|
this.velocity.y = bearing.y; |
|
} |
|
|
|
move(){ |
|
this.x += this.velocity.x; |
|
this.y += this.velocity.y; |
|
this._wrapWalls(); |
|
this._detectCollision(); |
|
this._updateTrail(); |
|
} |
|
|
|
eat (food){ |
|
this.tail += food.calories; |
|
} |
|
|
|
die (){ |
|
this.tail = this.initialLength; |
|
} |
|
|
|
_wrapWalls(){ |
|
if (this.x < 0) { this.x = this.max - 1; } // wrap left->right |
|
if (this.x > this.max - 1) { this.x = 0; } // wrap right->left |
|
if (this.y < 0) { this.y = this.max - 1; } // wrap bottom->top |
|
if (this.y > this.max - 1) { this.y = 0; } // wrap top->bottom |
|
} |
|
|
|
_detectCollision(){ |
|
for(var i = 0; i < this.trail.length; i++) { |
|
if (this.trail[i].x === this.x && this.trail[i].y === this.y) { |
|
this.die(); |
|
} |
|
} |
|
} |
|
|
|
_updateTrail(){ |
|
this.trail.push({ x: this.x, y: this.y }); |
|
while(this.trail.length > this.tail) { |
|
this.trail.shift(); |
|
} |
|
} |
|
} |
|
|
|
class Apple { |
|
constructor(options = {}) { |
|
const defaults = { x: 15 , y: 15, calories: 1}; |
|
Object.assign(this, defaults, options); |
|
} |
|
|
|
get position(){ |
|
return { x: this.x, y: this.y }; |
|
} |
|
|
|
static spawn({ max, calories }){ |
|
let ax = Math.floor(Math.random() * max); |
|
let ay = Math.floor(Math.random() * max); |
|
let sprout = new Apple({ x: ax, y: ay, calories: calories}); |
|
return sprout; |
|
} |
|
} |