Maison > Article > interface Web > Introduction graphique complète du code du jeu de puzzle HTML5 en 2 heures
Lorsque j'ai appris lufylegend.js pour la première fois, j'ai utilisé lufylegend.js pour développer le premier mini-jeu HTML5 - un puzzle. J'ai également écrit un article de blog pour le montrer : mini-jeu HTML5. Intelligence Puzzle" "Publié pour défier votre tempête de réflexion. Cependant, j'étais nouveau dans le développement de jeux à cette époque et j'avais peu d'expérience, donc je n'ai pas étudié les défauts d'algorithme et de code du jeu, ce qui a entraîné de nombreux bugs dans le jeu, et il était même possible que les énigmes ne puissent pas être résolues. restauré après avoir été perturbé. Récemment, des amis me posent souvent des questions sur ce jeu. Ils espèrent que je pourrai corriger les bugs du code pour faciliter l'apprentissage des débutants. D'ailleurs, j'ai aussi l'intention de tester ma vitesse en écrivant ce genre de petit jeu, donc j'ai pris du temps. pour écrire ce jeu du début à la fin. Je l'ai réécrit et calculé le temps. Il a fallu environ 2 heures entre la préparation et la modification du matériel jusqu'à la fin du jeu.
Voici l'adresse du jeu :
http://yuehaowang.github.io/games/puzzle/
Ceci est mon record de jeu , Bienvenue au défi :
Ensuite, parlons de la façon de développer et de terminer ce jeu. (Appuyez sur "Chronologie")
Préparez le moteur de jeu lufylegend Vous pouvez le télécharger sur le site officiel :
lufylegend.com/lufylegend
Adresse du document moteur :
lufylegend.com/lufylegend/api
On peut dire que sans le puissant moteur lufylegend, il faudrait au moins une journée pour créer ce genre de mini HTML5 -jeu utilisant un canevas natif.
Préparer les matériaux (10min) Modifier les matériaux (20min). Parce que je suis vraiment handicapé et pas doué en P-images, il m'a fallu environ 20 minutes pour modifier les images, 囧...
Développer l'interface de démarrage. Le jeu ne peut pas se passer de l'interface de démarrage, nous implémentons donc d'abord cette partie du code. Avant c'est le code dans index.html
, le code est le suivant :
<!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>
introduit principalement quelques fichiers js, pas grand chose à dire. Préparez ensuite un fichier Main.js et ajoutez l'interface d'initialisation et le code de chargement des ressources à ce fichier :
/** 初始化游戏 */ 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); /** 添加Introduction graphique complète du code du jeu de puzzle HTML5 en 2 heures */ addBeginningUI(); }
Le code ci-dessus a des commentaires détaillés, vous pouvez le lire selon la documentation et les commentaires du moteur. Certaines variables globales seront utilisées dans le futur code, vous pouvez donc les ignorer pour le moment. Vient ensuite le code de la fonction addBeginningUI
, utilisé pour implémenter l'interface de démarrage :
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(); }); }
À ce stade, exécutez le code et obtenez notre interface de démarrage :
Quand j'ai vu cette photo, j'avais en fait envie de me plaindre qu'elle était trop "simple", 囧...
Cependant, j'essaie de montrer la vitesse de production cette fois, donc j'espère vous pouvez le lire.
Ces 40 minutes sont la période la plus critique, pendant laquelle nous devons terminer la partie principale de tout le jeu. Tout d'abord, nous devons utiliser du code pour implémenter le processus suivant :
初始化游戏界面数据(如游戏时间、所用步数)和显示一些UI部件(如图样) | -> 获取随机的拼图块位置 | -> 显示打乱后的拼图块
Nous transformons ces étapes en fonctions afin de pouvoir les appeler uniformément :
function startGame () { isGameOver = false; /** 初始化时间和步数 */ startTime = (new Date()).getTime(); time = 0; steps = 0; /** 初始化拼图块列表 */ initBlockList(); /** 打乱拼图 */ getRandomBlockList(); /** 显示拼图 */ showBlock(); /** 显示缩略图 */ showThumbnail(); /** 显示时间 */ addTimeTxt(); /** 显示步数 */ addStepsTxt(); stageLayer.addEventListener(LEvent.ENTER_FRAME, onFrame); }
Au début du fonction, nous définissons la variable isGameOver
sur false
signifie que le jeu n'est pas terminé. Dans le code ultérieur, nous verrons le rôle de cette variable. Ensuite, nous avons initialisé les deux variables globales time
et steps
utilisées pour représenter le temps et le nombre de pas. De plus, la valeur de la variable initialisée startTime
a été utilisée pour calculer le temps de jeu ultérieurement.
Ensuite, nous commencerons à initialiser les pièces du puzzle. Voir le code dans initBlockList
:
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)); } }
Ici, nous utilisons une classe Block
, qui est utilisée pour afficher les pièces du puzzle et stocker les données des pièces du puzzle, et fournit quelques méthodes pour manipuler les pièces de puzzle, voici le code de son constructeur : la classe
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
hérite de LSprite
et appartient à un objet d'affichage, nous avons donc ajouté un objet bitmap à cette classe pour afficher les pièces de puzzle photo correspondante. En plus de cela, nous avons également ajouté une bordure à la pièce du puzzle, qui est utilisée pour séparer les pièces du puzzle environnantes lorsqu'elles sont affichées. La classe Block
a un attribut index
qui représente la position correcte de la pièce du puzzle dans la liste des pièces du puzzle blockList
. Enfin, nous avons ajouté un événement mouse down à cette classe pour gérer le déplacement de la vignette après avoir appuyé sur la souris.
Ensuite, nous présenterons également une méthode de cette classe setLocation
:
Block.prototype.setLocation = function (x, y) { this.locationX = x; this.locationY = y; this.x = x * 130; this.y = y * 130; };
Cette méthode est utilisée pour définir la position d'affichage de l'objet pièce de puzzle et enregistrer la "position du tableau" de la pièce du puzzle. Qu'est-ce que la « position du tableau » ? Les lecteurs peuvent en apprendre davantage à travers les images suivantes :
Comme vous pouvez le voir, la « position du tableau » est similaire à l'indice de l'élément dans un tableau à deux dimensions. Le but de stocker cet emplacement est d'accéder facilement à d'autres pièces de puzzle à proximité depuis blockList
. Cette méthode est appelée lorsque l'on affiche le puzzle. Avant d'afficher le puzzle, il faut d'abord brouiller le puzzle Voir le code suivant :
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(); } }
La partie du puzzle brouillée est directement randomisée à l'aide de la méthode sort
. du tableau. Perturber :
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)!