这次使用www.tangide.com制作了一款推广类拼图游戏 ,
这游戏复杂点在各种拖动, 滑动的动画实现上. 下面我主要分享下动画部分的实现方法.
0 在线运行:
http://www.tangide.com/apprun.html?appid=previewupyunosgames1-241435729564104
1: 确认界面布局
首先是对游戏功能区域的设计和
划分
滑动选择区在屏幕最下方, 每一块小的拼图会出现在这里, 玩家可以左右滑动让整个滑动区域轮动以查看全部拼图块.
这里我放入了一张图片在屏幕最左下角,游戏开始时通过对这张图的自动复制, 随机排序, 插入图片等操作来
拼图放置区在计时器下方, 玩家需要吧小拼图从滑动选择区拖到这个区域的正确位置以完成游戏.
这里是4X4的游戏, 我放置了16张小图片用来确认16个格子的位置, 并让这16个格子以第一个格子为基准自动对齐.
这么做确认麻烦但由于我是首次开发此类型游戏,这样做利于调试.
可以看到滑动选择区和拼图放置区之间有一段空隙, 这主要是为不同分辨率的手机预留的.截图分辨率为480*800.
这样的布局可以在不同屏幕比的设备 例如ip4S,5S上都显示得体.
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: 图片拖动效果
游戏中对图片的选择,拖动, 放开 我使用指针按下, 移动, 松开事件来完成.
拖出动作的逻辑:
1: 判断指针按下时必须选中的是有效成员, 该有效成员将成为hold
2: 上一次指针按下以后又进行了指针移动(pointMove)且Y坐标移动到了一定范围以外, 此时将hold从picBar中删除
3: 对所选中的picBar成员使用setPositon来实现拖动他的效果.
4: 对仍然在屏幕底端的 且在发生移动时处于hold成员右侧的picBar成员播放向左移动的动画.
5: 根据指针抬起时所在的位置判断需要执行放下图片 还是重置图片位置.
图片放下的逻辑:
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);
};
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);
});
};
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);
};
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) {});
}
};
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;
};
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");
}
};
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;
};
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