HTML5游戏实战:计时拼图游戏制作

这次使用www.tangide.com制作了一款推广类拼图游戏 ,

这游戏复杂点在各种拖动, 滑动的动画实现上. 下面我主要分享下动画部分的实现方法.


0 在线运行: 

http://www.tangide.com/apprun.html?appid=previewupyunosgames1-241435729564104



1: 确认界面布局

HTML5游戏实战:计时拼图游戏制作_第1张图片

首先是对游戏功能区域的设计和

划分

滑动选择区在屏幕最下方, 每一块小的拼图会出现在这里, 玩家可以左右滑动让整个滑动区域轮动以查看全部拼图块.

这里我放入了一张图片在屏幕最左下角,游戏开始时通过对这张图的自动复制, 随机排序, 插入图片等操作来


拼图放置区在计时器下方, 玩家需要吧小拼图从滑动选择区拖到这个区域的正确位置以完成游戏.

这里是4X4的游戏, 我放置了16张小图片用来确认16个格子的位置, 并让这16个格子以第一个格子为基准自动对齐.

这么做确认麻烦但由于我是首次开发此类型游戏,这样做利于调试.


可以看到滑动选择区和拼图放置区之间有一段空隙, 这主要是为不同分辨率的手机预留的.截图分辨率为480*800.

这样的布局可以在不同屏幕比的设备 例如ip4S,5S上都显示得体.



2: 滑动区域初始化

HTML5游戏实战:计时拼图游戏制作_第2张图片

win.initPictrue = function() {
    var tempPicList = [];
    
    for (var i=1;i<=16;i++) {//排列图片
        picBar[i-1] = "图片"+i;
        tempPicList[i-1] = i-1;
        if (! win.find("图片"+i)) {
            win.dupChild("图片1").setName("图片"+i);
        }
        var x = (i-1)*basePic.w;
        win.find("图片"+i,true).setPosition(x,win.h-basePic.h);
        win.find("图片"+i,true).setVisible(true);
    }
    //打乱图片顺序
    
    var w = 404/4;
    var h = 404/4;
    
    for (i=0; i<16; i++) {
        var random = Math.floor(Math.random()*tempPicList.length);
        if (!DEBUG) {
            picOrder[i] = tempPicList[random];
            tempPicList.splice(random,1);
        }else {
            picOrder[i] = i;
        }
        var s = {xx:0,yy:0};
        coord.push(s);
        coord[i].xx = w*((i+1)%4 === 0 ? 3 : ((i+1)%4) - 1);
        coord[i].yy = h*(Math.ceil((i+1)/4) - 1);
        if (DEBUG) {
            console.log("dd_initPictrue x="+coord[i].xx+" y="+coord[i].yy);
        }
    }
    
    for (i=0;i<16;i++) {//给picBar的位置设置图片
        win.find(picBar[i]).setImageSrc(PUZZLEIMAGE);
        win.find(picBar[i]).setImageSrcRect(
            coord[picOrder[i]].xx, coord[picOrder[i]].yy, w, h);
        
        if (DEBUG) { 
            win.find(picBar[i]).setText(picOrder[i]+1);
        }
        win.find(picBar[i]).key = picOrder[i]+1;
    }
};


通过对屏幕下方拖动栏的初始化完成了picBar的随机排序, 随机插入图片, 以及最终用来判断游戏结束的key值记录.


3: 图片放置区域初始化

win.initSeat = function() {
    for (var i=1; i<=16; i++) {
        var x = baseSeat.x + baseSeat.w*((i%4) === 0 ? 3: (i%4) -1) + GAP;
        var y = baseSeat.y + baseSeat.h*(Math.ceil(i/4) -1) + GAP;
        //console.log("dd_initSeat SEAT="+SEAT + i);
        win.find("座位"+i).setPosition(x,y);
        seatGuest[i-1] = "";
    }
};
这一步自动调整好座位位置.


4: 图片选择区滑动效果


OnSwipeLeft = function(args) {
    var win = this.getWindow();
    win.swipeMe("left");
};

win.swipeMe = function(direction) {
    if(hold === ""
        || hold.indexOf("图片") !== 0
        || picHolding == 1
        || hold.y < win.h-basePic.h) {
        return;
    }
    console.log("dd_swipeMe "+direction);
            //距离限制:
    if (win.moveOutOfRange(direction)) {
        direction = "shake";
        return;
    }
    win.swipeAnimation(direction);
};


