今天是2016年最后一天上班了。最近几天都比较休闲,有时间空闲下来写写文档之类的。

2016过得真是快。感觉没做什么就过去了。想到之前想坚持每个月写一写博客都没坚持到。希望2017年可以吧。

无聊之余,看到了之前写的一个拼图游戏。就发一发共享下。写了有1年了。有些地方写得不是很好。但也能用了。

先上图看效果:

完整的代码可以去 git下载:https://github.com/zhouxitian/puzzle.

这里主要说一下思路什么的。

1、写这个之前也去搜索过网上的一些类似的游戏。当感觉都没有自己想要的。主要是网上的插件有些兼容不太好,例如移动端没兼容等。其次就是游戏难度控制感觉不好,有时过于困难有时过于简单。

2、后来想想,还是自己动手写一个吧。这样比较符合自己想要的效果。

3、主要考虑的事情其实就是一个。怎么控制游戏难度。不会出现太容易活太难的情况,也不会出现完成不了的情况。

4、后来就想到了这个控制步数,来达到控制难度的方法。其实也很简单。就是难度为1。那么只要走1步就能完成游戏,难度为10。那么最少需要10步(难度控制在游戏总格数的一半比较好)。

5、知道这类游戏规律的人会觉得容易,但不会的人却总在兜圈。玩多了,自然会感觉容易些。

6、怎么控制步数来达到控制游戏难度这个效果呢。当时想的就是一个逆向思维。即 既然最后是要完成游戏,那么怎么 不一开始就当游戏是拼好的。然后根据难度随机走,尽量排除之前走过的,这样就能把游戏打乱。然后出来的结果能稳定的控制步数。

7、总结:有时想问题总钻牛角尖,还不如静下来。逆向思考一下问题,或许就会获得灵感。

