DAY 15
0
Modern Web

Day15 - 請蛇上台

``````class Snake {
constructor() {
// 蛇頭位子
this.head = new Vector(0, 0);
// 除了蛇頭外蛇身的位子
this.body = [];
// 蛇移動的步伐
this.step = new Vector(1, 0);
// 蛇頭加上蛇身長度
this.maxLength = 3;
}
update() {
// push head to body
// generate new head
// 整隻蛇往前一步，蛇頭當然也就往前一步
// shift the tail
// 整隻蛇往前一步，蛇尾當然也就往前一步，故刪除舊蛇尾位子
while(this.body.length > this.maxLength){
this.body.shift();
}

};
setDirection(direction) {
var newStep;
switch(direction){
case 'Right':
newStep = new Vector(1,0);
break
case 'Left':
newStep = new Vector(-1,0);
break
case 'Up':
newStep = new Vector(0,-1);
break
case 'Down':
newStep = new Vector(0,1);
break
};
// 蛇本來往左走，如果玩家按下向右鍵，則不理玩家，蛇維持往左步伐
if(!this.step.equal(newStep.mul(-1))){
this.step = newStep;
}
};
// 判斷蛇頭有沒有撞牆
isWithinBoundary(boundary) {
var xWithinBoundary = this.head.x >= 0 && this.head.x < boundary
var yWithinBoundary = this.head.y >= 0 && this.head.y < boundary

return xWithinBoundary && yWithinBoundary;

};
};
``````
``````class Game{
constructor() {
// 舞台由 20 格，每格 26px 的格子組成，且每個之間的間距為 2px
this.blocks = 20;
this.blockWidth = 26;
this.blockSpace = 2;
// 請蛇上台
this.snake = new Snake();
this.foods = [];
this.generateFoods();
this.start = true;
this.bgm = new BGM();
// init
this.init();
}
init() {
// set canvas
this.canvas = document.getElementById('canvasSnake');
this.canvas.width = this.blockWidth * this.blocks + this.blockSpace * (this.blocks - 1);
this.canvas.height = this.canvas.width;
this.ctx = this.canvas.getContext('2d');

// render canvas view
this.render();

// update game data
this.update();

};

render() {
// bg
this.ctx.fillStyle = '#dbe3c5';
this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.width);
// 畫出舞台格子
for (var bX = 0; bX < this.blocks; bX++) {
for (var bY = 0; bY < this.blocks; bY++) {
this.drawBlock(new Vector(bX, bY), '#ced6b8')
};
};

// snake
this.snake.body.forEach(sbPosition =>
this.drawBlock(sbPosition, `rgb(\${144-this.snake.maxLength*3},\${150+this.snake.maxLength},\${128+this.snake.maxLength*2})`));

this.foods.forEach(fPosition => {
this.drawBlock(fPosition, 'rgb(24,174,198)');
});
// render again
requestAnimationFrame(()=>this.render(),1);
};
// 計算我們畫的舞台格子，實際是在 canvas 上多少 px 的位子
getPosition(blockXY) {
return {
x:blockXY.x * (this.blockWidth + this.blockSpace),
y:blockXY.y * (this.blockWidth + this.blockSpace)
}
};
// 畫一格（舞台格子的大小）
drawBlock(blockPos, color) {
var pos = this.getPosition(blockPos);
this.ctx.fillStyle = color;
this.ctx.fillRect(pos.x, pos.y, this.blockWidth, this.blockWidth);
};
update() {
if(this.start){
// 更新蛇的狀態
this.snake.update();
// 確認食物是不是被蛇吃了
this.foods.forEach((food, i) => {
this.generateFoods();
this.foods.splice(i, 1);
this.snake.maxLength ++;
}
});
// 確認蛇是不是輸了
// 蛇有超出邊界嗎
if(!this.snake.isWithinBoundary(this.blocks)){
this.endGame();
};
// 蛇有撞到自己嗎
this.snake.body.forEach((sbPos) =>{
this.endGame();
}
})
};

// update again
setTimeout(()=>this.update(),150);
};
generateFoods() {
var x = parseInt(Math.random() * this.blocks);
var y = parseInt(Math.random() * this.blocks);
var newFood = new Vector(x, y);
this.foods.push(newFood);
};
startGame () {
this.snake = new Snake();
this.start = true;
this.bgm.play();
};
endGame () {
this.start = false;
this.bgm.pause();
};
};
``````