图片 15

这种游戏实际上是由第一位玩家所控制,希望我能把代码里的bug改一下方便初学者学习

初学lufylegend.js之日,作者用lufylegend.js开拓了第三个HTML5小游戏——拼图游戏,还写了篇博文来炫人眼目一下:HTML5小游戏《智力大拼图》公布,挑衅你的斟酌沙暴。不过当下初学游戏开采,资历浅薄,所以未有美貌专研游戏里的算法和代码的劣势,诱致游戏现身了累累bug,以致拼图打乱后极大概不能够恢复生机。近年来经常有朋友问起那一个游乐,希望笔者能把代码里的bug改一下方便人民群众初读书人学习,顺便小编也准备测量检验一下温馨写这种小游戏的快慢,所以就抽取了有个别时刻将以此娱乐从头至尾重新写了叁次,计算了一晃用时,从筹划、改进质地到最终成功娱乐,一共用了大约2h的光阴。

时间: 2019-08-10阅读: 589标签: 游戏

效果图&DEMO

一,什么是TicTacToe(井字棋)

本游戏为在下用lufylegend开辟的第二款小游戏。此游玩是贵裔可能我们小时候都玩过,因为玩它异常的粗略,只须求一张草稿纸和三只笔就能够初阶游戏,所以广受孩子款待。恐怕本身说了半天,对它名字不熟习的相恋的人也不懂笔者在说神马。那不妨,小编就引述Wiki(维基百科)的牵线作为大家对它名字的认知,顺便也勾起大家小时候的回想:

