博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
H5 canvas控制坦克移动2
阅读量:6354 次
发布时间:2019-06-22

本文共 11136 字,大约阅读时间需要 37 分钟。

  接着上一篇( ),这篇主要修复两个bug,第一,玩家按下方向键时,坦克的炮筒应该指向相应的方向,并向该方向移动,第二,坦克不能开出边界,上一节的代码坦克是可以开出边界的,这样显然是不行的。
 
  
1. 修复第一个bug
  我们的思路是,给造坦克的函数里再传一个方向的参数,我们让"u", "d", "l", "r"分别表示上下左右,封装这样一个可以传方向的函数之后,我们在用户按下不同的键时传不同的参数进去,由于整个地板每隔100毫秒会刷新一次,那么这个函数就能以肉眼分辨不出来的速度,在用户按下键的一瞬间相应地生产出不同方向的坦克了。
  如下:
//封装一个画坦克的函数,传两个参数x,y,分别代表左上角的横纵坐标//再增加一个参数dir来表示方向 上下左右分别传"u" "d" "l" "r"function drawTank(x,y,dir){    var cxt = getCxt();    switch(dir){        case "u":                             //此时造一个向上的坦克            cxt.fillStyle = "#542174";            cxt.fillRect(x,y,20,65);                            cxt.fillRect(x+70,y,20,65);                            cxt.fillRect(x+23,y+10,44,50);                            cxt.fillStyle = "#FCB827";            cxt.beginPath();            cxt.arc(x+45,y+35,16,0,2*Math.PI,false);                    cxt.closePath();            cxt.fill();            cxt.strokeStyle = "#FCB827";            cxt.lineWidth = "8.0";            cxt.moveTo(x+45,y+35);                                    cxt.lineTo(x+45,y-25);                                    cxt.stroke();            break;        case "d":                             //此时造向下的坦克            cxt.fillStyle = "#542174";            cxt.fillRect(x,y,20,65);                            cxt.fillRect(x+70,y,20,65);                            cxt.fillRect(x+23,y+10,44,50);            cxt.fillStyle = "#FCB827";            cxt.beginPath();            cxt.arc(x+45,y+35,16,0,2*Math.PI,false);                    cxt.closePath();            cxt.fill();            cxt.strokeStyle = "#FCB827";            cxt.lineWidth = "8.0";            cxt.moveTo(x+45,y+35);            cxt.lineTo(x+45,y+95);        //和向上造相比,只有炮筒需要改变                    cxt.stroke();            break;        case "l":                             //此时造向左的坦克            cxt.fillStyle = "#542174";            cxt.fillRect(x,y,65,20);        //和向上造坦克相比,画第一个矩形时长宽互换即可            cxt.fillRect(x,y+70,65,20);        //向左的坦克,注意坐标之间的转换即可,以下类似不再一一解释            cxt.fillRect(x+10,y+23,50,44);            cxt.fillStyle = "#FCB827";            cxt.beginPath();            cxt.arc(x+35,y+45,16,0,2*Math.PI,false);            cxt.closePath();            cxt.fill();            cxt.strokeStyle = "#FCB827";            cxt.lineWidth = "8.0";            cxt.moveTo(x+35,y+45);            cxt.lineTo(x-25,y+45);            cxt.stroke();            break;        case "r":            cxt.fillStyle = "#542174";            cxt.fillRect(x,y,65,20);        //和造向左的坦克类似,只要改动炮筒即可向右            cxt.fillRect(x,y+70,65,20);                    cxt.fillRect(x+10,y+23,50,44);            cxt.fillStyle = "#FCB827";            cxt.beginPath();            cxt.arc(x+35,y+45,16,0,2*Math.PI,false);            cxt.closePath();            cxt.fill();            cxt.strokeStyle = "#FCB827";            cxt.lineWidth = "8.0";            cxt.moveTo(x+35,y+45);            cxt.lineTo(x+95,y+45);            cxt.stroke();    }}
  和之前不同的是,我们增加了一个switch语句,用来判断传进来的方向,然后将不同的画坦克的动作放进不同的case分支里。看起来代码量很大,但其实都是从向上画的代码改过来的,难度不大,具体一些的说明都写在了注释里。
  接下来我们给myTank这个对象增加一个属性direction,用来确定坦克的方向,初始的时候我们让它等于"u",画一个向上的坦克。
  相应地,造坦克的时候多传一个参数进去就可以了:
drawTank(myTank.x,myTank.y,myTank.direction);
  再接着我们要做的就是在玩家按下不同的键时响应的改变方向就可以了:
