HTML+CSS+JQ试做经典坦克大战(四)
各位小伙伴们大家好,第四篇为大家带来坦克移动脚本的制作。
为了更好的帮助想收获知识的小伙伴,我将在本篇内容中列出要用到的知识点,并加一定的注解。
本篇内容主要知识点包括:
.position()的使用
获取当前DOM元素相对于其定位元素的距离,使用后返回{left=xxx,top=xxx},分别用.left和.top获取,单位是px;注意.position()与.offset()的区别,后者是获取当前元素与浏览器可视区域边缘的距离。
Math.floor Math.ceil Math.round的区别
分别是对一个数字进行,向下取整、向上取整和四舍五入;
.each(function(index,element){})
JQ中遍历操作,each中传入index的作用;
DOM组1.add(DOM组2)的作用
这个用法,是将DOM组1和DOM组2合并到DOM组1中,当然也可以把他们的结果赋值给新的DOM组;
.animate()的一些用法
JQ中自定义简单动画用 .animate(),内部可传入参数,第一个参数是动画的css改变值,可以是一个JSON对象,比如{top:100,left:200,color:”#fff“,"border-top":"1px"},每组属性用逗号隔开,属性名与属性值用冒号隔开,对于有“-”的属性名和字符串类型的属性值,需用引号包裹;
第二个参数,传入动画的时间,可以是一个数字,比如200 500 2000等等,可以为0,也可以是slow等;
其它参数,可以传入“linear”或“swing”,分别表线性动画和“先慢后快再慢”动画,如不传入该参数,默认是swing。
其它参数,还可以传入一个函数,这个函数表示动画执行结束后,需要再执行什么。
.aniate()的详细用法,请大家参照JQ的说明文档。
object.slice(a,b)的用法
截取操作,object可以是数组字符串,类数组(比如DOM组)。一个简单的例子:
arr1=[1,2,3,4,5,6];
arr2=arr1.slice(1,4);
执行后,arr1=[1,2,3,4,5,6];arr2=[2,3,4];//原数组不变,slice(a,b)中的ab是起始位置
slice(a,b)和splice(a,b)的区别
arr1=[1,2,3,4,5,6];
arr2=arr1.splice(1,4);
执行后,arr1=[1,6];arr2=[2,3,4,5];//原数组被截取了,并且splice(a,b)中的b,是截取的长度
DOM组.filter(选择器)
在当前DOM组中选择元素,注意区分和.find() .children()的区别,后两者分别表示在当前DOM元素的后代元素中选择和 子元素中选择。
以上就是本节内容将应用的主要知识点,我解释的不一定准确,一切以Jquery中文文档为标准。
下面开始本节内容吧!
分析一下,此时坦克将进行向上移动,坦克的前方有砖墙,坦克是不能穿越墙壁的,此时,我们就需要计算坦克距离最近的墙壁的距离,来算出坦克可以移动的最大距离。
首先,我们是不是得计算坦克所在的列数,当前坦克应该在第2列和第3列,然后在这两列出,选择所有的墙壁,最后算出最近的墙壁距离。
function tankermove(id,k){//id是坦克的id,k代表移动方向
/*获取id坦克当前位置的xy坐标*/
var $tanker=$(id);//定义坦克
var $x1= Math.floor($tanker.position().left/50)+1;//计算坦克所在第一列,左右移动用
var $x2= Math.ceil($tanker.position().left/50)+1;//坦克所在第二列
var $y1= Math.floor($tanker.position().top/50)+1;//计算坦克所在第一行,上下移动用
var $y2= Math.ceil($tanker.position().top/50)+1;//坦克所在第二行
var $tanker_left=$tanker.position().left;//定义坦克左边距
var $tanker_right=$tanker_left+50;//坦克右边距
var $tanker_top=$tanker.position().top;//坦克上边距
var $tanker_bottom=$tanker_top+50;//坦克下边距
图中的坦克执行这些语句后,可以得到$x1=2,$x2=3,意思是坦克在第二第三列。如果坦克正好只在第二列,那么$x1=$x2=2;
if(k==1){//向上移动
/*获取坦克当前位置所在行列的所有不可穿越墙,并计算出坦克所能移动的最大距离$blank*/
var $wall=chooselie($x1).add(chooselie($x2));//获取坦克所在列中所有的墙壁,chooselie(n)是一个自定义函数,用来获取第n列中的墙;这个函数后面讲
var $tankertop=$tanker.position().top;//定义坦克前部距离,这里重复定义了,其实可以用前面定义好的$tanker_top;
var $blank=$tankertop;//定义坦克的可移动距离,初始值是坦克的上边距;
var $wall2=[];//定义一个空的墙壁数组;
$wall.each(function(index, element) {//这步操作的作用,判断获取的墙壁是否能挡住坦克,因为以后会加入一些被破坏过的宽高只有一半的墙壁,这个遍历,就是把真正能挡住坦克的墙壁PUSH入新的数组$wall2中;
if(!($(this).position().left>=$tanker_right$(this).position().left+$(this).width()<=$tanker_left)){
$wall2.push($(this)[0]);//this本来只是一个html节点,$(this)可以使其变为DOM对象
}
});
$wall=$($wall2);//重新给$wall赋值,注意这一步,很重要,$wall2是一个真正的数组,数组内元素是DOM对象,但是数组是不具备DOM数组对象的一些特性的,比如不能用.each遍历。$($wall2)就可以把数组转化为DOM类数组对象
$wall.each(function(index, element) {//现在开始遍历墙面
var $top=$(this).position().top+$(this).height();//计算墙面底边到顶部的距离
if($tankertop-$top<$blank&&$tankertop-$top>=0) {//如果坦克顶部距离-墙面顶部距离比$blank小,并且是一个正值(负值表示墙在坦克的屁股后面,当然就没意义了)
$blank=$tankertop-$top;
}//更新$blank
});
$tanker.css('transform','rotate(0deg)');//坦克掉头
if($blank>0) {//如果坦克可移动距离大于0,坦克移动
var $obj_top=$tanker.position().top-$blank;
var obj={top:$obj_top};
$tanker.animate(obj,8*$blank,'linear');//8代表坦克移动的速度,越大坦克移动越慢,匀速移动
}
}
这里的k=1,是坦克向上移动,下面的代码,k=2时,坦克向下移动
else if(k==2){//向下移动
var $wall=chooselie($x1).add(chooselie($x2));
var $tankerbottom=550-$tanker.position().top;
var $blank=$tankerbottom;
var $wall2=[];
$wall.each(function(index, element) {
if(!($(this).position().left>=$tanker_right$(this).position().left+$(this).width()<=$tanker_left)){
$wall2.push($(this)[0]);
}
});
$wall=$($wall2);
$wall.each(function(index, element) {
var $bottom=600-$(this).position().top;
if($tankerbottom-$bottom<$blank&&$tankerbottom-$bottom>=0) $blank=$tankerbottom-$bottom;
});
$tanker.css('transform','rotate(180deg)');
if($blank>0) {
var $obj_top=$tanker.position().top+$blank;
var obj={top:$obj_top};
$tanker.animate(obj,8*$blank,'linear');
}
}
这里不多做解释了,向下移动只是把其中一些计算过程改变了,其余是一样的;
下面看左右移动
向左移动分析:坦克想向左移动,且不能穿越墙,那么就必须知道那些强能挡住坦克,因此先必须计算坦克再在的行,再选中所在行中的强,最后计算可移动距离。
var $y1= Math.floor($tanker.position().top/50)+1;
var $y2= Math.ceil($tanker.position().top/50)+1;//这两行计算所在行的代码上面已经有了
//当前坦克计算后,可以得到$y1=2;$y2=3,表示坦克在第二第三行;
else if(k==3){向左移动
var $wall1=$('.map_i').slice(17*$y1-17,17*$y1).filter('[class*="qiang"]');
var $wall2=$('.map_i').slice(17*$y2-17,17*$y2).filter('[class*="qiang"]');//选择当前行中的所有墙;
var $wall=$wall1.add($wall2);//用add方法,将两组列合并到$wall中
var $tankerleft=$tanker.position().left;//定义坦克左边距
var $blank=$tankerleft;//定义最大移动距离,初始就是左边距
var $wall2=[];
$wall.each(function(index, element) {
if(!($(this).position().top>=$tanker_bottom$(this).position().top+$(this).height()<=$tanker_top)){
$wall2.push($(this)[0]);
}
});
$wall=$($wall2);//这步操作也不解释了,和上下移动一样,除去了那些残缺的不能阻挡坦克前进的墙;
$wall.each(function(index, element) {
var $left=$(this).position().left+$(this).width();
if($tankerleft-$left<$blank&&$tankerleft-$left>=0) $blank=$tankerleft-$left;
});//遍历墙壁,计算最大移动距离,保存在$blank中
$tanker.css('transform','rotate(270deg)');//坦克掉头
if($blank>0){
var $obj_top=$tanker.position().left-$blank;
var obj={left:$obj_top};
$tanker.animate(obj,8*$blank,'linear');
}//坦克执行移动动画
}
else{//坦克向右移动
var $wall1=$('.map_i').slice(17*$y1-17,17*$y1).filter('[class*="qiang"]');
var $wall2=$('.map_i').slice(17*$y2-17,17*$y2).filter('[class*="qiang"]');
var $wall=$wall1.add($wall2);
$tanker.css('transform','rotate(90deg)');
var $tankerright=800-$tanker.position().left;
var $blank=$tankerright;
var $wall2=[];
$wall.each(function(index, element) {
if(!($(this).position().top>=$tanker_bottom$(this).position().top+$(this).height()<=$tanker_top)){
$wall2.push($(this)[0]);
}
});
$wall=$($wall2);
$wall.each(function(index, element) {
var $right=850-$(this).position().left;
if($tankerright-$right<$blank&&$tankerright-$right>=0) $blank=$tankerright-$right;
});
if($blank>0){
var $obj_top=$tanker.position().left+$blank;
var obj={left:$obj_top};
$tanker.animate(obj,8*$blank,'linear');
}
}
}
这两组代码就是坦克如何进行左右移动的代代码
到此,坦克移动脚本就完成了。
下面看一看 前面提到的chooselie(n)是如何定义的
function chooselie(ii){//已知当前列坐标ii的情况下,选中该列中不可穿越墙
var $qiang;
switch(ii){
case 1:$qiang=$('.map_i:nth-child(17n+1)[class*="qiang"]');break;
case 2:$qiang=$('.map_i:nth-child(17n+2)[class*="qiang"]');break;
case 3:$qiang=$('.map_i:nth-child(17n+3)[class*="qiang"]');break;
case 4:$qiang=$('.map_i:nth-child(17n+4)[class*="qiang"]');break;
case 5:$qiang=$('.map_i:nth-child(17n+5)[class*="qiang"]');break;
case 6:$qiang=$('.map_i:nth-child(17n+6)[class*="qiang"]');break;
case 7:$qiang=$('.map_i:nth-child(17n+7)[class*="qiang"]');break;
case 8:$qiang=$('.map_i:nth-child(17n+8)[class*="qiang"]');break;
case 9:$qiang=$('.map_i:nth-child(17n+9)[class*="qiang"]');break;
case 10:$qiang=$('.map_i:nth-child(17n+10)[class*="qiang"]');break;
case 11:$qiang=$('.map_i:nth-child(17n+11)[class*="qiang"]');break;
case 12:$qiang=$('.map_i:nth-child(17n+12)[class*="qiang"]');break;
case 13:$qiang=$('.map_i:nth-child(17n+13)[class*="qiang"]');break;
case 14:$qiang=$('.map_i:nth-child(17n+14)[class*="qiang"]');break;
case 15:$qiang=$('.map_i:nth-child(17n+15)[class*="qiang"]');break;
case 16:$qiang=$('.map_i:nth-child(17n+16)[class*="qiang"]');break;
case 17:$qiang=$('.map_i:nth-child(17n+17)[class*="qiang"]');break;
};
return $qiang;
}
这里的知识点,:nth-child(an+b)的应用;
[class*='qiang']的应用,表示选择class中包含“qiang”这个字符串的元素;
前面章节的学习,我们知道 砖墙的class='map_i zhuan_qiang' ;铁墙的class='map_i tie_qiang';河流的class='map_i river_qiang';老家的class='map_i zhuan_qiang_laojia'。因此这个选择器就可以把这些坦克不能穿越的墙全部过滤选中。
坦克停止移动后位置的微调函数movet()
这个函数很有必要,因为不做位置调整,坦克停止移动后的位置会是这样一个数值{top=102.34,left=234.53}。这样的结果就是,我想让坦克穿越一个宽50的隧道,坦克几乎永远也过不去,因为我很难让坦克停止移动后,top值和left值是50的倍数。
function movet(){
var $tanker=$('#yyf');//定义坦克
var $top=Math.round($tanker.position().top/5)*5+'px';//圆整top值,这里圆整后,top总是5的倍数
var $left=Math.round($tanker.position().left/5)*5+'px';//圆left值,这里圆整后,left总是5的倍数
$tanker.css('top',$top).css('left',$left);//调整位置,这里其实可以这样写$tanker.css({top:$top,left:$left});当时学艺不精。
}
经过这个调整后,坦克停止后的位置{top=102.34,left=234.53}就会圆整为{top=100,left=235},这样我再想穿越一个50宽的通道,就变的很容易了。
坦克移动命令的绑定
$(document).bind('keydown',function(event){//添加移动按键开炮ESC回车等按键指令
if(event.which==65)tankermove('#yyf',3);
if(event.which==87) tankermove('#yyf',1);
if(event.which==83) tankermove('#yyf',2);
if(event.which==68) tankermove('#yyf',4);
if(event.which==74) shot('#yyf');
if(event.which==27) {
slidedownmubu();
$(this).unbind('keydown').unbind('keyup');
setTimeout('pastemap(mapdata[0])',2150);
}
});
$(document).bind('keyup',function(event){//添加松开移动按键后,坦克立即停止的指令
if(event.which==65)$('#yyf').stop(true,false);//stop(true,false)表示元素立即停止动画,停在原地
else if(event.which==87)$('#yyf').stop(true,false);
else if(event.which==83) $('#yyf').stop(true,false);
else if(event.which==68)$('#yyf').stop(true,false);
movet();//坦克停止移动后,做一个位置的小调整
});
这里也不多解释了,前面的篇章已经介绍过这个绑定函数
共同学习,写下你的评论
评论加载中...
作者其他优质文章