为了方便,部分代码也发一发吧。完整的例子还是去git下:

  1 ;(function(window,undefined){
  2     myPuzzle=function(opt){
  3         var t=this;
  4         t.options={
  5             id:"game",
  6             pic:"images/p1.jpg",//图片
  7             x:4,//
  8             y:3,//
  9             hard:5,//最大难度最好不要大于总格数的一半
 10             duration:100,//毫秒
 11             startInit:null,//每次重新开始游戏时的回调(相当于初始化)
 12             stepStart:null,//每步开始移动时的回调
 13             finish:null,//游戏完成后的回调
 14             stepEnd:null//每走一步后的回调
 15         };
 16         t.extend(t.options,opt);
 17         t.container=t.getId(t.options.id);
 18         t.length=t.options.x*t.options.y;
 19         t.current={};//当前拖动的元素信息
 20         t.move=true;//当前的移动对象是否li
 21         t.isMove=false;//是否能移动
 22         t.init();
 23     }
 24     myPuzzle.prototype={
 25         init:function(){
 26             var t=this;
 27             t.createGrid();//排序
 28             t.touch=new myTouch({//滑屏
 29                 wrapper:"#"+t.wid,
 30                 start:function(e){
 31                     e=e||window.event;
 32                     t.touchChild=e.target||e.srcElement;
 33                     t.move=true;
 34                     if(t.touchChild.nodeName.toLowerCase()!="li"){
 35                         t.move=false;
 36                     }
 37                     t.checkMove=false;//是否需要检查 是否能移动
 38                     t.isMove=false;//是否能移动
 39                     if(t.move){
 40                         t.touchChild.style.transitionDuration="0ms";
 41                         t.current.index=parseInt(t.touchChild.getAttribute("index"));
 42                         t.current.x=t.position[t.current.index].x*t.cWidth;
 43                         t.current.y=t.position[t.current.index].y*t.cHeight;
 44                         t.current._x=t.position[t.current.index]._x*t.cWidth;
 45                         t.current._y=t.position[t.current.index]._y*t.cHeight;
 46                     }
 47                 }
 48             });
 49             t.start();
 50             t.touch.moveX=t.touch.moveY=function(){
 51                 if(t.move){
 52                     var _t=this,MoveXY=0;
 53                     if(!t.checkMove){//判断移动方向
 54                         if(_t.x){
 55                             if(_t.changeX<0){
 56                                 t.direction="left";
 57                             }else if(_t.changeX>0){
 58                                 t.direction="right";
 59                             }
 60                             MoveXY=_t.changeX;
 61                         }else if(_t.y){
 62                             if(_t.changeY<0){
 63                                 t.direction="up";
 64                             }else if(_t.changeY>0){
 65                                 t.direction="down";
 66                             }
 67                             MoveXY=_t.changeY;
 68                         }
 69                         t.isMove=t.canMove(t.position[t.current.index]._index,t.direction);
 70                         if(t.isMove){//能移动才触发 每步的开始回调
 71                             t.options.stepStart&&t.options.stepStart.call(t);
 72                         }
 73                         //console.log(t.position[t.index]);
 74                         //console.log(t.getRadomPosition(t.position[t.index]._index))
 75                         t.checkMove=true;
 76                     }else{//限制移动范围
 77                         if(_t.x){
 78                             if(t.direction=="left"){
 79                                 MoveXY=_t.changeX<-t.cWidth?-t.cWidth:(_t.changeX>0?0:_t.changeX);
 80                             }else{
 81                                 MoveXY=_t.changeX>t.cWidth?t.cWidth:(_t.changeX<0?0:_t.changeX);
 82                             }
 83                         }else if(_t.y){
 84                             if(t.direction=="up"){
 85                                 MoveXY=_t.changeY<-t.cHeight?-t.cHeight:(_t.changeY>0?0:_t.changeY);
 86                             }else{
 87                                 MoveXY=_t.changeY>t.cHeight?t.cHeight:(_t.changeY<0?0:_t.changeY);
 88                             }
 89                         }
 90                     }
 91                     if(t.isMove){
 92                         if(_t.x){
 93                             t.touchChild.style.cssText='transition-duration:0ms;-webkit-transition-duration:0ms;z-index:2;left:'+(t.current._x+MoveXY)+'px;top:'+t.current._y+'px;background-position: -'+t.current.x+'px -'+t.current.y+'px;';
 94                         }else if(_t.y){
 95                             t.touchChild.style.cssText='transition-duration:0ms;-webkit-transition-duration:0ms;z-index:2;left:'+t.current._x+'px;top:'+(t.current._y+MoveXY)+'px;background-position: -'+t.current.x+'px -'+t.current.y+'px;';
 96                         }
 97                     }    
 98                 }
 99             }
100             t.touch.endX=t.touch.endY=function(){
101                 if(t.move&&t.isMove){
102                     var _t=this,xy,_x=t.position[t.current.index]._x,_y=t.position[t.current.index]._y,_index=t.position[t.current.index]._index;
103                     if(_t.x){
104                         xy=t.position[t.index]._x*t.cWidth;
105                         t.touchChild.style.cssText='transition-duration:'+t.options.duration+'ms;-webkit-transition-duration:'+t.options.duration+'ms;z-index:0;left:'+xy+'px;top:'+t.current._y+'px;background-position: -'+t.current.x+'px -'+t.current.y+'px;';
106                     }else if(_t.y){    
107                         xy=t.position[t.index]._y*t.cHeight;
108                         t.touchChild.style.cssText='transition-duration:'+t.options.duration+'ms;-webkit-transition-duration:'+t.options.duration+'ms;z-index:0;left:'+t.current._x+'px;top:'+xy+'px;background-position: -'+t.current.x+'px -'+t.current.y+'px;';
109                     }
110                     //交换坐标
111                     t.position[t.current.index]._x=t.position[t.index]._x;
112                     t.position[t.current.index]._y=t.position[t.index]._y;
113                     t.position[t.current.index]._index=t.position[t.index]._index;
114                     t.position[t.index]._x=_x;
115                     t.position[t.index]._y=_y;
116                     t.position[t.index]._index=_index;
117                     var win=true;
118                     for(var i=t.position.length;i--;){
119                         if(t.position[i].index!=t.position[i]._index){
120                             win=false;
121                             break;
122                         }
123                     }
124                     t.touch.moveing=true;
125                     t.step++;
126                     if(win){
127                         setTimeout(function(){
128                             t.removeClass(t.visible,"hidden");
129                             t.options.finish&&t.options.finish.call(t);
130                         },t.options.duration);
131                     }else{
132                         setTimeout(function(){
133                             t.touch.moveing=false;
134                             t.options.stepEnd&&t.options.stepEnd.call(t);
135                         },t.options.duration);    
136                     }
137                 }
138             }
139         },
140         start:function(){
141             var t=this;
142             t.position=new Array();//记录每个移动元素的坐标
143             t.positionM=new Array();//记录移动后的元素
144             t.step=0;//记录移动的步数
145             if(t.visible){
146                 t.removeClass(t.visible,"hidden");
147             }
148             for(var i=0;i<t.length;i++){
149                 var x=i%t.options.x;
150                 var y=Math.floor(i/t.options.x);
151                 t.position.push({index:i,x:x,y:y,_index:i,_x:x,_y:y});
152                 t.positionM.push(i);
153                 x=x*t.cWidth;
154                 y=y*t.cHeight;
155                 t.wrapper.children[i].style.cssText='left:'+x+'px;top:'+y+'px;transition-duration:0ms;-webkit-transition-duration:0ms;background-position: -'+x+'px -'+y+'px;';
156             }
157             t.setGrid();//抽空一格
158             t.upSet();//打乱
159             t.touch.moveing=false;
160             t.options.startInit&&t.options.startInit.call(t);
161         },
162         refresh:function(){
163             var t=this;
164             t.width=parseInt(t.getStyles(t.container,"width"));
165             t.height=parseInt(t.getStyles(t.container,"height"));
166             t.cWidth=t.width/t.options.x;
167             t.cHeight=t.height/t.options.y;
168             for(var i=t.length;i--;){
169                 x=t.position[i].x*t.cWidth;//原始x坐标
170                 y=t.position[i].y*t.cHeight;//原始y坐标
171                 _x=t.position[i]._x*t.cWidth;//移动x坐标
172                 _y=t.position[i]._y*t.cHeight;//移动y坐标
173                 t.wrapper.children[i].style.cssText='left:'+_x+'px;top:'+_y+'px;background-position:-'+x+'px -'+y+'px;';
174             }
175         },
176         createGrid:function(){//排序
177             var t=this;
178             t.width=parseInt(t.getStyles(t.container,"width"));
179             t.height=parseInt(t.getStyles(t.container,"height"));
180             t.cWidth=t.width/t.options.x;
181             t.cHeight=t.height/t.options.y;
182             var position=t.getStyles(t.container,"position");
183             if(position=="static"){
184                 t.container.style.position="relative";
185             }
186             var ul=document.createElement('ul');
187             var data=new Date().getTime();
188             t.wid="myPuzzle_"+data;
189             ul.setAttribute("id",t.wid);
190             var style={};
191             style['#'+t.wid]='position:absolute;width:100%;height:100%;left:0;top:0;user-select:none;-webkit-user-select:none;';
192             style['#'+t.wid+' li']='transition-property:left,top,opacity;transition-timing-function:linear;-webkit-transition-property:left,top,opacity;-webkit-transition-timing-function:linear;width:'+(100/t.options.x)+'%;height:'+(100/t.options.y)+'%;position:absolute;background-image:url('+t.options.pic+');background-repeat:no-repeat;background-size:'+t.options.x+'00% '+t.options.y+'00%;';
193             style['#'+t.wid+' li.hidden']='visibility:hidden;opacity:0;';
194             //style['#'+t.wid+' li.hidden']='opacity:0.5;';
195             t.setCss(t.container,style);
196             var html='';
197             for(var i=0;i<t.length;i++){
198                 var x=i%t.options.x;
199                 var y=Math.floor(i/t.options.x);
200                 //t.position.push({index:i,x:x,y:y,_index:i,_x:x,_y:y});
201                 //t.positionM.push(i);
202                 x=x*t.cWidth;
203                 y=y*t.cHeight;
204                 html+='<li index='+i+'>'+i+'</li>';
205             }
206             ul.innerHTML=html;
207             t.container.appendChild(ul);
208             t.wrapper=t.getId(t.wid);
209         },
210         setGrid:function(){//随机空一格
211             var t=this;
212             var random=Math.round(Math.random()*(t.length-1));
213             t.visible=t.wrapper.children[random];
214             t.addClass(t.visible,"hidden");
215             t.index=random;
216         },
217         upSet:function(){//根据难度打乱排序
218             var t=this;
219             t.answer=new Array();//记录走法
220                 var answer=new Array();//记录移动方向
221                 for(var i=t.options.hard;i--;){
222                     var moveObj=t.getRadomPosition(t.position[t.index]._index);//记录能移动的元素(下标,方向);
223                     var random=Math.ceil(Math.random()*moveObj.length)-1;//在能移动的元素里随机一个移动
224                     ;(function(){//检查随机走向,修正使其不走重复的路径
225                         var length=answer.length;
226                         if(length>0){
227                             length--;
228                             //console.log("random:"+random);
229                             if((moveObj[random].direction=="up"&&answer[length]=="down")||(moveObj[random].direction=="down"&&answer[length]=="up")||(moveObj[random].direction=="left"&&answer[length]=="right")||(moveObj[random].direction=="right"&&answer[length]=="left")){
230                                 //console.log("相邻方向相反:"+moveObj[random].direction,answer[length],random);
231                                 if(random<moveObj.length-1){
232                                     random++;
233                                 }else{
234                                     random=0;
235                                 }
236                                 //console.log("相邻方向相反:"+moveObj[random].direction,answer[length],random);
237                                 arguments.callee();
238                             }else if(moveObj.length>2&&length>1&&((moveObj[random].direction=="up"&&answer[length-1]=="down")||(moveObj[random].direction=="down"&&answer[length-1]=="up")||(moveObj[random].direction=="left"&&answer[length-1]=="right")||(moveObj[random].direction=="right"&&answer[length-1]=="left"))){
239                                 //console.log(moveObj,answer)
240                                 for(var i=moveObj.length;i--;){
241                                     if(i!=random//不取当前的
242                                     &&!((moveObj[i].direction=="up"&&answer[length-1]=="down")||(moveObj[i].direction=="down"&&answer[length-1]=="up")||(moveObj[i].direction=="left"&&answer[length-1]=="right")||(moveObj[i].direction=="right"&&answer[length-1]=="left"))//随机方向对比上一个方向
243                                     &&!((moveObj[i].direction=="up"&&answer[length]=="down")||(moveObj[i].direction=="down"&&answer[length]=="up")||(moveObj[i].direction=="left"&&answer[length]=="right")||(moveObj[i].direction=="right"&&answer[length]=="left"))){//随机方向对比前一个方向
244                                         random=i;
245                                         //console.log("相隔方向相反:"+moveObj[random].direction,random);
246                                         break;
247                                     }
248                                 }
249                             }
250                         }
251                     })();    
252                     t.touchChild=t.container.querySelectorAll("li")[moveObj[random].index];
253                     //console.log(t.position[t.index].index+"换"+moveObj[random].index);
254                     t.answer.push(moveObj[random].index);
255                     answer.push(moveObj[random].direction);
256                     t.current.x=t.position[moveObj[random].index].x*t.cWidth;
257                     t.current.y=t.position[moveObj[random].index].y*t.cHeight;
258                     t.current._x=t.position[moveObj[random].index]._x*t.cWidth;
259                     t.current._y=t.position[moveObj[random].index]._y*t.cHeight;
260                     
261                     var xy,
262                     _x=t.position[moveObj[random].index]._x,//临时保存需交换的坐标
263                     _y=t.position[moveObj[random].index]._y,
264                     _index=t.position[moveObj[random].index]._index;
265                     
266                     if(moveObj[random].direction=="right"||moveObj[random].direction=="left"){
267                         xy=t.position[t.index]._x*t.cWidth;
268                         t.touchChild.style.cssText='transition-duration:0ms;-webkit-transition-duration:0ms;z-index:0;left:'+xy+'px;top:'+t.current._y+'px;background-position: -'+t.current.x+'px -'+t.current.y+'px;';
269                     }else{    
270                         xy=t.position[t.index]._y*t.cHeight;
271                         t.touchChild.style.cssText='transition-duration:0ms;-webkit-transition-duration:0ms;z-index:0;left:'+t.current._x+'px;top:'+xy+'px;background-position: -'+t.current.x+'px -'+t.current.y+'px;';
272                     }
273                     //交换移动后的坐标
274                     var aa=t.positionM[t.position[t.index]._index];
275                     //console.log("空"+t.positionM[t.position[t.index]._index]+"换"+_index+","+_index+"换"+aa);
276                     t.positionM[t.position[t.index]._index]=t.positionM[_index];
277                     t.positionM[_index]=aa;    
278                     //交换坐标
279                     t.position[moveObj[random].index]._x=t.position[t.index]._x;
280                     t.position[moveObj[random].index]._y=t.position[t.index]._y;
281                     t.position[moveObj[random].index]._index=t.position[t.index]._index;
282                     t.position[t.index]._x=_x;
283                     t.position[t.index]._y=_y;
284                     t.position[t.index]._index=_index;
285                 }
286                 t.answer.reverse();//=t.reverse(t.answer,t.answer.length-1,"");
287         },
288         //递归反转数组(不改变原数组),arr数组;length数组长度;str:""(空字符串)
289         reverse:function(arr,length,str){
290             var t=this;
291             return length==0?str+arr[0]:(t.reverse(arr,length-1,str+arr[length]+"=>"));
292         },
293         /**返回所有能移动的坐标信息
294         ***index:当前隐藏元素的下标
295         **/
296         getRadomPosition:function(index){
297             var t=this;
298             var arry=new Array();
299             if(t.position[index].y>0){
300                 arry.push({index:t.positionM[index-t.options.x],direction:"down"});
301             }
302             if(t.position[index].x>0){
303                 arry.push({index:t.positionM[index-1],direction:"right"});
304             }
305             if(t.position[index].x<t.options.x-1){
306                 arry.push({index:t.positionM[index+1],direction:"left"});
307             }
308             if(t.position[index].y<t.options.y-1){
309                 arry.push({index:t.positionM[index+t.options.x],direction:"up"});
310             }
311             return arry;
312         },
313         /**判断是否能移动 
314         ***index:移动对象的下标 
315         ***direction移动方向 
316         **/
317         canMove:function(index,direction){
318             var t=this,_index=t.position[t.index]._index;
319             if((direction=="right"&&index==_index-1)||(direction=="left"&&index==_index+1)||(direction=="down"&&index==_index-t.options.x)||(direction=="up"&&index==_index+t.options.x)){
320                 return true;
321             }
322             return false;
323         },
324         extend:function(target,source){//拷贝不引用,改变拷贝的数组不会改变原数组
325             var t=this;
326             for (var p in source){
327                 if(t.getType(source[p])=="array"||t.getType(source[p])=="object"){
328                     target[p]=t.getType(source[p])=="array"?[]:{};
329                     arguments.callee(target[p],source[p]);
330                 }else{
331                     target[p] = source[p];
332                 }
333             }
334             return target;
335         },
336         getType:function(o)
337         {
338             var _t;
339             return ((_t = typeof(o)) == "object" ? o==null && "null" || Object.prototype.toString.call(o).slice(8,-1):_t).toLowerCase();
340         },
341         getId:function(elemId){return document.getElementById(elemId);},
342         getStyles:function(obj,name){
343             if(window.getComputedStyle){
344                 var getStyles;
345                 if ( obj.ownerDocument.defaultView.opener ) {
346                     var computed =obj.ownerDocument.defaultView.getComputedStyle( obj, null );
347                     getStyles= computed.getPropertyValue(name)||computed[ name];
348                 }else{
349                     var computed =window.getComputedStyle( obj, null);
350                     getStyles= computed.getPropertyValue(name)||computed[ name ];
351                 }
352             }else{
353                 getStyles=obj.currentStyle[name];
354             }
355             if(name=="width"){
356                 var maxWidth=arguments.callee(obj,"max-width");
357                 var pmaxWidth=parseFloat(maxWidth)||0;
358                 var pgetStyles=parseFloat(getStyles)||0;
359                 if(pmaxWidth&&(pgetStyles>pmaxWidth||!pgetStyles)){
360                     getStyles=maxWidth;
361                 }
362             }else if(name=="height"){
363                 var maxHeight=arguments.callee(obj,"max-height");
364                 var pmaxHeight=parseFloat(maxHeight)||0;
365                 var pgetStyles=parseFloat(getStyles)||0;
366                 if(pmaxHeight&&(pgetStyles>pmaxHeight||!pgetStyles)){
367                     getStyles=maxHeight;
368                 }
369             }
370             return getStyles;
371         },
372         setCss:function(obj,styleObj){
373             var cssCode = '';
374             if(document.createStyleSheet)//兼容ie8不能动态加载css
375             {  
376                 var sheet = document.createStyleSheet();
377                 for (var c in styleObj){
378                     this.insertCssRule(sheet,c,styleObj[c]);
379                 }
380             }else{
381                 for (var c in styleObj){
382                     cssCode+=c+'{'+styleObj[c]+'}';
383                 }
384                 var styleElement = document.createElement('STYLE');
385                 styleElement.type = 'text/css';
386                 var innerHTML = document.createTextNode(cssCode);
387                 styleElement.appendChild(innerHTML);
388                 if(obj.hasChildNodes()){
389                     obj.insertBefore(styleElement,obj.children[0]);
390                 }else if(obj){
391                     obj.appendChild(styleElement);
392                 }else{
393                     document.head.appendChild(styleElement);
394                 }
395             }    
396         },
397         insertCssRule:function(sheet,selectorText,cssText, position) {
398             position=position||0;
399             if (sheet.insertRule) {
400                 sheet.insertRule(selectorText + "{" + cssText + "}", position);
401             } else if (sheet.addRule) {
402                 sheet.addRule(selectorText, cssText, position);
403             }
404         },
405         addClass:function(o,cn){var re = new RegExp("(\\s*|^)"+cn+"\\b","g");o.className +=o.className?(re.test(o.className)?"":" "+ cn):cn;},
406         removeClass:function(o,cn){var re = new RegExp("(\\s*|^)"+cn+"\\b","g");var sName = o.className;o.className = sName.replace(re,"");}
407     }
408 })(window);

 欢迎高手指教。虽然不经常上博客。偶然上来看看也会回复的。

 posted on 2016-12-30 16:21  XiTian  阅读(1400)  评论(0编辑  收藏  举报