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 相关文章推荐
070823更新的一个[消息提示框]组件 兼容ie7
Aug 29 Javascript
document.designMode的功能与使用方法介绍
Nov 22 Javascript
zShowBox 图片放大展示jquery版 兼容性
Sep 24 Javascript
js实现在文本框光标处添加字符的方法介绍
Nov 24 Javascript
js实现iframe跨页面调用函数的方法
Dec 13 Javascript
jQuery使用之标记元素属性用法实例
Jan 19 Javascript
利用vue.js插入dom节点的方法
Mar 15 Javascript
Redux 和 Mobx的选择问题:让你不再困惑!
Sep 18 Javascript
微信小程序实现的贪吃蛇游戏【附源码下载】
Jan 03 Javascript
React如何避免重渲染
Apr 10 Javascript
你可能不知道的CORS跨域资源共享
Mar 13 Javascript
antd form表单数据回显操作
Nov 02 Javascript
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获取汉字的拼音(全部与首字母)
2013/06/27 PHP
非常实用的PHP常用函数汇总
2014/12/17 PHP
PHP中的命名空间详细介绍
2015/07/02 PHP
基于PHP实现通过照片获取ip地址
2016/04/26 PHP
php 静态属性和静态方法区别详解
2017/04/09 PHP
PHP实现类似题库抽题效果
2018/08/16 PHP
jQuery的强大选择器小结
2009/12/27 Javascript
基于jquery的jqDnR拖拽溢出的修改
2011/02/12 Javascript
EXTJS FORM HIDDEN TEXTFIELD 赋值 使用value不好用的问题
2011/04/16 Javascript
JavaScript Array Flatten 与递归使用介绍
2011/10/30 Javascript
ie中js创建checkbox默认选中问题探讨
2013/10/21 Javascript
jquery实现弹出窗口效果的实例代码
2013/11/28 Javascript
Javascript实现div的toggle效果实例分析
2015/06/09 Javascript
javascript中递归函数用法注意点
2015/07/30 Javascript
jquery实现仿Flash的横向滑动菜单效果代码
2015/09/17 Javascript
jQuery实现的指纹扫描效果实例(附演示与demo源码下载)
2016/01/26 Javascript
JavaScript贪吃蛇小组件实例代码
2017/08/20 Javascript
温故知新——JavaScript中的字符串连接问题最全总结(推荐)
2017/08/21 Javascript
seajs中最常用的7个功能、配置示例
2017/10/10 Javascript
解决Vue使用mint-ui loadmore实现上拉加载与下拉刷新出现一个页面使用多个上拉加载后冲突问题
2017/11/07 Javascript
Node.js 中使用 async 函数的方法
2017/11/20 Javascript
基于layui的下拉列表的数据回显方法
2019/09/24 Javascript
Python 中的lambda函数介绍
2018/10/10 Python
使用Python opencv实现视频与图片的相互转换
2019/07/08 Python
python爬虫刷访问量 2019 7月
2019/08/01 Python
python网络编程 使用UDP、TCP协议收发信息详解
2019/08/29 Python
PyCharm 2019.3发布增加了新功能一览
2019/12/08 Python
python怎么自定义捕获错误
2020/06/29 Python
Python 使用 PyQt5 开发的关机小工具分享
2020/07/16 Python
Django web自定义通用权限控制实现方法
2020/11/24 Python
HTML5是否真的可以取代Flash
2010/02/10 HTML / CSS
英文自荐信
2013/12/15 职场文书
安全生产一岗双责责任书
2014/07/28 职场文书
村安全生产责任书
2014/08/25 职场文书
职场中的你,辞职信写对了吗?
2019/06/26 职场文书
nginx 配置指令之location使用详解
2022/05/25 Servers