井字棋,大陆、江西又叫做井字游戏、圈圈叉叉;其它也会有打井游戏、OX棋的称之为,香港(Hong KongState of Qatar多称井字过三关、过三关,是种纸笔游戏。八个游戏用户,叁个打圈(O卡塔尔国,一个打叉(XState of Qatar,轮番在3乘3的格上打自个儿的暗号,最早以横、直、斜连成一线则为胜。若是两个都下得准确正确,将得和局。这种娱乐实际上是由第一人游戏用户所主宰,第4个人游戏的使用者是攻,第四个人游戏者是守。第壹个人游戏用户在角位行第一子的话赢面最大(见图一),第2个人游戏者假如在边,角位下子,第2个人游戏发烧友就能够以两粒连线牵制着第一位游戏者,然后创设“三头蛇”。

图片 1

图一

 

写在前头

上一篇小说小编写了贰个简易的iOS
拼图游戏(儿时的记念——拼图游戏),现在本身要让那一个游戏聪明起来,支持您来完毕拼图。写那篇作品的时候正还好看《最强大脑》,节目里的第二个PK正是回复这种拼图(非图而是数字,数字华容道),节目塑造了要命忐忑的空气,其实这种拼图复原算是比较轻易的。
不再前戏,直接步向正题:游戏源码点这里(拼图游戏),您能够从那份源码中get到的技巧点:

> 设置代理类为调节器控食
> A*算法(含依据完全二叉树完成优先队列)
> GCD实信号量调整数组遍历流量
> 电火花计时器+GCD实信号量实现数组遍历的中止、继续

游戏效果:

图片 2

娱乐效果.gif

以下是娱乐地址:

眼看初学游戏开辟,涉世浅薄,所以未有优良专研游戏里的算法和代码的老毛病,以致游戏产出了成百上千bug,以致拼图打乱后很恐怕不能够复苏。前段时间时时有意中人问起那些游乐,希望自身能把代码里的bug改一下惠及初读书人学习,顺便笔者也筹划测验一下协调写这种小游戏的快慢,所以就收取了一部分时光将那几个娱乐从头至尾重新写了一回,总括了一下用时,从酌量、修正材质到结尾成功娱乐,一共用了大致2h的年华。

图片 3

二,游戏在何地玩?

言听计从大家看了介绍就对井字棋有了询问。现在自己用HTML5匹配开源游戏引擎lufylegend开垦出了这一款游戏,并促成了智能AI(AI)确定保证游戏中游戏的使用者能棋缝敌手。

接下去是十二日游在线试玩和下载源码之处:

下载地址(含源代码):

在线试玩地址:

 

基本思路

那么怎么样让本来土冒气质的娱乐全部人工智能(AI),自动回复拼图,从此今后华丽化身体高度大上啊?

  • 方案一:
    我们超级轻松想到的法子,按打乱的一一逆序还原。此方案的失调顺序将在原来严守原地的图纸根据游戏准绳移动多次,进而达成打乱效果。可是本身想你们已经意识了叁个标题,小编所陈设的一日游打乱后空位设置在右下角,大家还要求想方法将空位移动到右下角的目之处。
  • 方案二:
    不关怀打乱的长河,依次将编号0、1、2…
    回归到科学地方,逐步压缩乱序图区域。可是再三最终两行的几块要求多少调度下计策。
  • 方案三:
    不珍重打乱的经过,从打乱后的景观最早,依据早晚限制原则,对下一步的有余也许性举行搜寻剖断,稳步演进,进而寻觅复原步骤。作者选拔的是A*寻找算法。

那是本人的嬉戏记录,招待各位挑战:

效果图

三,游戏截图

图片 4

图片 5

 

四,游戏引擎

 

本游戏运用国产的lufylegend引擎,版本为1.6.1,倘使我们感兴趣能够去官方网址看看

lufylegend官方网站:

lufylegend API文档:

下面有此引擎的下载和API介绍。关于用lufylegend开荒娱乐的其余作品:

【HTML5玩耍开拓】不难的《找区别汉字版》,来考考你的眼力吧

 

方案解读

那是自己的游乐记录,招待各位挑衅:

接下去就来说讲怎么着开采产生那款游戏的。(按“编年体”)

一、策画工作

先精通三个概念和定理

定义:在贰个1,2,…,n的排列中,借使一对数的左右地点与大小顺序相反,前几天前的数超越前边的数,那么它们就叫做二个逆序。贰个排列中逆序的总和就称为这几个排列的逆序数。逆序数为偶数的排列称为偶排列;逆序数为奇数的排列称为奇排列。如2431中,21,43,41,31是逆序,逆序数是4,为偶排列。——那是清华《高端代数》上的定义。

定理:调换叁个排列中的三个数,则排列的奇偶性发生变动。

五,算法&代码疏解

先来个游戏带头化:

init(30,"mylegend",390,420,main);

为了方便操作游戏中的一些数量,大家设定大多变量:

var backLayer,chessLayer,overLayer;
var statusText = new LTextField();
var statusContent="您先请吧……";
var matrix = [
    [0,0,0],
    [0,0,0],
    [0,0,0]
];
var usersTurn = true;
var step = 0;
var title = "井字棋";
var introduction = ""
var infoArr = [title,introduction];

第一行是层变量;第二行是实例化的文本框对象,用来展现文字;第三行是现阶段显示新闻的文字,比如该哪方走,哪方赢了等,会依附不相同景况改动。

 

matrix是用来保存当前棋盘数据的数组,要是下一步棋,就能变动内部多少,顺便也说一下,为了区别【空白格子】,【作者方下的职位】,【计算机下的地点】,我们用-1来代表【作者方下的岗位】,用0来表示【空白格子】,1来代表【计算机下的职责】;看官且记,那-1,0,1在棋盘数组中便各有了代表意义。

userTurn是用来推断游戏的使用者是或不是能够下棋;step是用来代表走的步数,用来剖断棋盘是或不是下满;title,introduction还有infoArr原来是用来构建关于分界面包车型地铁,结果做到最终即便了,大家一向忽视掉吗。

接下去正是main函数,由于并未有图片,所以就从未加载部分了:

function main(){
    gameInit();
    addText();
    addLattice();    
}

main调用的多少个函数如下:

function gameInit(){
    initLayer();
    addEvent();
}

function addText(){
    statusText.size = 15;    
    statusText.weight = "bold";
    statusText.color = "white";
    statusText.text = statusContent;
    statusText.x = (LGlobal.width-statusText.getWidth())*0.5;
    statusText.y = 393;

    overLayer.addChild(statusText);
}

function addLattice(){
    backLayer.graphics.drawRect(10,"dimgray",[0,0,390,420],true,"dimgray");
    backLayer.graphics.drawRect(10,"dimgray",[0,0,390,390],true,"lavender");
    for(var i=0;i<3;i++){
        backLayer.graphics.drawLine(3,"dimgray",[130*i,0,130*i,390]);
    }
    for(var i=0;i<3;i++){
        backLayer.graphics.drawLine(3,"dimgray",[0,130*i,390,130*i]);
    }
}

解释一下他们的遵守。首先,gameInit是用来开端化游戏的,包涵开首化层一类的事物。addText是用来加上边文字的。addLattice使用来画棋盘的。代码很简短,参照lufylegend
API文书档案看一下就会看懂。

 

接下去大家来看gameInit里调用的函数:

function initLayer(){
    backLayer = new LSprite();
    addChild(backLayer);

    chessLayer = new LSprite();
    backLayer.addChild(chessLayer);

    overLayer = new LSprite();
    backLayer.addChild(overLayer);
}
function addEvent(){
    backLayer.addEventListener(LMouseEvent.MOUSE_DOWN,onDown);
}

initLayer是用来实例化层的,表达了有些就是实例化LCoca Cola。addEvent用来加点击事件。

 

接下来接下去就来拜候事件触发的onDown:

function onDown(){
    var mouseX,mouseY;
    mouseX = event.offsetX;
    mouseY = event.offsetY;

    var partX = Math.floor(mouseX/130);
    var partY = Math.floor(mouseY/130);
    if(matrix[partX][partY]==0){
        usersTurn=false;
        matrix[partX][partY]=-1;
        step++;
        update(partX,partY);

        if(win(partX,partY)){
            statusContent = "帅呆了,你赢啦!点击屏幕重开游戏。";
            gameover();
            addText();
        }else if(isEnd()){
            statusContent = "平局啦~~点击屏幕重开游戏。";
            gameover();
            addText();
        }else{
            statusContent = "电脑正在思考中……";
            addText();
            computerThink();
        }
    }
}

其一函数要做的就是先收取点击地方,然后依据点的岗位下一颗棋,然后就要棋盘数组中相应的职分设为-1,表示是小编方走的,然后决断:下了这一步棋后的胜败或许平局意况,何况调用相应的函数和呈现相应的文字。决断赢,大家用win函数,代码如下:

function win(x,y){
    if(Math.abs(matrix[x][0]+matrix[x][1]+matrix[x][2])==3){
        return true;
    }
    if(Math.abs(matrix[0][y]+matrix[1][y]+matrix[2][y])==3){
        return true;
    }
    if(Math.abs(matrix[0][0]+matrix[1][1]+matrix[2][2])==3){
        return true;
    }
    if(Math.abs(matrix[2][0]+matrix[1][1]+matrix[0][2])==3){
        return true;
    }
    return false;
}

率先大家看清第x行,第0,1,2列的数字相加的绝对值是还是不是为3(由于那一个函数在底下还要采纳,所以大家要做得通用性,所以就用了相对值)。为啥等于3呢?因为看官是还是不是记得我们地方说的:-1代表【我方下的职分】,0代表【空白格子】,1意味着【计算机下之处】。但凡是下了棋的地点,值总是1要么-1,所以一旦有多个相似方棋子连在一同,那那多少个值加起来的相对值一定是3。由此就回到true代表赢了。如若直白剖断到最终都未有,就再次来到false,代表还从未赢。

 

大家用isEnd判定平局,代码如下:

function isEnd(){
    return step>=9;
}

代码很简短,便是判定棋盘占满未有。

里面用到updata承受更新棋盘。代码如下:

function update(x,y){
    var v = matrix[x][y];
    if(v>0){
        chessLayer.graphics.drawArc(10,"green",[x*130+65,y*130+65,40,0,2*Math.PI]);
    }else if(v<0){
        chessLayer.graphics.drawLine(20,"#CC0000",[130*x+30,130*y+30,130*(x+1)-30,130*(y+1)-30]);
        chessLayer.graphics.drawLine(20,"#CC0000",[130*(x+1)-30,130*y+30,130*x+30,130*(y+1)-30]);
    }
}

以上的代码也很好理解,正是先收取画的那点是什么,如若是本身方画的(在棋盘数组就是-1),在认清时,抽出的值倘若小于0,就画个叉叉。如若大于0也正是代表计算机画的(在棋盘数组深爱味着1),就画个圆。

onDown中还用到了gameOver函数,代码如下:

function gameover(){
    backLayer.removeEventListener(LMouseEvent.MOUSE_DOWN,onDown);
    backLayer.addEventListener(LMouseEvent.MOUSE_DOWN,function(){
        chessLayer.removeAllChild();
        backLayer.removeChild(chessLayer);
        backLayer.removeChild(overLayer);
        removeChild(backLayer);
        matrix = [
            [0,0,0],
            [0,0,0],
            [0,0,0]
        ];
        step = 0;
        main();
        statusContent = "您先请吧……";
        addText();
    });
}

肖似代码有一些长,其实非常轻易,正是简单的移除分界面上的整个对象,何况把某个值苏醒为私下认可值。还应该有onDown中的computerThink函数,代码如下:

function computerThink(){
    var b = best();
    var x = b.x;
    var y = b.y;
    matrix[x][y]=1;
    step++;
    update(x,y);

    if(win(x,y)){
        statusContent = "哈哈你输了!点击屏幕重开游戏。";
        gameover();
        addText();
    }else if(isEnd()){
        statusContent = "平局啦~~点击屏幕重开游戏。";
        gameover();
        addText();
    }else{
        statusContent = "该你了!!!";
        addText();
    }
}

先是那些函数用了best函数,这些函数会再次回到贰个要下的地点,然后大家把在棋盘数组中相应的岗位设置为1,何况把走的步数+1。然后在对应岗位画上。然后推断是不是赢了大概平局,只怕没赢没输没平局。

best是ComputerAI算法部分,代码如下:

function best(){
    var bestx;
    var besty;
    var bestv=0;
    for(var x=0;x<3;x++){
        for(var y=0;y<3;y++){
            if(matrix[x][y]==0){
                matrix[x][y] = 1;
                step++;
                if(win(x,y)){
                    step--;
                    matrix[x][y] = 0;    
                    return {'x':x,'y':y,'v':1000};
                }else if(isEnd()){
                    step--;
                    matrix[x][y]=0;    
                    return {'x':x,'y':y,'v':0};
                }else{
                    var v=worst().v;
                    step--;
                    matrix[x][y]=0;
                    if(bestx==null || v>=bestv){
                        bestx=x;
                        besty=y;
                        bestv=v;
                    }
                }
            }
        }
    }
    return {'x':bestx,'y':besty,'v':bestv};
}

算法的笔触如下:首先大家遍历棋盘数组,然后判定遍历到的那格借使是空的(也就值是0)就先假诺画上,何况就要棋盘数组中相应的岗位设为1,表示Computer是下的,然后将走的步数+1。普通的操作就完了,接下去正是给下的这一步评分阶段,代码如下:

if(win(x,y)){
    step--;
    matrix[x][y] = 0;    
    return {'x':x,'y':y,'v':1000};
}else if(isEnd()){
    step--;
    matrix[x][y]=0;    
    return {'x':x,'y':y,'v':0};
}else{
    var v=worst().v;
    step--;
    matrix[x][y]=0;
    if(bestx==null || v>=bestv){
        bestx=x;
        besty=y;
        bestv=v;
    }
}

率先我们看清一下假设下了这一步,是还是不是就赢了,倘若是,就先把步数改回去,何况把棋盘数组改为下这一步事情发生前的棋盘数组(因为大家在computerThink里要改一道,所以先改回去,制止改重了),然后回来这一步的职分,何况评分为1000。最终这么些进度用return来落到实处,return是神马,笔者想就别讲了呢。推断是还是不是赢了,大家用了win函数,下面已经说过了。

 

唯独万一晃了这一步没赢咋办,就跟着判定是或不是下了成平局,怎么技巧成平局呢?正是把方方面面棋盘占满且对方并没有赢,本身也尚无赢正是平手。由于假使别人赢了,就不博览会开计算机AI,也就不会调用best函数,换句话说就是不容许打开到这一步;倘使是计算机赢了,在上头判定中已经做了相应操作何况用return已经推出函数了,也不会运作到此步,因而直接推断占满未有就能够了。因而用到isEnd函数,上边也用到过,何况讲到过,这里不罗嗦。

比方上边包车型地铁二种处境都不对如何做?那就随意下个呢。不过无论下也不可能乱下。因此利用了worst来选取“随意下”最佳的岗位。代码如下:

function worst(){
    var bestx;
    var besty;
    var bestv = 0;
    for(var x=0;x<3;x++){
        for(var y=0;y<3;y++){
            if(matrix[x][y] == 0){
                matrix[x][y] = -1;
                step++;
                if(win(x,y)){
                    step--;
                    matrix[x][y] = 0;    
                    return {'x':x,'y':y,'v':-1000};
                }else if(isEnd()){
                    step--;
                    matrix[x][y]=0;    
                    return {'x':x,'y':y,'v':0};;
                }else{
                    var v=best().v;
                    step--;
                    matrix[x][y]=0;
                    if(bestx==null || v<=bestv){
                        bestx=x;
                        besty=y;
                        bestv=v;
                    }
                }

            }
        }
    }
    return {'x':bestx,'y':besty,'v':bestv};
}

其一函数和best是反着来的,它是一旦下了某一步后,外人会赢大概平局。借让人家走那步会赢,就回来这几个职位,把那个职位先占住。平局和对方赢是类似的法则,就是见哪儿不对就填哪儿。最终的判读是在对方相当的小概赢的情形下使用的,正是通过best函数取最棒的。这一个best函数在地方讲过了。不作解释了~~

透过worst那一个函数会回去几个值,第多个和第一个是无论下的义务,最后二个是评分。在best中大家把那多少个再次回到值选择到,而且通过评分决断这一个选项是或不是比平局的结果还要差,再回到给computerThink这么些函数来作画结构。因而这些进程很绕。我们要搞明白关系,搞明白了就轻巧了。

* 方案一

方案一实现起来较为轻松,假定大家前些天曾经将一直以来的(处于复原态)拼图移动了多次,也便是拼图打乱了(如下图左)。接下来将空位移动到右下角。

图片 6

图1.png

眼看,移动的国策有为数不菲种,我们只要求一种,譬如那样:空位先稳步横向移动到最左边,再稳步纵向移动到最下侧。你看,能够啊。(上航海用教室左侧)

算法分解:

1。求出当前空位所在行、列。
2。当前空位与其右侧格子换位。
3。重复1、2,直到空位位于最右侧(最大列)。
4。当前空位与其下侧格子换位。
5。重复1、4,直到空位位于最下侧(最大行)。

这么,大家曾经成功了打乱步骤。当然,我们移动的每一步都亟待记录在案,以便遵照记录逆步骤进行苏醒。注:作者提供的游艺源码中并从未富含该方案(方案一)的代码完结,感兴趣的读者可以活动达成。

图片 7

预备阶段

二、实现进程

以3*3拼图为例进行深入分析

* 方案二

对此方案二,写那篇小说的时候才有的时候思虑参与这一个方案。基本思路上文中其实已经表明,暂不展开琢磨。

接下去就来说讲什么支付到位那款游戏的。(按“编年体”)

有备无患lufylegend游戏引擎,大家能够去官网下载:lufylegend.com/lufylegend

1、随机打乱拼图

1)伊始化从0-8的数组initializeNums

NSMutableArray *initializeNums = [NSMutableArray array];//初始化0-n数字
for (int i = 0; i < _puzzleCount; i++) {
    [initializeNums addObject:@(i)];
}

2)从initializeNums随机抽取数字add到数组randomNums,获得随机数组

NSMutableArray *randomNums = [NSMutableArray array];//随机数组
for (int i = 0; i < _puzzleCount; i++) {   
    int randomNum = arc4random() % initializeNums.count;
    [randomNums addObject:initializeNums[randomNum]];
    [initializeNums removeObjectAtIndex:randomNum];   
}

3)决断拼图是或不是可还原

