今天是2016年最后一天上班了。最近几天都比较休闲,有时间空闲下来写写文档之类的。
2016过得真是快。感觉没做什么就过去了。想到之前想坚持每个月写一写博客都没坚持到。希望2017年可以吧。
无聊之余,看到了之前写的一个拼图游戏。就发一发共享下。写了有1年了。有些地方写得不是很好。但也能用了。
先上图看效果:
完整的代码可以去 git下载:https://github.com/zhouxitian/puzzle.
这里主要说一下思路什么的。
1、写这个之前也去搜索过网上的一些类似的游戏。当感觉都没有自己想要的。主要是网上的插件有些兼容不太好,例如移动端没兼容等。其次就是游戏难度控制感觉不好,有时过于困难有时过于简单。
2、后来想想,还是自己动手写一个吧。这样比较符合自己想要的效果。
3、主要考虑的事情其实就是一个。怎么控制游戏难度。不会出现太容易活太难的情况,也不会出现完成不了的情况。
4、后来就想到了这个控制步数,来达到控制难度的方法。其实也很简单。就是难度为1。那么只要走1步就能完成游戏,难度为10。那么最少需要10步(难度控制在游戏总格数的一半比较好)。
5、知道这类游戏规律的人会觉得容易,但不会的人却总在兜圈。玩多了,自然会感觉容易些。
6、怎么控制步数来达到控制游戏难度这个效果呢。当时想的就是一个逆向思维。即 既然最后是要完成游戏,那么怎么 不一开始就当游戏是拼好的。然后根据难度随机走,尽量排除之前走过的,这样就能把游戏打乱。然后出来的结果能稳定的控制步数。
7、总结:有时想问题总钻牛角尖,还不如静下来。逆向思考一下问题,或许就会获得灵感。
为了方便,部分代码也发一发吧。完整的例子还是去git下:
;(function(window,undefined){
myPuzzle=function(opt){
var t=this;
t.options={
id:"game",
pic:"images/p1.jpg",//图片
x:4,//列
y:3,//行
hard:5,//最大难度最好不要大于总格数的一半
duration:100,//毫秒
startInit:null,//每次重新开始游戏时的回调(相当于初始化)
stepStart:null,//每步开始移动时的回调
finish:null,//游戏完成后的回调
stepEnd:null//每走一步后的回调
};
t.extend(t.options,opt);
t.container=t.getId(t.options.id);
t.length=t.options.x*t.options.y;
t.current={};//当前拖动的元素信息
t.move=true;//当前的移动对象是否li
t.isMove=false;//是否能移动
t.init();
}
myPuzzle.prototype={
init:function(){
var t=this;
t.createGrid();//排序
t.touch=new myTouch({//滑屏
wrapper:"#"+t.wid,
start:function(e){
e=e||window.event;
t.touchChild=e.target||e.srcElement;
t.move=true;
if(t.touchChild.nodeName.toLowerCase()!="li"){
t.move=false;
}
t.checkMove=false;//是否需要检查 是否能移动
t.isMove=false;//是否能移动
if(t.move){
t.touchChild.style.transitionDuration="0ms";
t.current.index=parseInt(t.touchChild.getAttribute("index"));
t.current.x=t.position[t.current.index].x*t.cWidth;
t.current.y=t.position[t.current.index].y*t.cHeight;
t.current._x=t.position[t.current.index]._x*t.cWidth;
t.current._y=t.position[t.current.index]._y*t.cHeight;
}
}
});
t.start();
t.touch.moveX=t.touch.moveY=function(){
if(t.move){
var _t=this,MoveXY=0;
if(!t.checkMove){//判断移动方向
if(_t.x){
if(_t.changeX<0){
t.direction="left";
}else if(_t.changeX>0){
t.direction="right";
}
MoveXY=_t.changeX;
}else if(_t.y){
if(_t.changeY<0){
t.direction="up";
}else if(_t.changeY>0){
t.direction="down";
}
MoveXY=_t.changeY;
}
t.isMove=t.canMove(t.position[t.current.index]._index,t.direction);
if(t.isMove){//能移动才触发 每步的开始回调
t.options.stepStart&&t.options.stepStart.call(t);
}
//console.log(t.position[t.index]);
//console.log(t.getRadomPosition(t.position[t.index]._index))
t.checkMove=true;
}else{//限制移动范围
if(_t.x){
if(t.direction=="left"){
MoveXY=_t.changeX<-t.cWidth?-t.cWidth:(_t.changeX>0?0:_t.changeX);
}else{
MoveXY=_t.changeX>t.cWidth?t.cWidth:(_t.changeX<0?0:_t.changeX);
}
}else if(_t.y){
if(t.direction=="up"){
MoveXY=_t.changeY<-t.cHeight?-t.cHeight:(_t.changeY>0?0:_t.changeY);
}else{
MoveXY=_t.changeY>t.cHeight?t.cHeight:(_t.changeY<0?0:_t.changeY);
}
}
}
if(t.isMove){
if(_t.x){
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;';
}else if(_t.y){
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;';
}
}
}
}
t.touch.endX=t.touch.endY=function(){
if(t.move&&t.isMove){
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;
if(_t.x){
xy=t.position[t.index]._x*t.cWidth;
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;';
}else if(_t.y){
xy=t.position[t.index]._y*t.cHeight;
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;';
}
//交换坐标
t.position[t.current.index]._x=t.position[t.index]._x;
t.position[t.current.index]._y=t.position[t.index]._y;
t.position[t.current.index]._index=t.position[t.index]._index;
t.position[t.index]._x=_x;
t.position[t.index]._y=_y;
t.position[t.index]._index=_index;
var win=true;
for(var i=t.position.length;i--;){
if(t.position[i].index!=t.position[i]._index){
win=false;
break;
}
}
t.touch.moveing=true;
t.step++;
if(win){
setTimeout(function(){
t.removeClass(t.visible,"hidden");
t.options.finish&&t.options.finish.call(t);
},t.options.duration);
}else{
setTimeout(function(){
t.touch.moveing=false;
t.options.stepEnd&&t.options.stepEnd.call(t);
},t.options.duration);
}
}
}
},
start:function(){
var t=this;
t.position=new Array();//记录每个移动元素的坐标
t.positionM=new Array();//记录移动后的元素
t.step=0;//记录移动的步数
if(t.visible){
t.removeClass(t.visible,"hidden");
}
for(var i=0;i<t.length;i++){
var x=i%t.options.x;
var y=Math.floor(i/t.options.x);
t.position.push({index:i,x:x,y:y,_index:i,_x:x,_y:y});
t.positionM.push(i);
x=x*t.cWidth;
y=y*t.cHeight;
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;';
}
t.setGrid();//抽空一格
t.upSet();//打乱
t.touch.moveing=false;
t.options.startInit&&t.options.startInit.call(t);
},
refresh:function(){
var t=this;
t.width=parseInt(t.getStyles(t.container,"width"));
t.height=parseInt(t.getStyles(t.container,"height"));
t.cWidth=t.width/t.options.x;
t.cHeight=t.height/t.options.y;
for(var i=t.length;i--;){
x=t.position[i].x*t.cWidth;//原始x坐标
y=t.position[i].y*t.cHeight;//原始y坐标
_x=t.position[i]._x*t.cWidth;//移动x坐标
_y=t.position[i]._y*t.cHeight;//移动y坐标
t.wrapper.children[i].style.cssText='left:'+_x+'px;top:'+_y+'px;background-position:-'+x+'px -'+y+'px;';
}
},
createGrid:function(){//排序
var t=this;
t.width=parseInt(t.getStyles(t.container,"width"));
t.height=parseInt(t.getStyles(t.container,"height"));
t.cWidth=t.width/t.options.x;
t.cHeight=t.height/t.options.y;
var position=t.getStyles(t.container,"position");
if(position=="static"){
t.container.style.position="relative";
}
var ul=document.createElement('ul');
var data=new Date().getTime();
t.wid="myPuzzle_"+data;
ul.setAttribute("id",t.wid);
var style={};
style['#'+t.wid]='position:absolute;width:100%;height:100%;left:0;top:0;user-select:none;-webkit-user-select:none;';
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%;';
style['#'+t.wid+' li.hidden']='visibility:hidden;opacity:0;';
//style['#'+t.wid+' li.hidden']='opacity:0.5;';
t.setCss(t.container,style);
var html='';
for(var i=0;i<t.length;i++){
var x=i%t.options.x;
var y=Math.floor(i/t.options.x);
//t.position.push({index:i,x:x,y:y,_index:i,_x:x,_y:y});
//t.positionM.push(i);
x=x*t.cWidth;
y=y*t.cHeight;
html+='<li index='+i+'>'+i+'</li>';
}
ul.innerHTML=html;
t.container.appendChild(ul);
t.wrapper=t.getId(t.wid);
},
setGrid:function(){//随机空一格
var t=this;
var random=Math.round(Math.random()*(t.length-1));
t.visible=t.wrapper.children[random];
t.addClass(t.visible,"hidden");
t.index=random;
},
upSet:function(){//根据难度打乱排序
var t=this;
t.answer=new Array();//记录走法
var answer=new Array();//记录移动方向
for(var i=t.options.hard;i--;){
var moveObj=t.getRadomPosition(t.position[t.index]._index);//记录能移动的元素(下标,方向);
var random=Math.ceil(Math.random()*moveObj.length)-1;//在能移动的元素里随机一个移动
;(function(){//检查随机走向,修正使其不走重复的路径
var length=answer.length;
if(length>0){
length--;
//console.log("random:"+random);
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")){
//console.log("相邻方向相反:"+moveObj[random].direction,answer[length],random);
if(random<moveObj.length-1){
random++;
}else{
random=0;
}
//console.log("相邻方向相反:"+moveObj[random].direction,answer[length],random);
arguments.callee();
}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"))){
//console.log(moveObj,answer)
for(var i=moveObj.length;i--;){
if(i!=random//不取当前的
&&!((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"))//随机方向对比上一个方向
&&!((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"))){//随机方向对比前一个方向
random=i;
//console.log("相隔方向相反:"+moveObj[random].direction,random);
break;
}
}
}
}
})();
t.touchChild=t.container.querySelectorAll("li")[moveObj[random].index];
//console.log(t.position[t.index].index+"换"+moveObj[random].index);
t.answer.push(moveObj[random].index);
answer.push(moveObj[random].direction);
t.current.x=t.position[moveObj[random].index].x*t.cWidth;
t.current.y=t.position[moveObj[random].index].y*t.cHeight;
t.current._x=t.position[moveObj[random].index]._x*t.cWidth;
t.current._y=t.position[moveObj[random].index]._y*t.cHeight; var xy,
_x=t.position[moveObj[random].index]._x,//临时保存需交换的坐标
_y=t.position[moveObj[random].index]._y,
_index=t.position[moveObj[random].index]._index; if(moveObj[random].direction=="right"||moveObj[random].direction=="left"){
xy=t.position[t.index]._x*t.cWidth;
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;';
}else{
xy=t.position[t.index]._y*t.cHeight;
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;';
}
//交换移动后的坐标
var aa=t.positionM[t.position[t.index]._index];
//console.log("空"+t.positionM[t.position[t.index]._index]+"换"+_index+","+_index+"换"+aa);
t.positionM[t.position[t.index]._index]=t.positionM[_index];
t.positionM[_index]=aa;
//交换坐标
t.position[moveObj[random].index]._x=t.position[t.index]._x;
t.position[moveObj[random].index]._y=t.position[t.index]._y;
t.position[moveObj[random].index]._index=t.position[t.index]._index;
t.position[t.index]._x=_x;
t.position[t.index]._y=_y;
t.position[t.index]._index=_index;
}
t.answer.reverse();//=t.reverse(t.answer,t.answer.length-1,"");
},
//递归反转数组(不改变原数组),arr数组;length数组长度;str:""(空字符串)
reverse:function(arr,length,str){
var t=this;
return length==0?str+arr[0]:(t.reverse(arr,length-1,str+arr[length]+"=>"));
},
/**返回所有能移动的坐标信息
***index:当前隐藏元素的下标
**/
getRadomPosition:function(index){
var t=this;
var arry=new Array();
if(t.position[index].y>0){
arry.push({index:t.positionM[index-t.options.x],direction:"down"});
}
if(t.position[index].x>0){
arry.push({index:t.positionM[index-1],direction:"right"});
}
if(t.position[index].x<t.options.x-1){
arry.push({index:t.positionM[index+1],direction:"left"});
}
if(t.position[index].y<t.options.y-1){
arry.push({index:t.positionM[index+t.options.x],direction:"up"});
}
return arry;
},
/**判断是否能移动
***index:移动对象的下标
***direction移动方向
**/
canMove:function(index,direction){
var t=this,_index=t.position[t.index]._index;
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)){
return true;
}
return false;
},
extend:function(target,source){//拷贝不引用,改变拷贝的数组不会改变原数组
var t=this;
for (var p in source){
if(t.getType(source[p])=="array"||t.getType(source[p])=="object"){
target[p]=t.getType(source[p])=="array"?[]:{};
arguments.callee(target[p],source[p]);
}else{
target[p] = source[p];
}
}
return target;
},
getType:function(o)
{
var _t;
return ((_t = typeof(o)) == "object" ? o==null && "null" || Object.prototype.toString.call(o).slice(8,-1):_t).toLowerCase();
},
getId:function(elemId){return document.getElementById(elemId);},
getStyles:function(obj,name){
if(window.getComputedStyle){
var getStyles;
if ( obj.ownerDocument.defaultView.opener ) {
var computed =obj.ownerDocument.defaultView.getComputedStyle( obj, null );
getStyles= computed.getPropertyValue(name)||computed[ name];
}else{
var computed =window.getComputedStyle( obj, null);
getStyles= computed.getPropertyValue(name)||computed[ name ];
}
}else{
getStyles=obj.currentStyle[name];
}
if(name=="width"){
var maxWidth=arguments.callee(obj,"max-width");
var pmaxWidth=parseFloat(maxWidth)||0;
var pgetStyles=parseFloat(getStyles)||0;
if(pmaxWidth&&(pgetStyles>pmaxWidth||!pgetStyles)){
getStyles=maxWidth;
}
}else if(name=="height"){
var maxHeight=arguments.callee(obj,"max-height");
var pmaxHeight=parseFloat(maxHeight)||0;
var pgetStyles=parseFloat(getStyles)||0;
if(pmaxHeight&&(pgetStyles>pmaxHeight||!pgetStyles)){
getStyles=maxHeight;
}
}
return getStyles;
},
setCss:function(obj,styleObj){
var cssCode = '';
if(document.createStyleSheet)//兼容ie8不能动态加载css
{
var sheet = document.createStyleSheet();
for (var c in styleObj){
this.insertCssRule(sheet,c,styleObj[c]);
}
}else{
for (var c in styleObj){
cssCode+=c+'{'+styleObj[c]+'}';
}
var styleElement = document.createElement('STYLE');
styleElement.type = 'text/css';
var innerHTML = document.createTextNode(cssCode);
styleElement.appendChild(innerHTML);
if(obj.hasChildNodes()){
obj.insertBefore(styleElement,obj.children[0]);
}else if(obj){
obj.appendChild(styleElement);
}else{
document.head.appendChild(styleElement);
}
}
},
insertCssRule:function(sheet,selectorText,cssText, position) {
position=position||0;
if (sheet.insertRule) {
sheet.insertRule(selectorText + "{" + cssText + "}", position);
} else if (sheet.addRule) {
sheet.addRule(selectorText, cssText, position);
}
},
addClass:function(o,cn){var re = new RegExp("(\\s*|^)"+cn+"\\b","g");o.className +=o.className?(re.test(o.className)?"":" "+ cn):cn;},
removeClass:function(o,cn){var re = new RegExp("(\\s*|^)"+cn+"\\b","g");var sName = o.className;o.className = sName.replace(re,"");}
}
})(window);
欢迎高手指教。虽然不经常上博客。偶然上来看看也会回复的。