win.swipeAnimation = function(direction) {
    for (var i=1; i<=picBar.length ;i++) {
        var targetPic = win.find(picBar[i-1]);
        
        var swipe = {
            duration:500,
            xStart:targetPic.x,
            yStart:targetPic.y,
            xEnd:targetPic.x-3*targetPic.w,
            yEnd:targetPic.y,
            interpolator:"l"};
            
        if (direction == "left") {
            swipe.xEnd = targetPic.x-3*targetPic.w;
        
        }else if (direction == "right") {
            swipe.xEnd = targetPic.x+3*targetPic.w;
            
        }else if (direction == "shake") {
            swipe.xEnd = targetPic.x;
            swipe.interpolator = "l";
            
        }else {
            console.log("dd_swipeAnimation ERROR");
            return;
        }
        
        targetPic.animate(swipe,function onDone(_obj) {});
    }
}; 
通过获取窗口的滑动事件 并对其做出简单的位置判断后. 将整个picBar向左或向右使用animation移动. 

如果picBar第一个元素已经在屏幕内则无法继续从左往右滑, 反之最后一个元素在屏幕内时 无法从右向左滑.



4: 图片拖动效果

游戏中对图片的选择,拖动, 放开 我使用指针按下, 移动, 松开事件来完成.


拖出动作的逻辑

HTML5游戏实战:计时拼图游戏制作_第3张图片

1: 判断指针按下时必须选中的是有效成员, 该有效成员将成为hold  

2: 上一次指针按下以后又进行了指针移动(pointMove)且Y坐标移动到了一定范围以外, 此时将hold从picBar中删除 

3: 对所选中的picBar成员使用setPositon来实现拖动他的效果.

4: 对仍然在屏幕底端的 且在发生移动时处于hold成员右侧的picBar成员播放向左移动的动画.

5: 根据指针抬起时所在的位置判断需要执行放下图片 还是重置图片位置.



图片放下的逻辑: 

HTML5游戏实战:计时拼图游戏制作_第4张图片

1:放下的位置如是正确则播放动画提示玩家,

2: 放下位置如是非正确的seat则没有动画效果,只是将图片移动到的合适位置.

3: 放下位置如是无效区域则hold的图片返回到屏幕下边栏,且已在屏幕下边栏的picBar成员全体向右移动一格.

win.pointDown = function(point) {
    if (picBarPlaying == 1
        || picHolding == 1) {
        hold = "";
        return;
    }
    var targetElement = this.findChildByPoint(point, true);
    holdPoint = point;
    if (targetElement.name){
        if (targetElement.name == "图片0") {
            hold = "";
            return;
        }
        console.log("dd_you are point down at "+targetElement.name);
        
        if (targetElement.name == "游戏界面") {
            return;
        }
        
        if (targetElement.name.indexOf("图片") === 0) {
            hold = targetElement.name;
        }else {
            hold = "";
            return;
        }
        win.find(hold).setZIndex(50);
    }
};

win.pointMove = function(point) {
    if(!win.pointerDown
        || hold === "") {
        return;
    }
            
    if (point.y < win.h-basePic.h || picHolding === 1) {
        win.drawOutPic(point);//移动被hold的图片
        if (picHolding === 0) {//首次移动出范围需删除其位置数据
            picHolding = 1;
            
            if (holdPoint !== ""){//删除失效的seatGuest
                for (var s=1;s<=16;s++) {
                    var seat = win.find("座位"+s);
                    if (   holdPoint.x >= seat.x
                        && holdPoint.x < seat.x+seat.w
                        && holdPoint.y >= seat.y
                        && holdPoint.y < seat.y+seat.h) {
                            break;
                    }
                }
                seatGuest[s-1] = "";
            }

            win.renewPicBar();
        }
    }
};

