今天是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);
欢迎高手指教。虽然不经常上博客。偶然上来看看也会回复的。