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

  周一没有看圣诞大战,这几天比较忙也没有看赛后的报道,今天就先不扯NBA,随便扯扯自己。昨天在电脑里找东西的时候翻到以前兼职健身教练时的照片,思绪一下子回到学生时代,脑子久久换不过来。现在深深觉得健身和写代码真的是两个极端,一个往死里做(做动作),一个往死里坐(坐椅子),现在的体重和巅峰时期足足差了6公斤,以前80公斤5*10的卧推变成了现在5*20的下斜俯卧撑,唉...掉了的块和体重里可都藏着我写过的代码啊!这里给大家送点小福利,珍藏版的卫平-布莱恩特奉上:

  好了不扯,接着上一篇H5坦克大战之【玩家控制坦克移动】(http://www.cnblogs.com/zhouhuan/p/H5_tankgame2.html),今天我们来继续看玩家怎么控制坦克,今天主要修复两处bug,第一个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();
    }
};    

(PS:一个人的力量毕竟有限,如在阅读的过程中发现有描述不当或者错误的地方欢迎随时指正,笔者不胜感激!)

时间: 2024-10-26 06:00:16

H5坦克大战之【玩家控制坦克移动2】的相关文章

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

自从威少砍下45+11+11的大号三双之后,网上出现了各种各样的神级段子,有一条是这样的: 威少:Hey,哥们,最近过得咋样! 浓眉:对方开启了好友验证,请先添加对方为好友 威少:...... JRS:2333333 看到了一条比赛当天的数据统计:威少45+11+11,杜少32+8+3,伊巴卡19+11+2,雷吉杰克逊17+3+6,哈登16+6+16,雷霆管理层真应该改名雷锋管理层了,现在对雷霆管理层的每日一轮都是JRS们的常态了. 好了,不扯,接着上一篇博客(H5坦克大战之画出坦克http:/

H5坦克大战之画出坦克

今天是个特殊的日子,圣诞节,也是周末,在这里先祝大家圣诞快乐!喜庆的日子,我们可以稍微放松一下,扯一扯昨天雷霆对战凯尔特人的比赛,这场比赛大威少又双叒叕拿下三双,而且是一个45+11+11的超级三双,其实小托马斯的表现也不遑多让,拿下31分9个助攻,末节一开始便带队打出一波小高潮反超比分,无奈威少爷最后几分钟暴走直接带走比赛,让人直呼精彩.好了,扯完之后我们进入正题. 今天给大家带来的是一个比较好玩的东西--H5坦克大战.这个东西的实现主要用到了H5的canvas以及原生的js,如果你已经是大牛

【Java_项目篇&lt;1&gt;】--JAVA实现坦克大战游戏--赋予敌人行动和攻击(五)

前期相关文章 [Java_项目篇<1>]–JAVA实现坦克大战游戏–画出坦克(一) [Java_项目篇<1>]–JAVA实现坦克大战游戏–坦克移动+添加敌方坦克(二) [Java_项目篇<1>]–JAVA实现坦克大战游戏–坦克发射子弹(三) [Java_项目篇<1>]–JAVA实现坦克大战游戏–子弹连发+爆炸效果(四) 一.任务需求 赋予敌人行动和攻击. 二.思路 - 敌人行动 1.需要把EnemyTank做成线程类实现Runnable接口. run方法中,

html5制作坦克大战

全部html5都采用绘图技术完成.坦克是画出来的.(坦克,子弹,墙,水,草坪) 首先我们画出坦克. 坦克是两边两个矩形,中间一个大矩形,矩形了有一个圆,还有一根线. 画出坦克的思路是以坦克的左上角为参照点,画出坦克的其他部分. 这样的好处是,当左上角的点发生改变是,坦克才能发生改变. 不使用图片的原因就是图片是比较耗费资源的.因为图片的像素点很大. tankGame1.html的代码 <!DOCTYPE html><html><head><meta charset

java小项目之:坦克大战,90后的集体回忆杀!

坦克大战小项目! 在小学初中的时候,我相信我们都曾经沉迷于一种玩具“红白机”,这应该是80后90后的童年回忆.用绝对好好学习的誓言,求着父母买一台.自己学会插在电视机上,再和小伙伴一起买一些游戏卡带,便能在周末和课余时间,趁着父母不在厮杀一整天. 魂斗罗,超级玛丽,冒险岛,热血格斗,忍者神龟等等数不清的游戏,在我们夜以继日的奋斗下,被我们玩得十分娴熟.各种技能,小操作信手拈来.但在我的记忆中,有一款游戏我从来没通关过,那就是今天这文章的主角<坦克大战>. <坦克大战>应该也是众多游

坦克大战(一)

坦克大战(一) 相信大家对坦克大战都不陌生,并且网上也有很多用java实现的小程序,最近用了几天时间将其使用javaScript语言通过面向对象的方式实现,在实现的过程中吸收了很多新的知识,现在趁着程序即将完成之际将其记录下来. 废话不多说,先上程序源码:https://github.com/safemen/TankGame2.0.git 项目需求 在写程序之前,我们需要先大体的了解一下项目的一些需求和注意事项,方便以后的开发. 程序在运行之前会有从下到上的开场动画,然后鼠标点击选择游戏的人数,

Java__线程---基础知识全面实战---坦克大战系列为例

今天想将自己去年自己编写的坦克大战的代码与大家分享一下,主要面向学习过java但对java运用并不是很熟悉的同学,该编程代码基本上涉及了java基础知识的各个方面,大家可以通过练习该程序对自己的java进行一下实战. 每个程序版本代码中,都附有相关注释,看完注释大家就可以对本程序设计有个很明显的思路.真的很有趣,想对java重新温习的同学完全不必再对厚厚的java基础书籍进行阅读了,在跟着本次代码练习并分析后,大家一定会对java各方面基础知识 尤其是线程的知识有更深一步的了解!!! 本次坦克大

基于HTML5坦克大战游戏简化版

之前我们有分享过不少经典的HTML5游戏,有些还是很有意思的,比如HTML5版切水果游戏和HTML5中国象棋游戏.今天要分享的是一款简化版的HTML5坦克大战游戏,方向键控制坦克的行进方向,空格键发射子弹,命中敌方坦克后也会发出声音,效果还算可以.效果图如下: 在线预览   源码下载 实现的代码. javascript代码: window.addEventListener("load", canvasApp, false); //是否支持canvas function canvasSu

C++代码之坦克大战(1)

对坦克大战情有独钟是因为大学时候第一次参加程序设计比赛就做的这个游戏.当时用的语言是Java,那个比赛让我悟出了面向对象的强大之处,我也是从那时开始接触设计模式的.对我而言,坦克大战有着非同寻常的意义,所以一定要带大家用C++实现一下. 坦克大战 我们依然使用EasyX在控制台程序中制作这个游戏程序.这一篇的主要任务是在屏幕上画出一个白色的主战坦克,可以通过方向键控制它的前进方向.效果如下: 下面我们正式开始. 画布类 在这个工程中,我们将EasyX画布相关的功能封装在一个Graphic类中,创