图1,通过活动要还原到的拼图状态
图2,随机打乱的拼图状态

图3,将图2中的空白块移动到拼图右下角的拼图状态,用来总计打乱的拼图是还是不是足以还原
④ 空白块处约等于数字8

大家的目标是把打乱拼图如图2经过移动(空白块与邻座数字块地点交换)还原到图1状态

不是每一种随机打乱的拼图都能还原到图1状态(根据定义定理有二分之一概率随机打乱的拼图不可能大张旗鼓)
⑦ 根据定义定理
图1的逆序数为0,为偶排列。所以独有图3也为偶排列,图2才有希望苏醒到图1状态

图片 8

图1

图片 9

图2

怎么总结图3的逆序数

① 先计算图2的逆序数
② 再计算图2图3调换步数
③ 将双方相加即得图3逆序数

图片 10

图3

判断图2是还是不是可复原代码:

//判断是否可还原拼图
inverCount = 0;
int curNum = 0;
int nextNum = 0;
for (int i = 0; i < _puzzleCount; i++) {
    curNum = [randomNums[i] intValue];
    if (curNum == _puzzleCount - 1) {
        inverCount += _difficulty - 1 - (i / _difficulty);
        inverCount += _difficulty - 1 - (i % _difficulty);
    }
    for (int j = i + 1; j < _puzzleCount; j++) {
        nextNum = [randomNums[j] intValue];
        if (curNum > nextNum) {
            inverCount++;
        }
    }

}
if (!(inverCount % 2)) {//对2求余,余0,逆序数为偶数,即偶排列;否则,为奇排列
    return randomNums;
}

