在javascript 贪吃蛇(一) 中,我们创建了Canvas、Snake及Food对象,本部分将继续进行余下的部分。
在开始进行余下部分的介绍前先将Utils.js公布出来,该js文件中设置了一些常用的方法。源码如下:
Array.prototype.indexOf = function(vItem) {
for ( var i = 0; i < this.length; i++) {
if (vItem == this[i]) {
return i;
}
}
return -1;
};
Array.prototype.sameItem = function(vItem) {// 判断元素在数组中出现的次数
var __iNum = 0;// 应用python的私有变量申明方式
for ( var i = 0; i < this.length; i++) {
(this[i] == vItem) ? __iNum++ : __iNum;
}
return __iNum;
};
function createElement(sTagName, sTagId, sText) {
var oElement = document.createElement(sTagName);
if (sTagId != null) {
oElement.setAttribute("id", sTagId);
}
if (sText != null) {
oElement.appendChild(document.createTextNode(sText));
}
return oElement;
}
function RandomEventUtils() {
}
RandomEventUtils.getIntRandomNumFromOneToN = function(iN) {
var iR = Math.random() * iN;
return Math.ceil(iR);
};
RandomEventUtils.instanceRandomSnakeForCanvas = function(oCanvas) {
var __head = this.getIntRandomNumFromOneToN(20 * 20);
if (!oCanvas.whichWallForBrick(__head)) {// 不属于墙
return new Snake(__head, __head - 1, "down");
} else {
return new Snake(34, 35, "right");
}
};
Scoreboard对象:
该对象定义的相对比较简单,仅仅是操作DOM生成显示游戏分数跟登记的相关位置。具体实现如下:
//Scoreboard.js
function Scoreboard() {
}
/*
* 生成如下HTML代码:
* <div id='scoreboard'>
* <div><label>Score</label><span id='score'>0</span></div>
* <div><label>Level</label><span id='level'>0</span></div>
* <center>top ten</center>
* </div>
*
*/
Scoreboard.prototype.draw = function() {
var oFragment = document.createDocumentFragment();// 创建文档碎片
var oDiv = createElement("div", "scoreboard", null);
var oDiv_div1 = createElement("div", null, null);
var oDiv_div1_label = createElement("label", null, "Score");
var oDiv_div1_span = createElement("span", "score", "0");
oDiv_div1.appendChild(oDiv_div1_label);
oDiv_div1.appendChild(oDiv_div1_span);
var oDiv_div2 = createElement("div", null, null);
var oDiv_div2_label = createElement("label", null, "Level");
var oDiv_div2_span = createElement("span", "level", "0");
oDiv_div2.appendChild(oDiv_div2_label);
oDiv_div2.appendChild(oDiv_div2_span);
var oCenter = createElement("center", null, "top ten");
oDiv.appendChild(oDiv_div1);
oDiv.appendChild(oDiv_div2);
oDiv.appendChild(oCenter);
oFragment.appendChild(oDiv);
document.body.appendChild(oFragment);
};
// 显示游戏分数与游戏等级
Scoreboard.prototype.showScoreAndLevel = function(iScore, iLevel) {
document.getElementById('score').innerHTML = iScore;
document.getElementById("level").innerHTML = iLevel;
};
// 初始游戏分数与登记
Scoreboard.prototype.cleanScoreAndLevel = function() {
document.getElementById('score').innerHTML = 0;
document.getElementById("level").innerHTML = 0;
};
// 显示游戏玩家游戏日志
Scoreboard.prototype.showHistory = function() {
var aCookie = document.cookie.split(";");
var oFragment = document.createDocumentFragment();// 创建文档碎片
for ( var i = 0; i < aCookie.length; i++) {
var oSpan = createElement("span", null, aCookie[i]);
oFragment.appendChild(oSpan);
}
var oScoreboard = document.getElementById("scoreboard");
var oTop = document.getElementById('top');
if (oTop != null) {
oScoreboard.removeChild(oTop);
}
oTop = createElement("div", "top", null);
oTop.appendChild(oFragment);
oScoreboard.appendChild(oTop);
};
SnakeGame对象:
这个为游戏对象。属性有:iScore(分数)、iLevel(登记)、timeOut(计时)、canvas(画布)、scoreboard(记分牌)、snake(蛇)、food(食物)
方法有:keyboardEventsListeners(键盘监听事件)、scoreAndSpeed(游戏分数及等级计算)、start(游戏开始)、stop(游戏暂停)等方法。具体实现如下:
// SnakeGame.js
function SnakeGame(oCanvas, oScoreboard, oSnake, oFood) {
this.iScore = 0;
this.iLevel = 0;
this.timeOut;
this.canvas = oCanvas;
this.scoreboard = oScoreboard;
this.snake = oSnake;
this.food = oFood;
}
// 键盘监听事件
SnakeGame.prototype.keyboardEventsListeners = function(oEvent) {
if (window.event) {// ie
// var direc = arguments[0].keyCode;
var direc = window.event.keyCode;
} else if (oEvent.which) {// ff
var direc = oEvent.which;
// var direc = arguments[0].which;
}
if (direc == 37 && this.snake.direction != "right") {// 37-->left
this.snake.direction = "left";
}
if (direc == 39 && this.snake.direction != "left") {// 39-->right
this.snake.direction = "right";
}
if (direc == 38 && this.snake.direction != "down") {// 38-->up
this.snake.direction = "up";
}
if (direc == 40 && this.snake.direction != "up") {// 40-->down
this.snake.direction = "down";
}
};
SnakeGame.prototype.__delay = function(obj, fn, time) {
fnGameDelay = function() {
fn.call(obj);// call方法会把fn方法中的this关键字替换成obj对象
// apply方法同call,但参数要用数组形式。IE中参数为空时不能用apply(obj,null),但FF中是可以的
// fn.apply(obj, new Array());
};
return setTimeout('fnGameDelay()', time);
};
// 记分及游戏级别控制
SnakeGame.prototype.scoreAndSpeed = function() {
this.iScore += Math.pow(2, this.food.iRank)
* Math.ceil(Math.sqrt(Math.pow(5, this.iLevel))) - 2;
var level = this.snake.body.length - 2;
if (level > 0 && level % 7 == 0 && this.iLevel != 10) {
this.iLevel += 1;
}
this.scoreboard.showScoreAndLevel(this.iScore, this.iLevel);
};
// 开始游戏
SnakeGame.prototype.start = function() {
// 当使用typeof param时,param可以声明也可以不用声明。
if (this.snake == undefined) {// null == undefined 为真,但null与undefied是不同的
this.snake = RandomEventUtils.instanceRandomSnakeForCanvas(this.canvas);
}
// 当使用声明变量但没有初始值时才可以使用 param == undefined 否则的话会报错
if (this.food == undefined) {
this.food = new Food();
}
if (this.food.bEat) {
this.scoreAndSpeed();
this.food.productionFoodForSnake(this.snake);
}
this.snake.draw();
this.snake.moveAndFindFood(this.food);
if (this.snake.isHitWall(this.canvas)) {
var sPlayer = prompt("Game Over!/nPlease input your name!", "Anonymous");
this.saveHistory(sPlayer);
this.onceAgain();
return;
}
this.snake.draw();
this.timeOut = this.__delay(this, this.start, 1000 - this.iLevel * 100);
};
// 游戏暂停
SnakeGame.prototype.stop = function() {
clearTimeout(this.timeOut);
};
// 游戏结束后的重新初始化
SnakeGame.prototype.onceAgain = function() {
this.scoreboard.cleanScoreAndLevel();
this.scoreboard.showHistory();
this.snake.cleanUpSnakeBody();
this.food.cleanUpLeftovers();
this.snake = null;
this.food = null;
this.iScore = 0;
this.iLevel = 0;
this.timeOut = null;
};
// 保存玩家游戏记录
SnakeGame.prototype.saveHistory = function(sPlayer) {
if (sPlayer != null && sPlayer != "") {
var oDate = new Date();
var sDate = oDate.getFullYear() + "-" + (oDate.getMonth() + 1) + "-"
+ oDate.getDate() + " " + oDate.getHours() + ":"
+ oDate.getMinutes() + ":" + oDate.getSeconds();
document.cookie = sPlayer + "=" + this.iScore + ":" + sDate;
}
};
// 预加载
SnakeGame.prototype.preload = function() {
// 添加控制按钮
var oDiv = createElement("div", "ctlbtn", null);
var btn_start = createElement("button", "start", "Start");// 开始
var btn_stop = createElement("button", "stop", "Stop");// 暂停
var btn_help = createElement("button", "help", "Help");// 帮助
oDiv.appendChild(btn_start);
oDiv.appendChild(btn_stop);
oDiv.appendChild(btn_help);
document.body.appendChild(oDiv);
this.canvas.draw();// 显示画布
this.scoreboard.draw();// 显示记分牌
this.scoreboard.showHistory();// 显示当前游戏历史排行记录
};
window.onload = function() {
var oCanvas = new Canvas();// 创建canvas对象
var oScoreboard = new Scoreboard();// 创建scoreboard对象
var oSnake;// 声明snake
var oFood;// 声明snake's food
var oSnakeGame = new SnakeGame(oCanvas, oScoreboard, oSnake, oFood);// 创建SnakeGame
oSnakeGame.preload();// SnakeGame预加载
// 为游戏按钮添加监听事件
document.body.onkeydown = function(oEvent) {
oSnakeGame.keyboardEventsListeners.call(oSnakeGame, oEvent);
};
document.getElementById("start").onclick = function() {
oSnakeGame.start.call(oSnakeGame);
};
document.getElementById("stop").onclick = function() {
oSnakeGame.stop.call(oSnakeGame);
};
document.getElementById("help").onclick = function() {
alert("键盘方向键控制蛇的移动!/n" + "点击“Start”开始游戏、“Stop”暂停游戏/n"
+ "游戏共有十个等级,从零开始难度依次增大/n" + "共有四种不同颜色的食物。分别是黄、蓝、红、黑/n"
+ "食物随机产生并且每种食物的分值随等级变化/n" + "级别越高则相同颜色的食物分值越高");
};
};
最后我们看戏该如何使用。相应的html页面snake.html及CSS样式snake.css为:
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>snake</title>
<mce:script type="text/javascript" src="Snake.js" mce_src="Snake.js"></mce:script>
<mce:script type="text/javascript" src="Food.js" mce_src="Food.js"></mce:script>
<mce:script type="text/javascript" src="Canvas.js" mce_src="Canvas.js"></mce:script>
<mce:script type="text/javascript" src="Utils.js" mce_src="Utils.js"></mce:script>
<mce:script type="text/javascript" src="Scoreboard.js" mce_src="Scoreboard.js"></mce:script>
<mce:script type="text/javascript" src="SnakeGame.js" mce_src="SnakeGame.js"></mce:script>
<link type="text/css" rel="stylesheet" href="snake.css" mce_href="snake.css">
</head>
<body>
</body>
</html>
@CHARSET "UTF-8";
body {
margin-left: 10px;
margin-top: 50px;
}
#ctlbtn {
padding: 0px;
border: 1px solid red;
width: 662px;
}
#ctlbtn #start,#stop,#help {
padding: 1px 2px 1px 1px;
border: 1px solid #ECE9D8;
background-color: #ffffff;
font-style: italic;
font-family: sans-serif;
cursor: pointer;
width: 46px;
}
#help {
cursor: help;
}
#canvas {
float: left;
width: 460px;
height: 440px;
border: 1px solid green;
}
#canvas span {
float: left;
padding-left: 1px;
width: 20px;
height: 20px;
border: 1px solid #CDCBFE;
font-size: 12px;
}
#scoreboard {
float: left;
width: 200px;
height: 440px;
border: 1px solid red;
}
#scoreboard center {
margin: 4px 0px 5px 0px;
padding: 1px 1px 1px 1px;
font-style: inherit;
font-family: cursive;
}
#scoreboard label {
font-style: italic;
font-family: cursive;
}
#score,#level {
padding-left: 20px;
border: 0px solid #A2C11E;
font-family: cursive;
}
#top {
padding: 0px 0px 0px 0px;
border: 0px dotted #A2C11E;
}
#top span {
margin-left: 0px;
width: 20;
border: opx dotted #A2C11E;
font-size: 13px;
}
#player {
margin: 200px 50px 50px 150px;
padding: 1px 1px 1px 1px;
position: absolute;
z-index: 20px;
border: 0px solid orange;
background-color: #CDCBFE;
}
#player button {
padding: 0px 0px 0px 0px;
border: 1px solid #ECE9D8;
background-color: #ffffff;
}
现在可以体验下js版的snake了。最终效果如下: