1 이전에 작성됨
"JavsScript Advanced 프로그래밍"이 내 기술을 연습하기 위해 작은 데모를 만들고 싶어했던 것 같아서 Snake 게임을 선택했습니다. 예전에는 모두 C#으로 작성했기 때문에 Snake를 클래스로 작성한 뒤 작은 메소드들로 하나씩 나누어서 제공해야 하는 메소드들만 제공했습니다. 이런 식으로 Snake는 모듈로 사용될 수 있으며 어디에서나 재사용이 가능합니다. 그러나 js로 작성하는 경우에는 js 언어의 특성을 모듈식 프로그래밍에 잘 활용하지 못하기 때문에 첫 번째 구현 버전에서는 완전히 프로세스 지향적 접근 방식을 채택하고 함수에 필요한 모든 변수를 전역 변수로 선언합니다. 이는 기능을 수행할 수도 있지만 재사용할 수 없으며 많은 최상위 변수가 정의되어 전역 변수를 오염시킵니다. 글을 쓴 후에는 꼭 제공해야 하는 변수나 함수형 인터페이스만 외부에 제공되도록 항상 제가 작성한 내용을 다시 캡슐화하고 싶습니다. 많은 정보를 확인한 결과 클로저를 사용하여 js 캡슐화를 구현할 수 있음이 밝혀졌습니다. 함수 내부의 지역 변수와 클로저 함수를 해당 타입의 프라이빗 변수와 함수로 선언함으로써 이를 통해 개발해야 할 인터페이스를 객체에 제공합니다.
2 Snake 컴포넌트 사용법
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에 생성자는 두 개의 매개변수를 전달합니다. 첫 번째 매개변수는 캔버스의 ID이고 두 번째 매개변수는 게임 구성 개체입니다. 비어 있으면 기본 구성이 사용됩니다. 마지막으로 개체의 startGame() 메서드를 호출하여 뱀의 논리를 구현합니다. 기본 방향 제어 키는 위, 아래, 왼쪽 및 오른쪽 키이며 일시 중지는 스페이스입니다. 효과는 다음과 같습니다.
<!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>
매개변수의 이름으로 알 수 있으며, 뱀의 색상, 음식, 게임 배경, 게임을 제어하는 방향 키를 구성할 수 있습니다. 구성 방향 순서는 [왼쪽, 아래, 오른쪽, 위]입니다. 효과는 다음과 같습니다.
<!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> <p id="gamebd"> <canvas id="gameScense" width="600" height="600"> </canvas> <p id="gameSet"> <p id="gameControl" class="gameBoxStyle"> <h3>游戏控制</h3> <p>方向键:上,下,左,右</p> <p>开始/暂停:空格</p> </p> <p 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"/> </p> <p id="game" class="gameBoxStyle"> <h3>游戏记录</h3> <a href="#" rel="external nofollow" rel="external nofollow" >查看历史记录</a> </p> </p> </p> <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의 세 가지 콜백 함수를 설정하여 인터페이스와 구성 요소 간의 상호 작용을 구현합니다. 효과는 다음과 같습니다.
예제 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> <p id="gamebd"> <canvas id="gameScense" width="600" height="600"> </canvas> <canvas id="gameScense1" width="600" height="600" style="background-color: black"> </canvas> <p id="gameSet"> <p id="gameControl" class="gameBoxStyle"> <h3>游戏控制</h3> <p>方向键:上,下,左,右</p> <p>开始/暂停:空格</p> </p> <p 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"/> </p> <p id="game" class="gameBoxStyle"> <h3>游戏记录</h3> <a href="#" rel="external nofollow" rel="external nofollow" >查看历史记录</a> </p> </p> </p> <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 作为方向键和暂停键。效果如下:
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 的模块化设计稍微了解了一下,但是,我也不知道上文所实现的贪吃蛇模块有哪些缺陷,希望有大神看到这篇文章,能给一些指导。当然了,该组件还可以进行进一步的扩展,比如将游戏的方块,替换成图片。
위 내용은 JavaScript로 Snake 위젯을 구현하는 예제 코드의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!