获得人身自由可复原的数组randomNums

- (NSMutableArray *)getNewAvailableRandomNums {

    //随机数字
    int inverCount = 0;
    while (1) {
        NSMutableArray *initializeNums = [NSMutableArray array];//初始化0-n数字
        for (int i = 0; i < _puzzleCount; i++) {
            [initializeNums addObject:@(i)];
        }

        NSMutableArray *randomNums = [NSMutableArray array];//随机数组
        for (int i = 0; i < _puzzleCount; i++) {

            int randomNum = arc4random() % initializeNums.count;

            [randomNums addObject:initializeNums[randomNum]];

            [initializeNums removeObjectAtIndex:randomNum];

        }
        //判断是否可还原拼图
        inverCount = 0;
        int curNum = 0;
        int nextNum = 0;
        for (int i = 0; i < _puzzleCount; i++) {
            curNum = [randomNums[i] intValue];
            if (curNum == _puzzleCount - 1) {
                inverCount += _difficulty - 1 - (i / _difficulty);
                inverCount += _difficulty - 1 - (i % _difficulty);
            }
            for (int j = i + 1; j < _puzzleCount; j++) {
                nextNum = [randomNums[j] intValue];
                if (curNum > nextNum) {
                    inverCount++;
                }
            }

        }
        if (!(inverCount % 2)) {//对2求余,余0,逆序数为偶数,即偶排列;否则,为奇排列
            return randomNums;
        }

    }
}

* 方案三

对此方案三,轻巧联想到尝试每个步骤只怕性,最后选出能够还原的步骤链。

图片 11

图2.png

上海体育场所即表示各样景况衍生出的或是路线,清除了再也的境况。对于这种暴力寻找算法,品质是非常的低的(关于寻觅算法,作者原先的篇章有介绍过最短路线的多个精髓算法

策动阶段

有备无患未雨策动lufylegend游戏引擎,大家能够去官网下载:

lufylegend.com/lufylegend

电动机文书档案地址:

lufylegend.com/lufylegend/api

可以说,若无强盛的lufylegend引擎,这种html5小游戏用原生canvas制作,少说要一天吧。

发动机文书档案地址:lufylegend.com/lufylegend/api

2、开端化拼图UI (九宫格)

代码:

- (void)customUI {
    CGFloat puzzleBgViewX = 0;
    CGFloat puzzleBgViewY = 64 + 20;
    CGFloat puzzleBgViewW = [UIScreen mainScreen].bounds.size.width;
    CGFloat puzzleBgViewH = puzzleBgViewW;

    _puzzleBgView = [[UIView alloc] initWithFrame:CGRectMake(puzzleBgViewX, puzzleBgViewY, puzzleBgViewW, puzzleBgViewH)];
    _puzzleBgView.backgroundColor = [UIColor lightGrayColor];
    [self.view addSubview:_puzzleBgView];

    CGFloat puzzleBtnX = 0;
    CGFloat puzzleBtnY = 0;
    CGFloat puzzleBtnW = puzzleBgViewW / _difficulty - kPuzzleBtnGap * 2;
    CGFloat puzzleBtnH = puzzleBtnW;

    for (int i = 0; i < _puzzleCount; i++) {
        puzzleBtnX = i % _difficulty * (puzzleBtnW + kPuzzleBtnGap * 2) + kPuzzleBtnGap;
        puzzleBtnY = i / _difficulty * (puzzleBtnH + kPuzzleBtnGap * 2) + kPuzzleBtnGap;
        UIButton *puzzleBtn = [UIButton buttonWithType:UIButtonTypeCustom];
        puzzleBtn.frame = CGRectMake(puzzleBtnX, puzzleBtnY, puzzleBtnW, puzzleBtnH);
        puzzleBtn.tag = i;
        puzzleBtn.clipsToBounds = YES;
        [_puzzleBgView addSubview:puzzleBtn];

        int  puzzleValue = [self.randomNums[i] intValue];
        if (puzzleValue == _puzzleCount - 1) {
            puzzleBtn.backgroundColor = [UIColor clearColor];
            _maxPuzzleBtn = puzzleBtn;
        } else {
                [puzzleBtn setTitle:[NSString stringWithFormat:@"%d", puzzleValue + 1] forState:UIControlStateNormal];
                puzzleBtn.backgroundColor = [UIColor colorWithRed:0x4A / 255.0 green:0xC2 / 255.0 blue:0xFB / 255.0 alpha:1];
            [puzzleBtn addTarget:self action:@selector(puzzleBtnAction:) forControlEvents:UIControlEventTouchUpInside];
        }
    }
}

点我)。拼图为4阶方阵时,拼图状态数(4*4)!

0~30min

预备资料(10min) +
校勘材料(20min)。由于在下实际手残,不专长P图,改过图片用了大约20min,囧……

可以说,若无强盛的lufylegend引擎,这种html5小游戏用原生canvas制作,少说要一天吧。

3、滑块移动逻辑

点击空白块周边数字块,数字块移到空白块区域(其实正是空白块和数字块交流)

图片 12

图4

index:数字块对应地方如图4
_difficulty : 拼图列数
③ 点击数字块依次推断其 是或不是有空白块
④ 找到空白块,将点击数字块与空白块地方调换,达成数字块移动作效果果

以数字块3(index = 4)为例深入分析

upIndex = index - _difficulty
推断是或不是在九宫格里&&其地点对应的值是或不是是8,即空白块。

upIndex >= 0 && [self.randomNums[upIndex] intValue] == _puzzleCount - 1

downIndex = index + _difficulty
决断是或不是在九宫格里&&其岗位对应的值是不是是8,即空白块。

if (downIndex <= _puzzleCount - 1 && [self.randomNums[downIndex] intValue] == _puzzleCount - 1

leftIndex = index - 1
推断是还是不是在九宫格里&&其义务对应的值是还是不是是8,即空白块

index % _difficulty > 0 && [self.randomNums[leftIndex] intValue] == _puzzleCount - 1

rightIndex = index + 1
剖断是还是不是在九宫格里&&其地点对应的值是还是不是是8,即空白块

index % _difficulty < _difficulty - 1 && [self.randomNums[rightIndex] intValue] == _puzzleCount - 1

代码:

- (void)puzzleBtnAction:(UIButton *)puzzleBtn {
    NSInteger index = puzzleBtn.tag;

    //上
    NSInteger upIndex = index - _difficulty;
    if (upIndex >= 0 && [self.randomNums[upIndex] intValue] == _puzzleCount - 1) {

        CGPoint maxPuzzleBtnCenter = _maxPuzzleBtn.center;
        CGPoint puzzleBtnCenter = puzzleBtn.center;
        _maxPuzzleBtn.tag = index;
        puzzleBtn.tag = upIndex;
        self.randomNums[upIndex] = @([self.randomNums[index] intValue]);
        self.randomNums[index] = @(_puzzleCount - 1);
        [UIView animateWithDuration:0.35 animations:^{
            puzzleBtn.center = maxPuzzleBtnCenter;
            _maxPuzzleBtn.center = puzzleBtnCenter;
        }];

        [self isWin];

        return;

    }
    //下
    NSInteger downIndex = index + _difficulty;
    if (downIndex <= _puzzleCount - 1 && [self.randomNums[downIndex] intValue] == _puzzleCount - 1) {
        CGPoint maxPuzzleBtnCenter = _maxPuzzleBtn.center;
        CGPoint puzzleBtnCenter = puzzleBtn.center;
        _maxPuzzleBtn.tag = index;
        puzzleBtn.tag = downIndex;
        self.randomNums[downIndex] = @([self.randomNums[index] intValue]);
        self.randomNums[index] = @(_puzzleCount - 1);
        [UIView animateWithDuration:0.35 animations:^{
            puzzleBtn.center = maxPuzzleBtnCenter;
            _maxPuzzleBtn.center = puzzleBtnCenter;
        }];

        [self isWin];
        return;
    }
    //左
    NSInteger leftIndex = index - 1;
    if (index % _difficulty > 0 && [self.randomNums[leftIndex] intValue] == _puzzleCount - 1) {
        CGPoint maxPuzzleBtnCenter = _maxPuzzleBtn.center;
        CGPoint puzzleBtnCenter = puzzleBtn.center;
        _maxPuzzleBtn.tag = index;
        puzzleBtn.tag = leftIndex;
        self.randomNums[leftIndex] = @([self.randomNums[index] intValue]);
        self.randomNums[index] = @(_puzzleCount - 1);
        [UIView animateWithDuration:0.35 animations:^{
            puzzleBtn.center = maxPuzzleBtnCenter;
            _maxPuzzleBtn.center = puzzleBtnCenter;
        }];

        [self isWin];
        return;
    }
    //右
    NSInteger rightIndex = index + 1;
    if (index % _difficulty < _difficulty - 1 && [self.randomNums[rightIndex] intValue] == _puzzleCount - 1) {
        CGPoint maxPuzzleBtnCenter = _maxPuzzleBtn.center;
        CGPoint puzzleBtnCenter = puzzleBtn.center;
        _maxPuzzleBtn.tag = index;
        puzzleBtn.tag = rightIndex;
        self.randomNums[rightIndex] = @([self.randomNums[index] intValue]);
        self.randomNums[index] = @(_puzzleCount - 1);
        [UIView animateWithDuration:0.35 animations:^{
            puzzleBtn.center = maxPuzzleBtnCenter;
            _maxPuzzleBtn.center = puzzleBtnCenter;
        }];

        [self isWin];
        return;
    }

}

20922789888000,广搜算法已基本不可能搜出结果,直到爆内存。拼图为5阶方阵时,状态数(5*5)!

1.551121004333098e25,10的二十二回方。先别焦急,大家得以选拔质量较高的A*算法为拼图游戏助力。

  • A*算法简要介绍:
    A*(A-Star卡塔尔(قطر‎算法是一种静态路网中求解最短路线最管用的直接搜索方法,也是一扫而光广大搜索难点的平价算法。它并非搜索各项也许的意况,而是有接收地启迪式找出,每三回在多少个情景中选拔大概最周围目标状态的三个。依此层层找出,显著比较暴力寻找,少走了众多的弯路。
    正文并不准备深切描述该算法技巧细节。推荐阅读A*算法详细明白。

对此该游戏,对每一个情景到对象状态的“间隔”举办估摸,将每一个四方偏离它科学地方的相距实行增添,取偏离值最小者择优选择。图示表明:

图片 13

图3.png

当前状态估价(本例中也就是曼哈顿距离)计算方式:
4距离它正确的位置也就是上图中7的位置:横向1+纵向1=2;
1距离它正确的位置0;
3距离它正确的位置也就是上图中2的位置:横向2+纵向1=3;
2距离它正确的位置也就是上图中3的位置:横向2+纵向1=3;
7距离它正确的位置也就是上图中5的位置:横向0+纵向1=1;
0距离它正确的位置也就是上图中4的位置:横向2+纵向1=3;
6距离它正确的位置0;
我们给予空格位特权,不对它考核。
5距离它正确的位置也就是上图中0的位置:横向0+纵向1=1;
然后将上述距离值累加。
当然实际还可以在这个基础上对估价进行调整,比如乘以一定系数。

30~50min

支付早先分界面。游戏不可能没有从头分界面所以大家第一完结这一部分代码。早先是index.html里的代码,代码如下:

<!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;">
    <div id="mygame"></div>
</body>
</html>

覆水难收是引进一些js文件,相当的少说。然后准备三个Main.js文件,在此个文件里增加开头化分界面和加载财富的代码:

/** 初始化游戏 */
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);

    /** 添加开始界面 */
    addBeginningUI();
}

上述代码有详实注明,我们可以对照引擎文书档案和注释进行阅读。有个别全局变量会在之后的代码中选取,大家能够先忽略。接下来是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();
    });
}

到此,运营代码,获得我们的开首分界面:

图片 14

看来那个画面,其实本身要好都想调侃一下其实是太“朴素”了,囧……

而是本身此次图个创造速度,所以还望各位看官海量。

开始

*4、另一种打乱拼图的章程

思路:将图1透过轻便次数随机移动达成打乱拼图的目标,那样打乱的拼图肯定是可复原的。

代码:

- (NSMutableArray *)getNewAvailableRandomNums2 {

   NSMutableArray *randomNums = [NSMutableArray array];//随机数组 - 初始化0-n数字
    for (int i = 0; i < _puzzleCount; i++) {
        [randomNums addObject:@(i)];
    }

    int randCount = _puzzleCount * _puzzleCount;
    int randDirection = 0; //0 上 1 下 2 左 3 右
    BOOL aliableDirection = NO;
    int blankIndex = 8;
    int index = 0;
    while (randCount--) {

        aliableDirection = NO;
        randDirection = arc4random() % 4;
        while (1) {
            switch (randDirection) {
                case 0:

                    if (blankIndex / _difficulty > 0) {
                        index = blankIndex - _difficulty;
                        aliableDirection = YES;
                    }
                    break;
                   case 1:

                    if (blankIndex / _difficulty < _difficulty - 1) {
                        index = blankIndex + _difficulty;
                        aliableDirection = YES;
                    }
                    break;
                case 2:

                    if (blankIndex % _difficulty > 0) {
                        index = blankIndex - 1;
                        aliableDirection = YES;
                    }
                    break;
                case 3:

                    if (blankIndex % _difficulty < _difficulty - 1) {
                        index = blankIndex + 1;
                        aliableDirection = YES;
                    }
                    break;
                default:
                    break;
            }
            if (aliableDirection == YES) {
                break;
            }
            randDirection = (randDirection + 1) % 4;
        }

        randomNums[blankIndex] = @([randomNums[index] intValue]);
        randomNums[index] = @(8);
        blankIndex = index;

    }
    return randomNums;
}

