JavaScript贪吃蛇小组件实例代码


Posted in Javascript onAugust 20, 2017

1 写在前面

看来《JavsScript高级编程》,想做一个小demo练练自己的手,选择了贪吃蛇游戏。由于以前都是用c#写的,将贪吃蛇写到一个类里面,然后一个一个小方法的拆分,只向外提供需要提供的方法。这样就可以将贪吃蛇作为一个模块,任何地方都可以复用的。然而,用js进行编写的时候,由于不能很好的利用js语言的特性进行模块化编程,所以第一版的实现完全采用面向过程的方式,将函数中所需要的变量全部声明为全局变量。虽然这样也能够实现功能,但是做不到复用,而且定义非常多的最顶层变量,污染了全局变量。写完之后,总想将自己写的重新封装一次,达到只向外提供必须要提供的变量、或者功能函数接口。查了一些许多资料,对于js的封装可以采用闭包的方式来进行实现。通过在函数内部声明局部变量和闭包函数来当做类型的私有变量和函数,然后通过this给对象向外提供需要开发的接口。

2 贪吃蛇组件的使用

2.1 初级示例

示例代码1如下:

<!DOCTYPE html>
<html lang="en">
<head>
 <meta charset="UTF-8">
 <title>贪吃蛇组件</title>
</head>
<body>
 <canvas width="600" height="600" id="gameScense"></canvas>
</body>
<script src="SnakeGame.js"></script>
<script>
 var snakeGame = new SnakeGame("gameScense",{
 });
 snakeGame.startGame();
</script>
</html>

首先引入SnakeGame.js组件,然后通过实例化 SnakeGame对象,并向SnakeGame构造函数传入两个参数。第一参数是canvas的id,第二个参数游戏配置的对象,如果为空的话,那么采用默认的配置。最后,调用对象的startGame()方法,即可实现贪吃蛇的逻辑。默认的方向控制键为上下左右按键、暂停为空格,效果如下:

JavaScript贪吃蛇小组件实例代码

我们可以通过更改实例化时传入的配置对象来实现对游戏的更多控制。

 示例代码2:

<!DOCTYPE html>
<html lang="en">
<head>
 <meta charset="UTF-8">
 <title>贪吃蛇组件</title>
</head>
<body>
 <canvas width="600" height="600" id="gameScense"></canvas>
</body>
<script src="SnakeGame.js"></script>
<script>
 var snakeGame = new SnakeGame("gameScense",{
  snakeColor:"red",
  foodColor:"green",
  scenseColor:"blue",
  directionKey:[68,83,65,87],
 });
 snakeGame.startGame();
</script>
</html>

通过参数的名字可以知道,配置蛇、食物、游戏背景的颜色,以及控制游戏的方向键。配置方向顺序为【左,下,右,上】。效果如下:

JavaScript贪吃蛇小组件实例代码

当然还有更加多的配置。还能够定义分数改变的回调的函数,以及游戏结束时的回调函数等。下面介绍一下配置参数,以及SnakeGame对象共有的方法。

2.2 公有方法

•startGame() : 开始游戏。在该方法内,会初始化各种设置。如,重置分数,蛇身,速度等。

•changeGameStatus():改变游戏状态,即暂停和开始,SnakeGame对象里面有一个私有变量,作为游戏的状态变量。

2.3 配置游戏参数的对象gameConfigObj属性、

gameConfigObj 对象一共该有10个属性,3个回调函数

属性

•size : 蛇块和食物的大小,默认20
•rowCount : 行,默认30行
•colCount : 列,默认30列
•snakeColor : 蛇身颜色,默认green
•foodColor : 食物颜色,默认yellow
•scenseColor : 游戏场景背景色, 默black
•directionKey : 方向键, 默认[39, 40, 37, 38] 上下左右
•pasueKey : 暂停键, 默认32,空格键
•levelCount : 速度等级控制,默认10.
•curSpeed : 初始速度,默认200毫秒

回调函数

•onCountChange : 事件,每一个食物,分数改变,并调用该方法,带有一个参数(count)

•onGamePause : 事件,游戏状态改变时,调用该方法,带有一个参数 1,代表暂停,0 ,代表游戏在进行。

•onGameOver : 事件,游戏结束时,调用该方法。

2.4使用进阶

通过上面的属性我们可以设计一个交互性更加强的程序。代码如下。

 示例3

<!DOCTYPE html>
<html lang="en">
<head>
 <meta charset="UTF-8">
 <title>贪吃蛇组件</title>
 <style type="text/css">
  *{
   margin:0px;
   padding:0px;
  }
  #gamebd{
   width:850px;
   margin:50px auto;
  }
  #gameScense{
   background-color:green;
   float:left;
  }
  #gameSet{
   margin-left:10px;
   float:left;
  }
  .gameBoxStyle{
   margin-bottom:7px;
   padding:5px 10px;
  }
  .gameBoxStyle h3{
   margin-bottom:7px;
  }
  .gameBoxStyle p{
   line-height: 1.7em;
  }
  .gameBoxStyle input{
   margin-top:7px;
   background-color: white;
   border:1px gray solid;
   padding:3px 9px;
   margin-right:9px;
  }
  .gameBoxStyle input[type=text]{
   width:90px;
  }
  .gameBoxStyle input:hover{
   background-color: #e2fff2;
  }
  .gameBoxStyle #txtValue{
   color:red;
  }
 </style>
</head>
<body>
<div id="gamebd">
 <canvas id="gameScense" width="600" height="600">
 </canvas>
 <div id="gameSet">
  <div id="gameControl" class="gameBoxStyle">
   <h3>游戏控制</h3>
   <p>方向键:上,下,左,右</p>
   <p>开始/暂停:空格</p>
  </div>
  <div id="gameStatus" class="gameBoxStyle">
   <h3>游戏状态</h3>
   <p>用户名:<input type="text" placeholder="输入用户名:" id="txtUserName" value="游客123"/> </p>
   <p>当前用户1得分:<span id="txtValue">0</span></p>
   <input type="button" value="开始游戏" id="btnStart"/>
   <input type="button" value="暂停" id="btnPause"/>
  </div>
  <div id="game" class="gameBoxStyle">
   <h3>游戏记录</h3>
   <a href="#" rel="external nofollow" rel="external nofollow" >查看历史记录</a>
  </div>
 </div>
</div>
<script src="js/SnakeGame.js"></script>
</body>
<script src="SnakeGame.js"></script>
<script>
 var btnStart=document.getElementById("btnStart");
 var btnPasue=document.getElementById("btnPause");
 var gameSnake = new SnakeGame("gameScense",{
  snakeColor:"red",
  onCountChange:function(count){
   var txtScore=document.getElementById("txtValue");
   txtScore.innerText=count.toString( );
   txtScore=null;
  },
  onGamePause:function(status){
   if(status){
    btnPasue.value = "开始";
   }else {
    btnPasue.value = "暂停"
   }
  },
  onGameOver:function (status) {
   alert("游戏结束");
  }
 });
 btnStart.onclick=function(event){
  if(checkUserName()){
   gameSnake.startGame();
   btnStart.blur();
  }
 }
 btnPasue.onclick=function(event) {
  gameSnake.changeGameStatus();
  btnStart.blur();
 }
 function checkUserName(){
  var txtUserName = document.getElementById("txtUserName");
  if(txtUserName.value.length==0){
   alert("用户名不能为空");
   return false;
  }else {
   return true;
  }
 }
</script>
</html>

上面的代码通过设置OnChangeCount、onGamePause、onGameOver,三个回调函数,实现界面与组件的交互。效果如下:

JavaScript贪吃蛇小组件实例代码

在《JavaScript高级编程》这本书中说道一个模块模式,但是这种模式是单例模式,也就是闭包最后返回一个字面量的对象。但是我需要在一个页面中能够同时开启两个贪吃蛇的窗口,两个游戏通过设置配置不同的方向键和按钮操作,实现两个人同时一起玩。所以,在实现SnakeGame组件时,没有采用道格拉斯所说的模块模式。下面演示一下,如何在一个页面中,让两个人同时一起玩游戏。代码如下: 

示例4

首先建立一个html文件

<!DOCTYPE html>
<html lang="en">
<head>
 <meta charset="UTF-8">
 <title>Jaume's贪吃蛇</title>
 <link rel="stylesheet" href="css/gameStyle.css" rel="external nofollow" >
</head>
<body>
<div id="gamebd">
 <canvas id="gameScense" width="600" height="600">
 </canvas>
 <canvas id="gameScense1" width="600" height="600" style="background-color: black">
 </canvas>
 <div id="gameSet">
  <div id="gameControl" class="gameBoxStyle">
   <h3>游戏控制</h3>
   <p>方向键:上,下,左,右</p>
   <p>开始/暂停:空格</p>
  </div>
  <div id="gameStatus" class="gameBoxStyle">
   <h3>游戏状态</h3>
   <p>当前用户1得分:<span id="txtValue">0</span></p>
   <p>当前用户2得分:<span id="txtValue1">0</span></p>
   <input type="button" value="开始游戏" id="btnStart"/>
  </div>
  <div id="game" class="gameBoxStyle">
   <h3>游戏记录</h3>
   <a href="#" rel="external nofollow" rel="external nofollow" >查看历史记录</a>
  </div>
 </div>
</div>
<script src="js/SnakeGame.js"></script>
<script src="js/UIScript.js"></script>
</body>
</html>

样式文件如下:

*{
 margin:0px;
 padding:0px;
}
#gamebd{
 /*width:850px;*/
 /*margin:50px auto;*/
 width:100%;
}
#gameScense{
 background-color:green;
 float:left;
}
#gameSet{
 margin-left:10px;
 float:left;
}
.gameBoxStyle{
 margin-bottom:7px;
 padding:5px 10px;
}
.gameBoxStyle h3{
 margin-bottom:7px;
}
.gameBoxStyle p{
 line-height: 1.7em;
}
.gameBoxStyle input{
 margin-top:7px;
 background-color: white;
 border:1px gray solid;
 padding:3px 9px;
 margin-right:9px;
}
.gameBoxStyle input[type=text]{
 width:90px;
}
.gameBoxStyle input:hover{
 background-color: #e2fff2;
}
.gameBoxStyle #txtValue{
 color:red;
}

在html中拖入了两个文件,一个是贪吃蛇组件,另一个是UIScript.js,其中的代码如下:

/**
 * Created by tjm on 8/16/2017.
 */
var btnStart=document.getElementById("btnStart");
var gameSnake = new SnakeGame("gameScense",{
 snakeColor:"red",
 directionKey:[68,83,65,87],
 pauseKey:81,
 onCountChange:function(count){
  var txtScore=document.getElementById("txtValue");
  txtScore.innerText=count.toString( );
  txtScore=null;
 },
 onGameOver:function (status) {
  alert("游戏结束");
 }
});
var gameSnake1 = new SnakeGame("gameScense1",{
 snakeColor:"green",
 size:20,
 onCountChange:function(count){
  var txtScore=document.getElementById("txtValue1");
  txtScore.innerText=count.toString();
  txtScore=null;
 },
 onGameOver:function (status) {
  alert("游戏结束");
 }
});
btnStart.onclick=function(event){
  gameSnake.startGame();
  gameSnake1.startGame();
  btnStart.blur();
}

实例化两个SnakeGame对象,一个对象使用默认的上下左右键和空格键作为方向键和暂停键,而另一个使用了,W、A、S、D 以及 Q 作为方向键和暂停键。效果如下:

JavaScript贪吃蛇小组件实例代码

嗯哼,没错,完美实现了。使用SnakeGame这个组件,创建贪吃蛇游戏就是如此的简单。下面简单介绍一下,组件的实现方式。

3贪吃蛇组件实现方式

在上一节中就提到过,没有采用过道哥拉斯的设计模式,下面给出贪吃蛇设计结构。具体的源代码,可以在后面的链接中进行下载。代码如下:

/**
 * Created by tjm on 8/18/2017.
 */
var SnakeGame = function () {
 /*蛇块和食物组件类*/
 function SnakeBlock(row,col){
  this.row=row;
  this.col=col;
 }
 SnakeBlock.prototype.draw = function(graphic,color,size){
  graphic.fillStyle=color;
  graphic.fillRect(size*this.col,size*this.row,size-2,size-2);
 }
 SnakeBlock.prototype.clearDraw = function(graphic,color,size){
  graphic.fillStyle=color;
  graphic.fillRect(size*this.col,size*this.row,size,size);
 }
 SnakeBlock.prototype.equal = function(snakeBlock){
  if(snakeBlock.row==this.row && snakeBlock.col==this.col){
   return true;
  }else{
   return false;
  }
 }
 /*贪吃蛇组件类*/
 function SnakeGame(gameScenseId, gameConfigObj) {
  // 私有属性
  var gameScense = document.getElementById(gameScenseId);
  var graphic = gameScense.getContext("2d");
  var count = 0;
  var snake;
  var curFood;
  var runId;
  var isMoved = false;//方向改变后,如果没有移动则方向键暂时失效。
  var gameStatus = false;
  var curDirection = 1;
  var size = gameConfigObj.size || 20;
  var rowCount = gameConfigObj.rowCount || 30;
  var colCount = gameConfigObj.colCount || 30;
  var snakeColor = gameConfigObj.snakeColor || "green";
  var foodColor = gameConfigObj.foodColor || "yellow";
  var scenseColor = gameConfigObj.scenseColor || "black";
  var directionKey = gameConfigObj.directionKey || [39, 40, 37, 38];
  var pauseKey = gameConfigObj.pauseKey || 32;
  var levelCount = gameConfigObj.levelCount || 10;
  var curSpeed = gameConfigObj.curSpeed || 200;
  //公开事件
  var onCountChange = gameConfigObj.onCountChange || null; //带有一个参数
  var onGamePause = gameConfigObj.onGamePause || null; //带有一个参数
  var onGameOver = gameConfigObj.onGameOver || null;
  //判断
  if(gameScense.width != size*rowCount || gameScense.height != size*colCount){
   throw "场景大小不等于行列大小*蛇块大小";
  }
  //特权方法
  this.startGame = startGame;
  this.changeGameStatus = changeGameStatus;
  //注册 dom 键盘事件
  var preFunc = document.onkeydown;
  document.onkeydown = function (e) {
   var key = (e || event).keyCode;
   handleKeyInput(key);
   if (typeof preFunc == "function") {
    preFunc(e);
   }
  }
  //私有方法
  /*初始化蛇身*/
  function initSnake(){
   ···
  }
  /*绘制场景背景色*/
  function initScense(){
   ···
  }
  /*产生食物*/
  function genFood(){
   ···
  }
  /*吃食物*/
  function eatFood(snakeHead){
   ···
  }
  /*判断游戏是否结束*/
  function gameOver(){
   ···
  }
  /*蛇移动*/
  function snakeMove(){
   ···
  }
  function changeSpeed(){
   ···
  }
  function handleKeyInput(key){
   ···
  }
  function initGame(){
   ···
  }
  function triggerEvent(callback,argument){
   ···
  }
  function runGame(){
   ···
  }
  function pauseGame() {
   ···
  }
  function changeGameStatus(){
   ···
  }
  function startGame(){
   ···
  }
 }
 return SnakeGame; //最后返回一个组件构造函数
}();

上面有一个很重要的地方,就是键盘注册的代码,单独列出来分析一下。

var preFunc = document.onkeydown;
  document.onkeydown = function (e) {
   var key = (e || event).keyCode;
   handleKeyInput(key);
   if (typeof preFunc == "function") {
    preFunc(e);
   }
  }

该段代码的逻辑是,首先判断在 document 上是否注册了onkeydown 事件,如果注册了该事件,则保存所引用的事件处理程序,然后重置onkeydown事件程序,然后在新的事件处理程序中,调用先前的事件处理程序,这样就实现了事件触发后,调用所有监听该事件处理程序,而不是直接覆盖。

另外关于贪吃蛇的设计逻辑,可以参看我另外一篇文章,个人觉得讲的非常详细了,文章:基于控制台实现贪吃蛇游戏

3 小结

通过这次贪吃蛇组件的设计,对 js 的模块化设计稍微了解了一下,但是,我也不知道上文所实现的贪吃蛇模块有哪些缺陷,希望有大神看到这篇文章,能给一些指导。当然了,该组件还可以进行进一步的扩展,比如将游戏的方块,替换成图片。有兴趣的可以从下面的链接进行下载,更改后别忘了分享哦。

源码下载链接:https://github.com/StartAction/SnakeGame

以上所述是小编给大家介绍的JavaScript贪吃蛇小组件实例代码,希望对大家有所帮助,如果大家有任何疑问欢迎给我留言,小编会及时回复大家的!

