Created
April 17, 2018 09:36
-
-
Save EasonChang0115/7c225f7cae64d592ef2731137d768bf0 to your computer and use it in GitHub Desktop.
貪吃蛇
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
canvas#mycanvas | |
.panel | |
h2 Score: 0 | |
button(onclick="game.startGame()") Start |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 遊戲中用到的向量物件 | |
var Vector = function(x,y){ | |
this.x = x || 0; | |
this.y = y || 0; | |
} | |
Vector.prototype.add = function(v) { | |
return new Vector(this.x + v.x, this.y + v.y); | |
} | |
Vector.prototype.sub = function(v) { | |
return new Vector(this.x - v.x, this.y - v.y); | |
} | |
Vector.prototype.length = function() { | |
return Math.aqrt(this.x * this.x + this.y * this.y); | |
} | |
Vector.prototype.set = function(x,y) { | |
this.x = x; | |
this.y = y; | |
} | |
Vector.prototype.equal = function(v) { | |
return this.x === v.x && this.y === v.y; | |
} | |
Vector.prototype.mul = function(s) { | |
return new Vector(this.x * s, this.y * s); | |
} | |
Vector.prototype.clone = function() { | |
return new Vector(this.x , this.y); | |
} | |
// 蛇物件 | |
var Snake = function(){ | |
this.body = []; | |
this.maxLength = 5; | |
this.head = new Vector(); | |
this.speed = new Vector(1,0); | |
this.direction = 'Right'; | |
} | |
Snake.prototype.update = function(){ | |
let newHead = this.head.add(this.speed); | |
this.body.push(this.head); | |
this.head = newHead; | |
while(this.body.length > this.maxLength){ | |
this.body.shift(1); | |
} | |
} | |
Snake.prototype.setDirection = function(dir){ | |
var target = {}; | |
if(dir === 'Up'){ | |
target = new Vector(0,-1); | |
} | |
if(dir === 'Down'){ | |
target = new Vector(0,1); | |
} | |
if(dir === 'Right'){ | |
target = new Vector(1,0); | |
} | |
if(dir === 'Left'){ | |
target = new Vector(-1,0); | |
} | |
if(target.equal(this.speed.mul(-1)) === false){ | |
this.speed = target; | |
} | |
} | |
Snake.prototype.checkBoundary = function(gameWidth){ | |
let xInRange = 0 <= this.head.x && this.head.x <= gameWidth; | |
let yInRange = 0 <= this.head.y && this.head.y <= gameWidth; | |
return xInRange && yInRange; | |
} | |
// 遊戲中用到的遊戲物件 | |
var Game = function(){ | |
this.bw = 12;//格子寬度 | |
this.bs = 2;//格子間距 | |
this.gameWidth = 40; | |
this.speed = 30;//速度 | |
this.snake = new Snake(); | |
this.foods = []; | |
this.start = false; | |
} | |
Game.prototype.startGame = function(){ | |
this.start = true; | |
this.snake = new Snake(); | |
$('.panel').hide(); | |
$('h2').text(`Score: 0`); | |
this.playSound("C#5",-10,0); | |
this.playSound("E5",-10,200); | |
} | |
Game.prototype.endGame = function(){ | |
this.playSound("A3",-10,100); | |
this.playSound("E2",-10,200); | |
this.playSound("A2",-10,300); | |
this.start = false; | |
$('h2').text(`Score: ${(this.snake.maxLength - 5) * 10 }`); | |
$('.panel').show(); | |
} | |
Game.prototype.init = function(){ | |
this.canvas = document.getElementById('mycanvas'); | |
this.canvas.width = this.bw * this.gameWidth + this.bs * (this.gameWidth - 1); | |
this.canvas.height = this.bw * this.gameWidth + this.bs * (this.gameWidth - 1); | |
this.ctx = this.canvas.getContext('2d'); | |
this.render(); | |
this.update(); | |
this.generateFood(); | |
} | |
Game.prototype.getPosition = function(x,y) { | |
return new Vector( | |
x * this.bw + (x - 1) * this.bs, | |
y * this.bw + (y - 1) * this.bs | |
); | |
} | |
Game.prototype.drawBlock = function(v,color) { | |
this.ctx.fillStyle = color; | |
var pos = this.getPosition(v.x, v.y); | |
this.ctx.fillRect(pos.x,pos.y,this.bw,this.bw); | |
} | |
Game.prototype.drawEffect = function(x,y){ | |
var r = 2; | |
var pos = this.getPosition(x,y); | |
var _this = this; | |
var effect = function(){ | |
r++; | |
_this.ctx.strokeStyle = `rgba(255,0,0,${(100 - r) / 100})`; | |
_this.ctx.beginPath(); | |
_this.ctx.arc(pos.x + _this.bw/2, pos.y + _this.bw/2, r, 0, Math.PI*2); | |
_this.ctx.stroke(); | |
if(r<100){ | |
requestAnimationFrame(effect); | |
} | |
} | |
requestAnimationFrame(effect); | |
} | |
Game.prototype.generateFood = function(){ | |
var x = parseInt(Math.random() * this.gameWidth); | |
var y = parseInt(Math.random() * this.gameWidth); | |
this.foods.push(new Vector(x,y)); | |
this.drawEffect(x,y); | |
this.playSound("E5",-15); | |
this.playSound("A5",-15,50); | |
} | |
Game.prototype.render = function() { | |
this.ctx.fillStyle = "rgba(0,0,0,0.3)"; | |
this.ctx.fillRect(0,0,this.canvas.width,this.canvas.height); | |
for( var x = 0; x < this.gameWidth; x++){ | |
for( var y = 0; y < this.gameWidth; y++){ | |
this.drawBlock(new Vector(x,y),"rgba(255,255,255,0.1)"); | |
} | |
} | |
// 畫出蛇 | |
this.snake.body.forEach( (sp,i) => { | |
this.drawBlock(sp,'white'); | |
}); | |
// 畫出食物 | |
this.foods.forEach( (food,i) => { | |
this.drawBlock(food,'red'); | |
}); | |
requestAnimationFrame(()=>{this.render()}); | |
} | |
Game.prototype.update = function() { | |
if(this.start){ | |
this.playSound("A2",-20); | |
this.snake.update(); | |
this.foods.forEach((food,i)=>{ | |
if(this.snake.head.equal(food)){ | |
this.snake.maxLength++; | |
this.foods.splice(i,1); | |
this.generateFood(); | |
} | |
}); | |
this.snake.body.forEach(bp => { | |
if(this.snake.head.equal(bp)){ | |
console.log('撞到身體'); | |
this.endGame(); | |
} | |
}); | |
if(this.snake.checkBoundary(this.gameWidth) === false){ | |
console.log('撞到邊界'); | |
this.endGame(); | |
} | |
} | |
this.speed = Math.sqrt(this.snake.body.length) + 5; | |
setTimeout(()=>{ | |
this.update(); | |
},parseInt(1000/this.speed)); | |
} | |
Game.prototype.playSound = function(note,vol,when) { | |
setTimeout(function(){ | |
var synth = new Tone.Synth().toMaster(); | |
synth.volumne = vol || -12; | |
synth.triggerAttackRelease(note,'8n') | |
},when || 0); | |
} | |
var game = new Game(); | |
game.init(); | |
$(window).keydown(function(evt){ | |
game.snake.setDirection(evt.key.replace('Arrow','')); | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.js"></script> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/tone/13.0.1/Tone.js"></script> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
html,body | |
width: 100% | |
height: 100% | |
margin: 0 | |
display: flex | |
justify-content: center | |
align-items: center | |
background-color: #000 | |
canvas | |
max-width: 100% | |
.panel | |
position: absolute | |
color: #fff | |
display: flex | |
justify-content: center | |
align-items: center | |
flex-direction: column | |
button | |
background-color: transparent | |
color: #fff | |
border-radius: 20px | |
padding: 5px 20px | |
cursor: pointer | |
outline: none | |
transition: all .3s | |
&:hover | |
background-color: #fff | |
color: #000 | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment