最近几天都比较休闲,有时间空闲下来写写文档之类的。
过得真是快。感觉没做什么就过去了。想到之前想坚持每个月写一写博客都没坚持到。希望后面可以吧。
无聊之余,看到了之前写的一个拼图游戏。就发一发共享下。写了有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);
欢迎高手指教。虽然不经常上博客。偶然上来看看也会回复的。