属性优化(TODO)

不经常想到了以下优化倾向:

  • 眼下源码每一遍UICollectionView数据源刷新时,为大局刷新方式,实际除了重新载入参数游戏,每回都只是2个格子在变化。能够修正为局地刷新。
  • 现阶段源码为找寻达成后再展开拼图复原。供给成本一定的岁月。尝试边探索边拼图的情势(边“生产”边“花费”)。
  • 据他们说方格数的轻微(难易程度)灵活调节A*算法的揣度,意在优化游戏还原步数。

50~90min

那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表示游戏未终止,在中期的代码里,我们会看见这么些变量的机能。接着我们最早化了用于表示时间和步数的timesteps那四个全局变量,其余初步化变量startTime的值用于末端总计游戏时间。
接下去,大家将在起来早先化拼图块了。见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));
    }
}

此处大家利用了八个Block类,那个类用于展现拼图块和积存拼图块的多寡,并提供了部分办法来操控拼图块,上边是其构造器的代码:

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类世襲自LSprite,归于一个来得对象,所以大家在此个类中加多了一个位图对象用于体现拼图块对应的图形。除了那一个之外,我们还为拼图块增加了多个边框,在体现时用于隔离周边的拼图块。Block类有三个index品质,代表拼图块在拼图块列表blockList中的正确地方。最终,大家为此类增添了二个鼠标按下事件,用于拍卖鼠标按下后活动图块操作。

接下去大家还要介绍这几个类的贰个措施setLocation

Block.prototype.setLocation = function (x, y) {
    this.locationX = x;
    this.locationY = y;

    this.x = x * 130;
    this.y = y * 130;
};

这么些方法用于安装拼图块对象的显得地点以致保存拼图块的“数组地点”。什么是“数组地方”呢?各位看官可以由此上边包车型地铁图纸加以掌握:

图片 15

能够观望,“数组地方”就相同于二维数组中的成分下标。积累那些地点的效果在于能够很平价地从blockList中拿走到相邻的别的拼图块。那几个艺术在大家体现拼图时有调用到,在展现拼图早前,我们得先打乱拼图,见如下代码:

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();
    }
}

打乱拼图部分直接用数组的sort艺术实行自由打乱:

blockList.sort(function () {
    return 0.5 - Math.random();
});

实际打乱算法有不知凡三种,小编那边运用最凶狠的主意,相当于即兴打乱。这种算法轻易是简轻巧单,坏在恐怕现身不能够恢复生机的现象。针对这么些标题,就有配套的检查评定打乱后是或不是可过来的算法,具体的算法理论自身摘用lufy大神的评论:

该类游戏是不是重温旧梦重就算看它打乱后的逆序次数之和是还是不是为偶数
若果你打乱后的数组中的每八个小图块为obj0obj1obj2,…它们胡言乱语早前的序号分别为obj0.numobj1.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();
    }   
};

到此处,大家就贯彻了打乱和操作拼图块部分。

预备材质(10min) +
修正质地(20min)。由于在下实际手残,不专长P图,更正图片用了大约20min,囧……

三、别的细节效能

1、难度选拔 3*3(低), 4*4(中), 5*5(高)
2、自定义图片拼图(相机和相册)
3、图片拼图提醒
4、步数计算
5、最好记录
6、移动提醒音设置

现实请下载demo查看

鸣谢

正文配套源码中央政府机构接套用了《拼图游戏和它的AI算法》该文作者封装的A*算法,在那对原来的书文者表示感激。

90~120min

末段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这几个游戏引擎,实在是足以大幅晋级开荒功效。

开拓开端分界面。游戏不可能未有从头分界面所以我们率先贯彻那某些代码。以前是index.html里的代码,代码如下:

四、参考

1、不得恢复生机拼图
2、回看特出,叙述滑块游戏背后的数学轶事
3、董俊品游戏为主算法 Round 17 —— 张宏瑞教你玩拼图游戏(15
puzzle)

发表评论

电子邮件地址不会被公开。 必填项已用*标注

相关文章