Javascript 相关文章推荐
javascript中获取选中对象的类型
Apr 02 Javascript
关于jquery input textare 事件绑定及用法学习
Apr 03 Javascript
jQuery网页右侧广告跟随滚动代码分享
Apr 20 Javascript
jQuery ajax的功能实现方法详解
Jan 06 Javascript
js调用刷新界面的几种方式
May 03 Javascript
微信小程序实现tab页面切换功能
Jul 13 Javascript
Vue中遍历数组的新方法实例详解
Jul 21 Javascript
解决layUI的页面显示不全的问题
Sep 20 Javascript
微信小程序实现限制用户转发功能的实例代码
Feb 22 Javascript
JavaScript实现矩形块大小任意缩放
Aug 25 Javascript
解决Antd 里面的select 选择框联动触发的问题
Oct 24 Javascript
vue watch监控对象的简单方法示例
Jan 07 Vue.js
React Native 环境搭建的教程
Aug 19 #Javascript
ionic App问题总结系列之ionic点击系统返回键退出App
Aug 19 #Javascript
浅谈关于.vue文件中style的scoped属性
Aug 19 #Javascript
如何理解Vue的作用域插槽的实现原理
Aug 19 #Javascript
Vue Transition实现类原生组件跳转过渡动画的示例
Aug 19 #Javascript
JavaScript文件的同步和异步加载的实现代码
Aug 19 #Javascript
深入理解ES6学习笔记之块级作用域绑定
Aug 19 #Javascript
You might like
PHP中数组的三种排序方法分享
2012/05/07 PHP
PHP 使用header函数设置HTTP头的示例解析 表头
2013/06/17 PHP
简单解析PHP程序的运行流程
2016/06/23 PHP
PHP内置函数生成随机数实例
2019/01/18 PHP
JavaScript 实现??打印?理
2007/04/28 Javascript
js中将字符串转换成json的三种方式
2011/01/12 Javascript
extjs实现选择多表自定义查询功能 前台部分(ext源码)
2011/12/20 Javascript
javascript 事件处理程序介绍
2012/06/27 Javascript
解决javascript:window.close()在chrome,Firefox下失效的问题
2013/05/07 Javascript
jquery特效 幻灯片效果示例代码
2013/07/16 Javascript
jQuery+ajax实现鼠标单击修改内容的思路
2014/06/29 Javascript
jQuery打印图片pdf、txt示例代码
2014/07/22 Javascript
angularjs学习笔记之完整的项目结构
2015/09/26 Javascript
AngularJS通过$location获取及改变当前页面的URL
2016/09/23 Javascript
Bootstrap CSS组件之按钮组(btn-group)
2016/12/17 Javascript
jQuery实现可移动选项的左右下拉列表示例
2016/12/26 Javascript
原生和jQuery的ajax用法详解
2017/01/23 Javascript
Javascript实现的StopWatch功能示例
2017/06/13 Javascript
vue router自动判断左右翻页转场动画效果
2017/10/10 Javascript
微信小程序收藏功能的实现代码
2018/06/12 Javascript
通过js实现压缩图片上传功能
2020/02/25 Javascript
JQuery通过键盘控制键盘按下与松开触发事件
2020/08/07 jQuery
pycharm 使用心得(六)进行简单的数据库管理
2014/06/06 Python
在Python程序员面试中被问的最多的10道题
2017/12/05 Python
Python爬取qq空间说说的实例代码
2018/08/17 Python
解决python中os.listdir()函数读取文件夹下文件的乱序和排序问题
2018/10/17 Python
Django自定义用户登录认证示例代码
2019/06/30 Python
python+openCV调用摄像头拍摄和处理图片的实现
2019/08/06 Python
使用Python将图片转正方形的两种方法实例代码详解
2020/04/29 Python
Keras框架中的epoch、bacth、batch size、iteration使用介绍
2020/06/10 Python
python实现PDF中表格转化为Excel的方法
2020/06/16 Python
使用Python画了一棵圣诞树的实例代码
2020/11/27 Python
JBL澳大利亚官方商店:扬声器、耳机和音响系统
2018/05/24 全球购物
六道php面试题附答案
2014/06/05 面试题
简单而又朴实的个人求职信分享
2013/12/12 职场文书
会计学自我鉴定
2014/02/06 职场文书