简介
代码
$(function() {
const FRAME_INTERVAL = 25; // 相当于每秒刷新40帧
const SPEED_DOWN = 4; // 速度相对于刷新频率进行减速
const BLOCK_SIZE = 15;
const MAP_SIZE_X = 20;
const MAP_SIZE_Y = 20;
const LABEL = 'metalSnake';
const MAIN_COLOR = 'rgba(0,0,0,.5)';
const SECOND_COLOR = 'rgba(0,0,0,.2)';
const BG_COLOR = '#fff';
const TIP_VALUE = {
'first': 'PRESS ENTER TO START',
'second': 'PRESS SPACE TO PAUSE',
'third': 'GAME OVER \n PRESS ENTER TO RESTART'
};
const SNAKE_BODY_INIT = [{
x: 2,
y: 0
}, {
x: 1,
y: 0
}, {
x: 0,
y: 0
}];
const SNAKE_BODY_RESET = [{
x: 2,
y: 0
}, {
x: 1,
y: 0
}, {
x: 0,
y: 0
}];
new Screen().init();
/**
* Screen module is a manage module
*/
function Screen() {
this.screen = $('#screen');
this.map = new Map();
this.tip = new Tip(LABEL);
this.snake = new Snake(SNAKE_BODY_INIT);
this.apple = new Apple();
this.isflash = true; // 设置 snake head 是否闪烁
this.flashFlag = true;
this.timer = null;
this.appleCoords = this.apple.getAppleCoords(this.snake);
this.count = 0; // 减速计数器
this.isOver = false;
// mixed area
this.init = () => {
if (this.isOver) {
this.snake.bodys = SNAKE_BODY_RESET;
}
this.initMap();
this.initTip();
this.initInputListener()
}
this.initMap = () => {
this.screen.append(this.map.canvas);
this.clearMap();
this.render()
}
this.initTip = () => {
this.screen.append(this.tip.pad);
this.tip.setTipValue(TIP_VALUE.first);
}
this.initInputListener = () => {
$(window).on('keydown', (e) => {
switch (e.keyCode) {
case 32:
this.pause();
break;
case 13:
if (this.isOver) {
this.init();
this.isOver = false;
} else {
this.start();
}
break;
case 37:
if (this.snake.direction != 'right') {
this.snake.directionQ.push('left');
}
break;
case 38:
if (this.snake.direction != 'down') {
this.snake.directionQ.push('up');
}
break;
case 39:
if (this.snake.direction != 'left') {
this.snake.directionQ.push('right');
}
break;
case 40:
if (this.snake.direction != 'up') {
this.snake.directionQ.push('down');
}
break;
}
})
};
this.start = () => {
this.pause();
this.timer = setInterval(this.run, FRAME_INTERVAL);
this.tip.setTipValue(TIP_VALUE.second);
}
this.pause = () => {
if (this.timer != null) {
clearInterval(this.timer);
}
this.tip.setTipValue(TIP_VALUE.first);
}
this.over = () => {
if (this.timer != null) {
clearInterval(this.timer);
}
this.tip.setTipValue(TIP_VALUE.third);
this.isOver = true;
}
this.run = () => {
if (this.count % SPEED_DOWN == 0) {
this.eatAppleCheck();
this.snake.moveCheck();
this.snake.borderCheck();
if (!this.snake.overCheck()) {
this.over();
}
this.count = 0;
}
this.count++;
this.render(this.isflash);
}
this.eatAppleCheck = () => {
if (this.snake.bodys[0].x == this.appleCoords.x && this.snake.bodys[0].y == this.appleCoords.y) {
this.snake.bodys.push({
x: null,
y: null
});
this.appleCoords = this.apple.getAppleCoords(this.snake);
}
}
// left area
this.clearMap = () => {
for (let i = 0; i < MAP_SIZE_X; i++) {
for (let j = 0; j < MAP_SIZE_Y; j++) {
$('<div>').css({
'position': 'absolute',
'box-sizing': 'border-box',
'top': BLOCK_SIZE * j + 'px',
'left': BLOCK_SIZE * i + 'px',
'width': BLOCK_SIZE + 'px',
'height': BLOCK_SIZE + 'px',
'background-color': BG_COLOR
}).appendTo(this.map.canvas);
}
}
}
this.render = (isFlash = false) => {
let headColor = MAIN_COLOR;
if (isFlash) {
headColor = this.flashFlag ? MAIN_COLOR : SECOND_COLOR;
this.flashFlag = !this.flashFlag;
}
this.clearMap();
this.map.canvas.empty();
for (let i = 0; i < this.snake.bodys.length; i++) {
if (i == 0) {
$('<div>').css({
'position': 'absolute',
'top': this.snake.bodys[i].y * BLOCK_SIZE + 'px',
'left': this.snake.bodys[i].x * BLOCK_SIZE + 'px',
'width': BLOCK_SIZE,
'height': BLOCK_SIZE,
'background-color': headColor
}).appendTo(this.map.canvas);
} else {
$('<div>').css({
'position': 'absolute',
'top': this.snake.bodys[i].y * BLOCK_SIZE + 'px',
'left': this.snake.bodys[i].x * BLOCK_SIZE + 'px',
'width': BLOCK_SIZE,
'height': BLOCK_SIZE,
'background-color': MAIN_COLOR
}).appendTo(this.map.canvas);
}
}
$('<div>').css({
'position': 'absolute',
'top': this.appleCoords.y * BLOCK_SIZE + 'px',
'left': this.appleCoords.x * BLOCK_SIZE + 'px',
'width': BLOCK_SIZE,
'height': BLOCK_SIZE,
'background-color': MAIN_COLOR
}).appendTo(this.map.canvas);
}
//right area
};
// left map module
function Map() {
this.coords = {
x: MAP_SIZE_X,
y: MAP_SIZE_Y
};
this.canvas = $('<div>').css({
'position': 'absolute',
'box-sizing': 'border-box',
'left': '10px',
'top': '10px',
'width': '300px',
'height': '300px',
'background-color': BG_COLOR
});
}
function Snake(initBody) {
this.bodys = initBody;
this.directionQ = [];
this.direction = 'right';
this.moveCheck = () => {
if (this.directionQ.length > 0) {
this.direction = this.directionQ.shift();
}
switch (this.direction) {
case 'left':
for (let i = this.bodys.length; i > 0; i--) {
if (i - 1 == 0) {
this.bodys[i - 1].x--;
} else {
this.bodys[i - 1].x = this.bodys[i - 1 - 1].x;
this.bodys[i - 1].y = this.bodys[i - 1 - 1].y;
}
}
break;
case 'up':
for (let i = this.bodys.length; i > 0; i--) {
if (i - 1 == 0) {
this.bodys[i - 1].y--;
} else {
this.bodys[i - 1].x = this.bodys[i - 1 - 1].x;
this.bodys[i - 1].y = this.bodys[i - 1 - 1].y;
}
}
break;
case 'right':
for (let i = this.bodys.length; i > 0; i--) {
if (i - 1 == 0) {
this.bodys[i - 1].x++;
} else {
this.bodys[i - 1].x = this.bodys[i - 1 - 1].x;
this.bodys[i - 1].y = this.bodys[i - 1 - 1].y;
}
}
break;
case "down":
for (let i = this.bodys.length; i > 0; i--) {
if (i - 1 == 0) {
this.bodys[i - 1].y++;
} else {
this.bodys[i - 1].x = this.bodys[i - 1 - 1].x;
this.bodys[i - 1].y = this.bodys[i - 1 - 1].y;
}
}
break;
}
}
this.borderCheck = () => {
if (this.bodys[0].x >= MAP_SIZE_X) {
this.bodys[0].x = 0;
} else if (this.bodys[0].x < 0) {
this.bodys[0].x = MAP_SIZE_X - 1;
} else if (this.bodys[0].y >= MAP_SIZE_Y) {
this.bodys[0].y = 0;
} else if (this.bodys[0].y < 0) {
this.bodys[0].y = MAP_SIZE_Y - 1;
}
}
this.overCheck = () => {
for (let i = 1; i < this.bodys.length; i++) {
if (this.bodys[0].x == this.bodys[i].x && this.bodys[0].y == this.bodys[i].y) {
return false;
}
}
return true;
}
}
function Apple() {
this.getAppleCoords = snake => {
let rX = Math.floor(Math.random() * MAP_SIZE_X);
let rY = Math.floor(Math.random() * MAP_SIZE_Y)
if (snake.bodys.every(e => {
return e.x != rX || e.y != rY;
})) {
return {
x: rX,
y: rY
};
} else {
this.getAppleCoords(snake);
}
}
}
// right tips module
function Tip(label) {
this.pad = $('<div>').css({
'position': 'absolute',
'box-sizing': 'border-box',
'left': '320px',
'top': '10px',
'width': '150px',
'height': '300px',
'background-color': 'transparent'
})
this.topPad = $('<div>').css({
'box-sizing': 'border-box',
'width': '150px',
'height': '200px',
'border': '1px solid #ccc',
'background-color': BG_COLOR
}).appendTo(this.pad);
this.label = $('<p>').css({
'box-sizing': 'border-box',
'width': '150px',
'height': '100px',
'line-height': '100px',
'text-align': 'center',
'font-size': '24px',
'color': MAIN_COLOR,
'background-color': 'inherit'
}).text(label).appendTo(this.pad);
this.setTipValue = (value) => {
$('<p>').text(value).appendTo(this.topPad.empty());
}
}
});