win.pointUp = function(point) {
    if (hold === "") {
        return;
    }
    //放置在SEAT区域 
    var lastSeat = win.find("座位16");
    if (   point.x >= baseSeat.x 
        && point.x < lastSeat.x+lastSeat.w
        && point.y >= baseSeat.y
        && point.y < lastSeat.y+lastSeat.h){
            
            var seatNum = 0;//获取hold的图放置的新位置
            for (var i=1;i<=16;i++) {
                var seat = win.find("座位"+i);
                if (   point.x >= seat.x
                    && point.x < seat.x+seat.w
                    && point.y >= seat.y
                    && point.y < seat.y+seat.h) {
                        seatNum = i;
                        break;
                }
            }
            if (seatNum < 1) {
                console.log("dd_pointUp : seatNum ERROR");
                return;
            }else {
                console.log("dd_pointUp : pointUp at 座位"+i);
            }
            
            //放置在有图片的SEAT,旧图被插回第一位
            if (seatGuest[i-1] !== ""
                && seatGuest[i-1].indexOf("图片")===0) {
                win.waitForAnimation(0,seatGuest[i-1]);//播放旧图插回动画   
            }
            seatGuest[i-1] = hold;
            win.putPicInSeat(i);//新图放入seat
            
            console.log("dd_key="+win.find(hold).key);
            
            //播放拼图放对的特效
            if (win.find(hold).key == i) {
                console.log("dd_放对啦!!!!");
                picHolding = 1;
                win.playBlink(hold);
            }
     
    } else if (picHolding === 1 && hold !== "") {
        //放在了无意义的区域, 需要重置图片位置
        win.waitForAnimation(0,hold);
    } else {
        console.log("dd_pointUp : ELSE ??");
    }
    
    //检测是否游戏完成now
    var gameOver = win.gameOverCheck();
    if (gameOver == 1) {
        console.log ("dd_gameOver");
        win.find("计时器控件").pause();
        
        function overThisGame() {
            var initData = {};
            me.openWindow("游戏结束界面", function (retData) {
                console.log("window closed.");
                win.newGame();
                win.initGame();
            }, false, initData);
        }
        setTimeout(function() {overThisGame();}, 510);
    }
    
    picHolding = 0;
    hold = "";
    holdPoint = "";
};
pointDown中实现对按下图片的识别 并将有效控件的名称记录为hold,


pointMove中结合hold 判断玩家是否在移动一个图片到可移动区域,

如果图片被拖动出滑动选择区,则留在滑动选择去的picBar成语播放对其动画.


pointUp中结合上两种方法中收集的数据,判断正在被hold的图片是否需要播放动画.

动画主要包含,1:放置在无效区域时 图片返回屏幕最下方, 2:最下方的其他图片右移给新插入的图片腾出位置

3:picBar中被拖出的图片放入puzzle座位区域的动画效果 4: 放置在了有效的区域,判断是否放置正确并播放动画.

每一次opintUp会检测游戏是否结束.


4: 其他方法代码展示

setStroke 为图片添加边框效果

win.setStroke = function(picName,canvas) {
    console.log("dd_setStroke : name="+picName);
    if (picName == "图片0") {
        return;
    }
    var pic = win.find(picName);
    pic.view.canvas.strokeStyle = "#E3881F";
    pic.view.canvas.lineWidth = 1;
    pic.view.canvas.strokeRect(0, 0, this.w, this.h);
};


insertPic 将放置在无效区域的图片插入回屏幕最下方

win.insertPic = function(seat,hold) {
    if (seat > picBar.length 
        || (seat == picBar.length && picBar.length !== 0)) {
        console.log("dd_insertPic : seat  ERROR = "+ seat);
        return;
    }

    var targetPic = win.find(hold);
    var targetSeat;
    if (win.find(picBar[seat])) {
        targetSeat = win.find(picBar[seat]);
    }
    var backToSeat = {
            duration:200,
            xStart:targetPic.x,
            yStart:targetPic.y,
            xEnd:targetSeat.x,
            yEnd:targetSeat.y,
            interpolator:"l"
    };
   
    if (picBar.length === 0) {
        backToSeat.xEnd = 0;
        backToSeat.yEnd = win.h-basePic.h,
        picBar.push(hold);
    }
            
    targetPic.animate(backToSeat,function onDone(_obj) {
        setTimeout(function() {picHolding = 0;}, 60);
    });
};

drawOutPic 完成对拖出图片的移动显示

win.drawOutPic = function(point) {
    //console.log("dd_pointMove : "+point.x+" "+point.y);
    var x = point.x-basePic.w/2;
    var y = point.y-basePic.h/2;
    win.find(hold).setPosition(x,y);
};