window.onkeydown = function(eve){                                                    switch(eve.keyCode){        case 38:        case 87:            myTank.y -= myTank.step;    //Y坐标减小向上移动            myTank.direction = "u";        //改变成向上的方向            break;        case 40:        case 83:            myTank.y += myTank.step;    //Y坐标增加向下移动            myTank.direction = "d";        //改变为向下的方向            break;        case 37:        case 65:            myTank.x -= myTank.step;    //X坐标减小向左移动            myTank.direction = "l";        //改变为向左的方向            break;        case 39:        case 68:            myTank.x += myTank.step;    //X坐标增加向右移动            myTank.direction = "r";        //改变为向右的方向    }};
  不过,我们最好再做进一步的封装,首先给myTank对象增加一些方法:
myTank.turnUp = function(){    myTank.y -= myTank.step;        myTank.direction = "u";        };myTank.turnDown = function(){    myTank.y += myTank.step;        myTank.direction = "d";        };myTank.turnLeft = function(){    myTank.x -= myTank.step;        myTank.direction = "l";        };myTank.turnRight = function(){    myTank.x += myTank.step;        myTank.direction = "r";        };
  再根据玩家的操作进行相应地调用:
window.onkeydown = function(eve){                                                    switch(eve.keyCode){        case 38:        case 87:            myTank.turnUp();            break;        case 40:        case 83:            myTank.turnDown();            break;        case 37:        case 65:            myTank.turnLeft();            break;        case 39:        case 68:            myTank.turnRight();    }};
  给myTank对象添加的属性和方法多了之后这样看着很烦,可读性也比较差,我们有必要对它进行改动:
var myTank = {    x : 350,    y : 400,    step : 3,    direction : "u",    turnUp : function(){        myTank.y -= myTank.step;            myTank.direction = "u";        },    turnDown : function(){        myTank.y += myTank.step;            myTank.direction = "d";        },    turnLeft : function(){        myTank.x -= myTank.step;            myTank.direction = "l";        },    turnRight : function(){        myTank.x += myTank.step;            myTank.direction = "r";            }};
  嗯,这样看着舒服多了。
 
  
2.解决第二个bug
  我们的思路是,重新封装一下turnUp turnDown turnLeft turnRight这几个方法,给里面加上判断条件,如果判断为将要出界,那么不再执行改变坐标的代码,这样,坦克就只能在可视区内运动了。具体判断方法如下:
  坦克如果将要开出上面的边界,那么开出去之前它一定是向上的,此时(myTank.x, myTank.y)点是坦克左边履带左上角的点,我们暂且将这个点称为原点,再回头看一下向上画坦克时的代码:cxt.arc(x+45,y+35,16,0,2*Math.PI,false); 可知圆盖中心点(也就是炮筒的起点)和原点之间的纵向距离为35,又易知炮筒的总长度为60(cxt.moveTo(x+45,y+35); cxt.lineTo(x+45,y-25);),那么显然炮筒的起点与原点的纵向距离就是25,所以我们就可以这样判断,坦克的y坐标减去25大于等于0的时候我们再让坦克向上动起来,转化成代码就是:
var myTank = {    turnUp : function(){        if((myTank.y-25) >= 0){            myTank.y -= myTank.step;                myTank.direction = "u";            }    }};
  这时候,坦克就不会再超出上面的边界了。
  坦克向下移动的时候,我们把坦克履带的长度考虑进去就可以了,如下:
var myTank = {    turnDown : function(){        if((myTank.y+90) <= 500){            myTank.y += myTank.step;                myTank.direction = "d";        }        }};
  同理坦克向左和向右移动时如下:
var myTank = {    turnLeft : function(){        if((myTank.x-25) >= 0){            myTank.x -= myTank.step;                myTank.direction = "l";            }    },    turnRight : function(){        if((myTank.x+90) <= 800){            myTank.x += myTank.step;                myTank.direction = "r";        }            }};

 

  
3. 补充说明
  上一节的代码其实存在一定的问题,就是每一次更新战场的时候都会去getCxt()一下,清理了战场之后后面又会drawTank(), drawTank()里面又有getCxt(),这样就会重复不断地获取同一个节点,而且更新战场的函数每100毫秒执行一次,虽然不会影响功能,但是会影响到游戏的性能,我们可以定义一个变量专门用来存放获取到的绘图环境,后面需要的时候直接用就好了。
 
  
4. 最终代码
//封装一个获取绘图环境的函数function getCxt(){    var myCanvas = document.getElementById('floor'),       myContext = myCanvas.getContext('2d');    return myContext;}//为了防止重复地获取节点影响性能,我们将获取到的绘图环境(也就是画笔对象)存起来var oCxt = getCxt();//封装一个画坦克的函数,传两个参数x,y,分别代表左上角的横纵坐标//再增加一个参数dir来表示方向 上下左右分别传"u" "d" "l" "r"function drawTank(x,y,dir){    switch(dir){        case "u":                             //此时造一个向上的坦克            oCxt.fillStyle = "#542174";            oCxt.fillRect(x,y,20,65);                            oCxt.fillRect(x+70,y,20,65);                            oCxt.fillRect(x+23,y+10,44,50);                            oCxt.fillStyle = "#FCB827";            oCxt.beginPath();            oCxt.arc(x+45,y+35,16,0,2*Math.PI,false);                    oCxt.closePath();            oCxt.fill();            oCxt.strokeStyle = "#FCB827";            oCxt.lineWidth = "8.0";            oCxt.moveTo(x+45,y+35);                                    oCxt.lineTo(x+45,y-25);                                    oCxt.stroke();            break;        case "d":                             //此时造向下的坦克            oCxt.fillStyle = "#542174";            oCxt.fillRect(x,y,20,65);                            oCxt.fillRect(x+70,y,20,65);                            oCxt.fillRect(x+23,y+10,44,50);            oCxt.fillStyle = "#FCB827";            oCxt.beginPath();            oCxt.arc(x+45,y+35,16,0,2*Math.PI,false);                    oCxt.closePath();            oCxt.fill();            oCxt.strokeStyle = "#FCB827";            oCxt.lineWidth = "8.0";            oCxt.moveTo(x+45,y+35);            oCxt.lineTo(x+45,y+95);        //和向上造相比,只有炮筒需要改变                    oCxt.stroke();            break;        case "l":                             //此时造向左的坦克            oCxt.fillStyle = "#542174";            oCxt.fillRect(x,y,65,20);        //和向上造坦克相比,画第一个矩形时长宽互换即可            oCxt.fillRect(x,y+70,65,20);        //向左的坦克,注意坐标之间的转换即可,以下类似不再一一解释            oCxt.fillRect(x+10,y+23,50,44);            oCxt.fillStyle = "#FCB827";            oCxt.beginPath();            oCxt.arc(x+35,y+45,16,0,2*Math.PI,false);            oCxt.closePath();            oCxt.fill();            oCxt.strokeStyle = "#FCB827";            oCxt.lineWidth = "8.0";            oCxt.moveTo(x+35,y+45);            oCxt.lineTo(x-25,y+45);            oCxt.stroke();            break;        case "r":            oCxt.fillStyle = "#542174";            oCxt.fillRect(x,y,65,20);        //和造向左的坦克类似,只要改动炮筒即可向右            oCxt.fillRect(x,y+70,65,20);                    oCxt.fillRect(x+10,y+23,50,44);            oCxt.fillStyle = "#FCB827";            oCxt.beginPath();            oCxt.arc(x+35,y+45,16,0,2*Math.PI,false);            oCxt.closePath();            oCxt.fill();            oCxt.strokeStyle = "#FCB827";            oCxt.lineWidth = "8.0";            oCxt.moveTo(x+35,y+45);            oCxt.lineTo(x+95,y+45);            oCxt.stroke();    }}//初始化一个对象myTank,用来存储一些属性和方法var myTank = {    x : 350,    y : 400,    step : 3,    direction : "u",    turnUp : function(){        if((myTank.y-25) >= 0){                //加判断条件防止开出边界            myTank.y -= myTank.step;                myTank.direction = "u";            }    },    turnDown : function(){        if((myTank.y+90) <= 500){            myTank.y += myTank.step;                myTank.direction = "d";        }        },    turnLeft : function(){        if((myTank.x-25) >= 0){            myTank.x -= myTank.step;                myTank.direction = "l";            }    },    turnRight : function(){        if((myTank.x+90) <= 800){            myTank.x += myTank.step;                myTank.direction = "r";        }            }};//先画一个坦克出来drawTank(myTank.x,myTank.y,myTank.direction);        //一开始先造一个向上的出来//封装一个更新战场的函数function updateFloor(){    oCxt.clearRect(0,0,800,500);            //更新之前先清除画布    drawTank(myTank.x,myTank.y,myTank.direction);        //清除完之后重新造坦克,坦克要移动就必须实时地根据坐标重新来造}//设置一个间歇调用的函数,每隔100ms更新一下战场setInterval(function(){    updateFloor();},100);//响应玩家的操作指令window.onkeydown = function(eve){                                                    switch(eve.keyCode){        case 38:        case 87:            myTank.turnUp();            break;        case 40:        case 83:            myTank.turnDown();            break;        case 37:        case 65:            myTank.turnLeft();            break;        case 39:        case 68:            myTank.turnRight();    }};

转载于:https://www.cnblogs.com/zhouhuan/p/H5_tankgame3.html

你可能感兴趣的文章
mvn常用命令
查看>>
redis zset 顺序问题
查看>>
C# 判断网站是不是discuz论坛
查看>>
[转载] 七龙珠第一部——第001话 布玛与孙悟空
查看>>
linux 内核升级
查看>>
BZOJ1791[Ioi2008]Island 岛屿 ——基环森林直径和+单调队列优化DP+树形DP
查看>>
BigDecimal的用法总结
查看>>
业务学习
查看>>
围观窗体与组件04 - 零基础入门学习Delphi26
查看>>
springmvc中的数据传递
查看>>
HTTP解析-----第一课
查看>>
constructor 属性返回变量或对象的构造函数。判断是否为日期,数组的例子
查看>>
Filesystem Hierarchy Standard
查看>>
简单的计算几何
查看>>
在Windows2008r2 安装.net4.5
查看>>
小程序学习视频教程
查看>>
js 脚本学习 索引
查看>>
外部服务的隔离及降级
查看>>
性能优化浅谈
查看>>
读取一个文件,将其Base64编码,每76个字符加一个换行(转)
查看>>