게임 주소는 다음과 같습니다.
http://yuehaowang.github.io/games/puzzle/제 게임 기록입니다 , 도전에 오신 것을 환영합니다:다음으로 이 게임을 개발하고 완성하는 방법에 대해 이야기하겠습니다. ("연대기"를 누르세요)
준비 단계lufylegend 게임 엔진을 준비하세요. 공식 웹사이트에서 다운로드하실 수 있습니다: lufylegend.com/lufylegend엔진 문서 주소: lufylegend.com/lufylegend/api강력한 lufylegend 엔진이 없으면 이 HTML5 미니 게임을 만드는 데 적어도 하루는 걸릴 것이라고 말할 수 있습니다. 네이티브 캔버스. 0~30분재료 준비(10분) + 재료 수정(20분). 저는 장애인이고 P-사진을 잘 못해서 사진을 수정하는데 20분 정도 걸렸습니다. 囧...30~50분시작 인터페이스를 개발합니다. 게임은 시작 인터페이스 없이는 불가능하므로 먼저 코드의 이 부분을 구현합니다.<!DOCTYPE html> <html> <head> <title>Puzzle</title> <meta charset="utf-8"> <meta name="viewport" content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no"> <script type="text/javascript" src="./lib/lufylegend-1.10.1.simple.min.js"></script> <script type="text/javascript" src="./js/Main.js"></script> </head> <body style="margin: 0px; font-size: 0px; background: #F2F2F2;"> <p id="mygame"></p> </body> </html>
는 주로 일부 js 파일을 소개하는데 별로 할 말이 없습니다. 그런 다음 Main.js 파일을 준비하고 초기화 인터페이스와 리소스 로딩 코드를 이 파일에 추가합니다: index.html
/** 初始化游戏 */ LInit(60, "mygame", 390, 580, main); var imgBmpd; /** 游戏层 */ var stageLayer, gameLayer, overLayer; /** 拼图块列表 */ var blockList; /** 是否游戏结束 */ var isGameOver; /** 用时 */ var startTime, time, timeTxt; /** 步数 */ var steps, stepsTxt; function main () { /** 全屏设置 */ if (LGlobal.mobile) { LGlobal.stageScale = LStageScaleMode.SHOW_ALL; } LGlobal.screen(LGlobal.FULL_SCREEN); /** 添加加载提示 */ var loadingHint = new LTextField(); loadingHint.text = "资源加载中……"; loadingHint.size = 20; loadingHint.x = (LGlobal.width - loadingHint.getWidth()) / 2; loadingHint.y = (LGlobal.height - loadingHint.getHeight()) / 2; addChild(loadingHint); /** 加载图片 */ LLoadManage.load( [ {path : "./js/Block.js"}, {name : "img", path : "./images/img.jpg"} ], null, function (result) { /** 移除加载提示 */ loadingHint.remove(); /** 保存位图数据,方便后续使用 */ imgBmpd = new LBitmapData(result["img"]); gameInit(); } ); } function gameInit (e) { /** 初始化舞台层 */ stageLayer = new LSprite(); stageLayer.graphics.drawRect(0, "", [0, 0, LGlobal.width, LGlobal.height], true, "#EFEFEF"); addChild(stageLayer); /** 初始化游戏层 */ gameLayer = new LSprite(); stageLayer.addChild(gameLayer); /** 初始化最上层 */ overLayer = new LSprite(); stageLayer.addChild(overLayer); /** 添加2시간 안에 HTML5 퍼즐 게임 코드 그래픽 소개 완료 */ addBeginningUI(); }위 코드에는 자세한 설명이 있으며, 엔진 문서 및 설명에 따라 읽을 수 있습니다. 일부 전역 변수는 향후 코드에서 사용되므로 지금은 무시해도 됩니다. 다음은 시작 인터페이스를 구현하는 데 사용되는
함수의 코드입니다.
function addBeginningUI () { var beginningLayer = new LSprite(); beginningLayer.graphics.drawRect(0, "", [0, 0, LGlobal.width, LGlobal.height], true, "#EDEDED"); stageLayer.addChild(beginningLayer); /** 游戏标题 */ var title = new LTextField(); title.text = "拼图游戏"; title.size = 50; title.weight = "bold"; title.x = (LGlobal.width - title.getWidth()) / 2; title.y = 160; title.color = "#FFFFFF"; title.lineWidth = 5; title.lineColor = "#000000"; title.stroke = true; beginningLayer.addChild(title); /** 开始游戏提示 */ var hint = new LTextField(); hint.text = "- 点击屏幕开始游戏 -"; hint.size = 25; hint.x = (LGlobal.width - hint.getWidth()) / 2; hint.y = 370; beginningLayer.addChild(hint); /** 开始游戏 */ beginningLayer.addEventListener(LMouseEvent.MOUSE_UP, function () { beginningLayer.remove(); startGame(); }); }
이 시점에서 코드를 실행하고 시작 인터페이스를 가져옵니다. addBeginningUI
사실 이 사진을 보고 너무 '단순하다', 민망하다고 불평하고 싶었는데...
그런데 이번에는 제작 속도를 보여드리려고 하니 꼭 봐주시길 바랍니다. 읽어보세요. 50~90분이 40분은 전체 게임의 주요 부분을 완료해야 하는 가장 중요한 시간입니다. 먼저 코드를 사용하여 다음 프로세스를 구현해야 합니다.初始化游戏界面数据(如游戏时间、所用步数)和显示一些UI部件(如图样) | -> 获取随机的拼图块位置 | -> 显示打乱后的拼图块
function startGame () { isGameOver = false; /** 初始化时间和步数 */ startTime = (new Date()).getTime(); time = 0; steps = 0; /** 初始化拼图块列表 */ initBlockList(); /** 打乱拼图 */ getRandomBlockList(); /** 显示拼图 */ showBlock(); /** 显示缩略图 */ showThumbnail(); /** 显示时间 */ addTimeTxt(); /** 显示步数 */ addStepsTxt(); stageLayer.addEventListener(LEvent.ENTER_FRAME, onFrame); }함수 시작 부분에 다음을 넣습니다.
게임이 끝나지 않았다는 것을 나타내기 위해 변수를
로 설정했습니다. 이후 코드에서 이 변수의 역할을 살펴보겠습니다. 그런 다음 시간과 걸음 수를 나타내는 데 사용되는 두 개의 전역 변수와 isGameOver
를 초기화했습니다. 또한 초기화된 변수 false
의 값은 나중에 게임 시간을 계산하는 데 사용되었습니다. time
다음으로 퍼즐 조각 초기화를 시작하겠습니다. steps
의 코드를 보세요: startTime
function initBlockList () { blockList = new Array(); for (var i = 0; i < 9; i++) { /** 根据序号计算拼图块图片显示位置 */ var y = (i / 3) >>> 0, x = i % 3; blockList.push(new Block(i, x, y)); } }
initBlockList
클래스를 사용합니다. , 다음과 같습니다. 생성자의 코드입니다. function Block (index, x, y) { LExtends(this, LSprite, []); var bmpd = imgBmpd.clone(); bmpd.setProperties(x * 130, y * 130, 130, 130); this.bmp = new LBitmap(bmpd); this.addChild(this.bmp); var border = new LShape(); border.graphics.drawRect(3, "#CCCCCC", [0, 0, 130, 130]); this.addChild(border); this.index = index; this.addEventListener(LMouseEvent.MOUSE_UP, this.onClick); }
Block
클래스는
클래스에는 퍼즐 조각 목록 Block
에서 퍼즐 조각의 정확한 위치를 나타내는 LSprite
속성이 있습니다. 마지막으로, 마우스를 누른 후 타일 이동을 처리하기 위해 이 클래스에 마우스 다운 이벤트를 추가했습니다. Block
index
다음으로 이 클래스의 메서드를 소개합니다blockList
:
Block.prototype.setLocation = function (x, y) { this.locationX = x; this.locationY = y; this.x = x * 130; this.y = y * 130; };
이 메서드는 퍼즐 조각 개체의 표시 위치를 설정하고 퍼즐 조각 개체의 "배열 위치"를 저장하는 데 사용됩니다. 퍼즐 조각. "배열 위치"란 무엇입니까? 독자들은 다음 그림을 통해 이에 대해 배울 수 있습니다. setLocation
보시다시피 "배열 위치"는 2차원 배열의 요소 첨자와 유사합니다. 이 위치를 저장하는 목적은 에서 근처에 있는 다른 퍼즐 조각에 쉽게 접근하기 위한 것입니다. 퍼즐을 표시하기 전에 먼저 퍼즐을 뒤섞어야 합니다.
function getRandomBlockList () { /** 随机打乱拼图 */ blockList.sort(function () { return 0.5 - Math.random(); }); /** 计算逆序和 */ var reverseAmount = 0; for (var i = 0, l = blockList.length, preBlock = null; i < l; i++) { if (!preBlock) { preBlock = blockList[0]; continue; } var currentBlock = blockList[i]; if (currentBlock.index < preBlock.index) { reverseAmount++; } preBlock = currentBlock; } /** 检测打乱后是否可还原 */ if (reverseAmount % 2 != 0) { /** 不合格,重新打乱 */ getRandomBlockList(); } }
뒤섞인 퍼즐 부분은 blockList
메서드를 사용하여 직접 무작위로 섞습니다. :
blockList.sort(function () { return 0.5 - Math.random(); });
其实打乱算法有很多种,我这里采用最粗暴的方法,也就是随机打乱。这种算法简单是简单,坏在可能出现无法复原的现象。针对这个问题,就有配套的检测打乱后是否可还原的算法,具体的算法理论我摘用lufy大神的评论:
此类游戏能否还原关键是看它打乱后的逆序次数之和是否为偶数
假设你打乱后的数组中的每一个小图块为obj0
,obj1
,obj2
,…它们打乱之前的序号分别为obj0.num
,obj1.num
…
接下来循环数组,如果前者的序号比后者大,如obj0.num > obj1.num
,这表示一个逆序
当全部的逆序之和为奇数时表示不可还原,重新打乱即可,打乱后重新检测,直到逆序之和为偶数为止
上面我给出的getRandomBlockList
里的代码就是在实现打乱算法和检测是否可还原算法。
还有一种打乱方式,大家可以尝试尝试:和复原拼图一样,将空白块一步一步地与周围的拼图随机交换顺序。这个打乱算法较上一种而言,不会出现无法复原的现象,而且可以根据打乱的步数设定游戏难度。
在完成打乱拼图块后,如期而至的是显示拼图块:
function showBlock() { for (var i = 0, l = blockList.length; i < l; i++) { var b = blockList[i]; /** 根据序号计算拼图块位置 */ var y = (i / 3) >>> 0, x = i % 3; b.setLocation(x, y); gameLayer.addChild(b); } }
显示了拼图块后,我们要做的就是添加操作拼图块的功能。于是需要拓展Block
类,为其添加事件监听器onClick
方法:
Block.prototype.onClick = function (e) { var self = e.currentTarget; if (isGameOver) { return; } var checkList = new Array(); /** 判断右侧是否有方块 */ if (self.locationX > 0) { checkList.push(Block.getBlock(self.locationX - 1, self.locationY)); } /** 判断左侧是否有方块 */ if (self.locationX < 2) { checkList.push(Block.getBlock(self.locationX + 1, self.locationY)); } /** 判断上方是否有方块 */ if (self.locationY > 0) { checkList.push(Block.getBlock(self.locationX, self.locationY - 1)); } /** 判断下方是否有方块 */ if (self.locationY < 2) { checkList.push(Block.getBlock(self.locationX, self.locationY + 1)); } for (var i = 0, l = checkList.length; i < l; i++) { var checkO = checkList[i]; /** 判断是否是空白拼图块 */ if (checkO.index == 8) { steps++; updateStepsTxt(); Block.exchangePosition(self, checkO); break; } } };
首先,我们在这里看到了isGameOver
全局变量的作用,即在游戏结束后,阻断点击拼图块后的操作。
在点击了拼图块后,我们先获取该拼图块周围的拼图块,并将它们装入checkList
,再遍历checkList
,当判断到周围有空白拼图块后,即周围有index
属性等于8的拼图块后,先更新操作步数,然后将这两个拼图块交换位置。具体交换拼图块位置的方法详见如下代码:
Block.exchangePosition = function (b1, b2) { var b1x = b1.locationX, b1y = b1.locationY, b2x = b2.locationX, b2y = b2.locationY, b1Index = b1y * 3 + b1x, b2Index = b2y * 3 + b2x; /** 在地图块数组中交换两者位置 */ blockList.splice(b1Index, 1, b2); blockList.splice(b2Index, 1, b1); /** 交换两者显示位置 */ b1.setLocation(b2x, b2y); b2.setLocation(b1x, b1y); /** 判断游戏是否结束 */ Block.isGameOver(); };
还有就是Block.getBlock
静态方法,用于获取给定的“数组位置”下的拼图块:
Block.getBlock = function (x, y) { return blockList[y * 3 + x]; };
在Block.exchangePosition
中,我们通过Block.isGameOver
判断玩家是否已将拼图复原:
Block.isGameOver = function () { var reductionAmount = 0, l = blockList.length; /** 计算还原度 */ for (var i = 0; i < l; i++) { var b = blockList[i]; if (b.index == i) { reductionAmount++; } } /** 计算是否完全还原 */ if (reductionAmount == l) { /** 游戏结束 */ gameOver(); } };
到这里,我们就实现了打乱和操作拼图块部分。
最后30min用于细枝末节上的处理,如显示拼图缩略图、显示&更新时间和步数,以及添加游戏结束画面,这些就交给如下冗长而简单的代码来完成吧:
function showThumbnail() { var thumbnail = new LBitmap(imgBmpd); thumbnail.scaleX = 130 / imgBmpd.width; thumbnail.scaleY = 130 / imgBmpd.height; thumbnail.x = (LGlobal.width - 100) /2; thumbnail.y = 410; overLayer.addChild(thumbnail); } function addTimeTxt () { timeTxt = new LTextField(); timeTxt.stroke = true; timeTxt.lineWidth = 3; timeTxt.lineColor = "#54D9EF"; timeTxt.color = "#FFFFFF"; timeTxt.size = 18; timeTxt.x = 20; timeTxt.y = 450; overLayer.addChild(timeTxt); updateTimeTxt(); } function updateTimeTxt () { timeTxt.text = "时间:" + getTimeTxt(time); } function getTimeTxt () { var d = new Date(time); return d.getMinutes() + " : " + d.getSeconds(); }; function addStepsTxt () { stepsTxt = new LTextField(); stepsTxt.stroke = true; stepsTxt.lineWidth = 3; stepsTxt.lineColor = "#54D9EF"; stepsTxt.color = "#FFFFFF"; stepsTxt.size = 18; stepsTxt.y = 450; overLayer.addChild(stepsTxt); updateStepsTxt(); } function updateStepsTxt () { stepsTxt.text = "步数:" + steps; stepsTxt.x = LGlobal.width - stepsTxt.getWidth() - 20; } function onFrame () { if (isGameOver) { return; } /** 获取当前时间 */ var currentTime = (new Date()).getTime(); /** 计算使用的时间并更新时间显示 */ time = currentTime - startTime; updateTimeTxt(); } function gameOver () { isGameOver = true; var resultLayer = new LSprite(); resultLayer.filters = [new LDropShadowFilter()]; resultLayer.graphics.drawRoundRect(3, "#BBBBBB", [0, 0, 350, 350, 5], true,"#DDDDDD"); resultLayer.x = (LGlobal.width - resultLayer.getWidth()) / 2; resultLayer.y = LGlobal.height / 2; resultLayer.alpha = 0; overLayer.addChild(resultLayer); var title = new LTextField(); title.text = "游戏通关" title.weight = "bold"; title.stroke = true; title.lineWidth = 3; title.lineColor = "#555555"; title.size = 30; title.color = "#FFFFFF"; title.x = (resultLayer.getWidth() - title.getWidth()) / 2; title.y = 30; resultLayer.addChild(title); var usedTimeTxt = new LTextField(); usedTimeTxt.text = "游戏用时:" + getTimeTxt(time); usedTimeTxt.size = 20; usedTimeTxt.stroke = true; usedTimeTxt.lineWidth = 2; usedTimeTxt.lineColor = "#555555"; usedTimeTxt.color = "#FFFFFF"; usedTimeTxt.x = (resultLayer.getWidth() - usedTimeTxt.getWidth()) / 2; usedTimeTxt.y = 130; resultLayer.addChild(usedTimeTxt); var usedStepsTxt = new LTextField(); usedStepsTxt.text = "所用步数:" + steps; usedStepsTxt.size = 20; usedStepsTxt.stroke = true; usedStepsTxt.lineWidth = 2; usedStepsTxt.lineColor = "#555555"; usedStepsTxt.color = "#FFFFFF"; usedStepsTxt.x = usedTimeTxt.x; usedStepsTxt.y = 180; resultLayer.addChild(usedStepsTxt); var hintTxt = new LTextField(); hintTxt.text = "- 点击屏幕重新开始 -"; hintTxt.size = 23; hintTxt.stroke = true; hintTxt.lineWidth = 2; hintTxt.lineColor = "#888888"; hintTxt.color = "#FFFFFF"; hintTxt.x = (resultLayer.getWidth() - hintTxt.getWidth()) / 2; hintTxt.y = 260; resultLayer.addChild(hintTxt); LTweenLite.to(resultLayer, 0.5, { alpha : 0.7, y : (LGlobal.height - resultLayer.getHeight()) / 2, onComplete : function () { /** 点击界面重新开始游戏 */ stageLayer.addEventListener(LMouseEvent.MOUSE_UP, function () { gameLayer.removeAllChild(); overLayer.removeAllChild(); stageLayer.removeAllEventListener(); startGame(); }); } }); }
Ok,2h下来,整个游戏就搞定咯~不得不表扬一下lufylegend这个游戏引擎,实在是可以大幅提升开发效率。
以上就是2小时完成HTML5拼图小游戏代码图文介绍的内容,更多相关内容请关注PHP中文网(www.php.cn)!