swapeAnimation 完成播放滑动选择区的滑动效果
win.swipeAnimation = function(direction) {
    for (var i=1; i<=picBar.length ;i++) {
        var targetPic = win.find(picBar[i-1]);
        
        var swipe = {
            duration:500,
            xStart:targetPic.x,
            yStart:targetPic.y,
            xEnd:targetPic.x-3*targetPic.w,
            yEnd:targetPic.y,
            interpolator:"l"};
            
        if (direction == "left") {
            swipe.xEnd = targetPic.x-3*targetPic.w;
        
        }else if (direction == "right") {
            swipe.xEnd = targetPic.x+3*targetPic.w;
            
        }else if (direction == "shake") {
            swipe.xEnd = targetPic.x;
            swipe.interpolator = "l";
            
        }else {
            console.log("dd_swipeAnimation ERROR");
            return;
        }
        
        targetPic.animate(swipe,function onDone(_obj) {});
    }
};

moveOutOfRange 判断是否可以继续滑动 滑动区域

win.moveOutOfRange = function(direction) {
    if (direction == "left"
        && win.find(picBar[(picBar.length-1)]).x+basePic.w <= win.w) {
            //向左滑时,如队末图片已完整出现在视野内则不处理.
            return 1;
    } else if (direction == "right"
        && win.find(picBar[0]).x >= 0 ){
            //向右滑时,如队末图片已完整出现在视野内则不处理.
            return 1;
    }
    return 0;
};


picBarRight 负责将滑动区域的picBar成员集体向右移动
win.picBarRight = function(seat,hold) {
    var i = seat;
    if (picBar.length === 0) {
        console.log("dd_picBarRight : picBar is enpty!!!");
        return;
    }
    
    for (i; iwin.removeHoldFromPicBar = function() {
    var index = -1;
    for (var i=0;i= 0) {
        picBar.splice(index,1);
        console.log("dd_removeHoldFromPicBar: index is "+index);
    }else {
        console.log("dd_removeHoldFromPicBar: index ERROR");
    }
};


playBlink 负责播放当玩家拖动图片到正确位置时的提示动画
win.playBlink = function(target){
    if ( !win.find(target,true) ) {
        console.log("dd_playBlink : target notFound");
        return;
    }
    targetPic = win.find(target,true);
    var opRate = 0.3;
    var zoom = 0.8;

    var blinkP1 = {
            duration:60,
            opacityStart:1,
            opacityEnd:opRate,
            scaleXStart:1,
            scaleXEnd:zoom,
            scaleYStart:1,
            scaleYEnd:zoom,
        };
    var blinkP2 = {
        duration:60,
        opacityStart:opRate,
        opacityEnd:1,
        scaleXStart:zoom,
        scaleXEnd:1,
        scaleYStart:zoom,
        scaleYEnd:1,
    };

    targetPic.animate(blinkP1,function onDone(_obj) {
        targetPic.animate(blinkP2,function onDone(_obj) {
            blinkP1.duration = 140;
            blinkP2.duration = 140;
            targetPic.animate(blinkP1,function onDone(_obj) {
                targetPic.animate(blinkP2,function onDone(_obj) {
                });
            });
        });
    });
};

gameOverCheck 负责检测游戏是否结束

win.gameOverCheck = function() {
    var i = 0;
    var key;
    seatGuest.length = 16;
    if (DEBUG) {
        for (i in seatGuest){
            key = win.find(seatGuest[i]).key-1;
            console.log("dd_seatGuest["+i+"]="+seatGuest[i]+"key="+key);
        }
    }
    i = 0;
    for (i in seatGuest){
        key = win.find(seatGuest[i]).key-1;
        if (seatGuest[i] === ""
            || key == "undefined"
            || key != i){
            return 0;
        }
    }
    return 1;
};


timmerGameRule 负责在进入游戏后显示游戏开始的倒计时,玩家有3秒钟去记忆拼图完成时该有的样子.
win.timmerGameRule = function() {
    var timePic = [];
    var add = "http://osgames1.b0.upaiyun.com/games/diwy_source/businessSample/timeLimitPuzzle/pic/number";
    for (var i=0;i<=3;i++){
        timePic[i]= add+i+".png";
    }
    
    var time = 3;
    function timmer() {
        if (!win.children) {
            clearInterval(Interval);
            return;
        }
        win.timmerView(true);
        win.find("计时板").setImageSrc(timePic[time]);
        if (time < 0) {
            clearInterval(Interval);
            win.timmerView(false);
            win.find("图片1").setVisible(true);
	        win.initSeat();
            win.initPictrue();
            win.find("计时器控件").start();
        }
        time --;
    }
    
    var Interval = setInterval(timmer,1000);
};


这次的分享就到这里. 2015-07-13

你可能感兴趣的:(游戏制作记录)