接着上一篇(http://www.cnblogs.com/zhouhuan/p/H5_tankgame2.html),这篇主要修复两个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();
}
};

H5 canvas控制坦克移动2的更多相关文章

  1. H5 canvas控制坦克移动

    接着上一篇(http://www.cnblogs.com/zhouhuan/p/H5_tankgame.html),这一篇研究一下怎么响应玩家的操作让坦克进行相应的移动.   1. 了解keydown ...

  2. H5 canvas绘制出现模糊的问题

    在之前做移动端小游戏幸运转盘.九宫格转盘,使用到了 canvas ,也是第一次在项目中使用 canvas 来实现. 近期测试人员反应 canvas 绘制的内容太模糊,心想着用 canvas 绘制出来的 ...

  3. h5 canvas 小球移动

    h5 canvas 小球移动 <!DOCTYPE html> <html lang="en"> <head> <meta charset= ...

  4. h5 canvas 画图

    h5 canvas 画图 <!DOCTYPE html> <html lang="en"> <head> <meta charset=&q ...

  5. H5 canvas建造敌人坦克

      接着上一篇(http://www.cnblogs.com/zhouhuan/p/H5_tankgame3.html),这一篇建造敌人的坦克. 思路是,基于可扩展性和性能等方面的考虑,用构造函数改造 ...

  6. 使用H5 canvas画一个坦克

      具体步骤如下:   1. 首先做出绘图区,作为坦克的战场   <canvas id="floor" width="800px" height=&quo ...

  7. H5坦克大战之【玩家控制坦克移动2】

    周一没有看圣诞大战,这几天比较忙也没有看赛后的报道,今天就先不扯NBA,随便扯扯自己.昨天在电脑里找东西的时候翻到以前兼职健身教练时的照片,思绪一下子回到学生时代,脑子久久换不过来.现在深深觉得健身和 ...

  8. H5坦克大战之【玩家控制坦克移动】

    自从威少砍下45+11+11的大号三双之后,网上出现了各种各样的神级段子,有一条是这样的: 威少:Hey,哥们,最近过得咋样! 浓眉:对方开启了好友验证,请先添加对方为好友 威少:...... JRS ...

  9. H5 canvas 绘图

    H5的canvas绘图技术   canvas元素是HTML5中新添加的一个元素,该元素是HTML5中的一个亮点.Canvas元素就像一块画布,通过该元素自带的API结合JavaScript代码可以绘制 ...

随机推荐

  1. collectionView itemW宽度计算不对

    ([[UIScreen mainScreen] bounds].size.width - 28) / 4.00 没加括号 collectioView不能正常撑开 用flowLayout 不要用 代理方 ...

  2. java8工具类使用

    1:map的使用 computeIfPresent ,如果键已经存在,将键和值作为参数传到函数式中,计算返回新的值 import java.util.HashMap; import java.util ...

  3. PAT Sum of Number Segments[数学问题][一般]

    1104 Sum of Number Segments(20 分) Given a sequence of positive numbers, a segment is defined to be a ...

  4. 认识与设计Serverless(一)

    一.什么是Serverless 定义:Serverless是一种无服务器的架构,区别于传统的Baas,SAAS,作为FAAS(函数即服务)而存在,函数由事件驱动触发并按需调用. 按需调用:区别于传统的 ...

  5. mysql性能测试-tpcc

    mysql性能测试-tpcc Tpcc-mysql TPC-C是专门针对联机交易处理系统(OLTP系统)的规范 Tpcc-mysql由percona根据规范实现 TPCC流程  更能模拟线上业务   ...

  6. Frame 框架的创建

    Qt 创建Frame框架的例子: QFrame * frm = new QFrame(this); //创建一个框架 frm->setFrameStyle(QFrame::StyledPanel ...

  7. Linux系统——Inotify事件监控工具

    每秒传输文件200个 Rsync放在定时任务中也只是一分钟执行一回,要想达到实时的效果,为防止单点nfs架构故障,再启动一台nfs服务器作为主nfs服务器的备份服务器,此时需要inotify实时同步数 ...

  8. 2016-ccf-data-mining-competition 搜狗用户画像构建

    想法1:   分成147(3*7*7)类, 后来觉得这样效果不好,后来看了看竞赛要求的也是分别预测,分别评分,而不是一次就把3类的标签都给出   所有后来我们改进了当时的想法,决定对年龄,性别,学历进 ...

  9. 【运维技术】Jenkins配置使用教程

    Jenkins配置使用教程 单机jenkins启动 软件安装和启动,必须含有java环境 # 安装jdk,参考其他教程,创建文件目录 mkdir -p /app/jenkins cd /app/jen ...

  10. Maven聚合项目在eclipse中显示没有层次

    大部分时间都在用idea做maven的项目,今天用eclipse导入了maven项目,果然不出所料,界面有显示问题,各个模块都堆叠在同一层级,根本看不出父项目与子项目之间的层次关系,见下图: 于是找修 ...