今天是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);

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

JavaScript拼图游戏的更多相关文章

  1. 用 JavaScript 实现简单拼图游戏

    本篇主要讲解,如何利用原生的 JavaScript 来实现一个简单的拼图小游戏. 线上体验地址:拼图 一.游戏的基础逻辑 想用一门语言来开发游戏,必须先了解如何使用这门语言来实现一些基础逻辑,比如图像 ...

  2. JavaScript写一个拼图游戏

    拼图游戏的代码400行, 有点多了, 在线DEMO的地址是:打开: 因为使用canvas,所以某些浏览器是不支持的: you know: 为什么要用canvas(⊙o⊙)?  因为图片是一整张jpg或 ...

  3. 利用Vue.js实现拼图游戏

    之前写过一篇<基于Vue.js的表格分页组件>的文章,主要介绍了Vue组件的编写方法,有兴趣的可以访问这里进行阅读:http://www.cnblogs.com/luozhihao/p/5 ...

  4. atitit.html5 拼图游戏的解决之道.

    atitit.html5 拼图游戏的解决之道. 1. 拼图游戏的操作(点击法and 拖动法) 1 1. 支持键盘上.下.左.右键移动: 1 2. 支持点击空白模块中的上下左右箭头移动: 1 3. 支持 ...

  5. Vue.js实现拼图游戏

    Vue.js实现拼图游戏 之前写过一篇<基于Vue.js的表格分页组件>的文章,主要介绍了Vue组件的编写方法,有兴趣的可以访问这里进行阅读:http://www.cnblogs.com/ ...

  6. 拼图游戏js

    实现算法: 1. JavaScript动态生成拼图:通过生成16个div,且除最后一个div不使用背景图片以外,其他div都设置拼图图片为背景.然后通过调整background-position来实现 ...

  7. 一款html拼图游戏详解

    本文是爱编程原创翻译,转载请看清文末的转载要求,谢谢合作! 游戏介绍 这篇文章是献给web游戏开发者用简单的开发工具开发一款游戏.此文介绍了用html.css.javascript只需简单和几个步骤开 ...

  8. C#实现拼图游戏

    C#实现<拼图游戏> (下) 原理篇   前言:在 http://www.cnblogs.com/labixiaohei/p/6698887.html 程序设计 之 C#实现<拼图游 ...

  9. SDL制作拼图游戏

    看完教程第三集后,好像自己能用这个来写一个简单的拼图游戏,第一次写出个带界面的游戏,好有成就感. 图片是自己慢慢截左上部分8个脸. #include <stdio.h> #include ...

随机推荐

  1. UVA 10795 新汉诺塔问题

    https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem& ...

  2. 关于引用JS和CSS刷新浏览器缓存问题

    有时候我们会碰到上线的新版本都要刷新一次缓存的问题.那是因为改了JS的内容,但是JSP引用的地方后面的字符串未发生改变导致浏览器读取浏览器缓存而不会重新加载新的JS内容,以下提供两种解决方式: 1.每 ...

  3. PHP入门二【PHP include 和 require的区别】

    include (或 require)语句会获取指定文件中存在的所有文本/代码/标记,并复制到使用 include 语句的文件中.包含文件很有用,如果您需要在网站的多张页面上引用相同的 PHP.HTM ...

  4. gulp入门演练

    一直想学习下gulp看了蛮多资料,然后总是感觉是是而非,突然开窍了,把自己学会的过程给大家分享下,入门超级简单的 gulp安装 安装gulp 如果参数-g 表示全局安装 $ npm install g ...

  5. Word,PDF,PPT,TXT之间的转换方法

    来源: 刘波的日志 一.把PPT转WORD形式的方法 1.利用"大纲"视图 打开PPT演示文稿,单击"大纲",在左侧"幻灯片/大纲”任务窗格的“大纲” ...

  6. 有关uploadifive的使用经验

    这段时间做了一个项目用到uploadifive上传控件,和uploadify不同,这个控件是基于HTML5的版本而不用支持falsh,因而移动端也可以使用. 整理用过的相关属性与方法: 属性 作用 a ...

  7. public private, protect. 以及继承。 草稿。

    #include <iostream>#include <thread>#include <memory> // | 父类的public成员 | 父类的protec ...

  8. OSChina码云试用

    首先在码云申请账户. 从 http://git-scm.com/download  下载window版的客户端.下载好,一步一步安装即可. $git config --global user.name ...

  9. linux下的nodejs安装

      linux下安装nodejs的方式: 1.源码安装 2.nvm安装 这里推荐使用nvm安装,避免下载nodejs源码:   安装步骤: 一.安装git        一般linux系统的git版本 ...

  10. jquery.autocomplete 模糊查询 支持分组

    //demo <!DOCTYPE html><html> <head> <meta charset="UTF-8"> <lin ...