为了账号安全,请及时绑定邮箱和手机立即绑定

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();//坦克停止移动后,做一个位置的小调整
    });
这里也不多解释了,前面的篇章已经介绍过这个绑定函数
点击查看更多内容
23人点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
Web前端工程师
手记
粉丝
176
获赞与收藏
2138

关注作者,订阅最新文章

阅读免费教程

感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消