不亲自编程,很多东西是体会不到的。现在理论讲了不少,可以试着实践一下了。理论不
足之处,随做随讲一点,也可以在网上查询。
现在我们来做一个高射炮打飞机的游戏。
如果遇到程序运行不通,可以根据报错信息找到那一行,看看有什么错误,往往是大小写
字母之类的问题。如果还找不到错误,可以删除这一行重写,等等各种手段,发挥同学们
的聪明才智,解决问题。当然,也可以把代码发给我看看。
先建立一个html文件:
<html>
<div id=BattleField style="border:1px solid #000000; background:#fff;position:
Absolute;Left:0px;Top:0px;Width:1200px;Height:600px" >
<div id=DiJi style="position:Absolute;background-color:00ff00"><LABEL
name=diji >敌机</LABEL></div>
<div id=GaoShePao style="position:Absolute;Left:500px;Top:500px"><LABEL
name=gun >炮</LABEL></div>
</div>
</html>
尚需进一步美化,我们现在是学编程,所以美术差点就差点吧。一个div里面嵌套了两个
div,这两个div分别是敌机和炮,style里都设置了position:Absolute,也就是用绝对位
置坐标,以方便在程序里控制其移动。主要页面元素都加了ID或name,以方便在程序中查
找和识别。
接下来,我们让鼠标点击“炮”时,做出开炮动作:
<LABEL name=gun onClick=KaiPao() >
然后做一个开炮函数KaiPao(),先做一个空的:
<script language=javascript>
function KaiPao(){
alert(“开炮!”);
}
</script>
试一试,点击“炮”弹出“开炮”消息框就对了。
结合真正的大炮,可以想到“开炮”这个函数需要做以下动作:
1, 产生一枚炮弹(实际大炮中装填炮弹的过程在游戏中省略了,并且弹药无限供应)
。
2, 炮弹按瞄准的方向以一定速度和轨迹移动。
3, 炮弹带有引信:当炮弹位置与敌机足够近时,引爆。如果超过预定的高度或时间
仍没有找到目标,也引爆。
4, 炮弹爆炸时,发出光、烟、声等视觉效果,摧毁附近目标,炮弹自身也消失。
现在我们一步一步实现上述内容。
Javascript支持动态生成页面元素。开炮动作一开始,先生成一枚炮弹:
var dan = document.createElement("div");
产生了一个div,并赋给一个变量dan,这样后面对这个div进行各种操作,只要对dan操作
即可。
这个新增的div必须加到一个已经存在的div下面,我们把它加进“战场(BattleField)”
document.getElementById("BattleField").appendChild(dan);
这一行里面,document.getElementById是非常常用的语法,返回那个以“BattleField”
为ID的div。然后调用div元素中系统提供的appendChild方法,把炮弹div加为它的下级元
素。
这时dan还是空的,什么也没有,先写上一个字让我们能看见:
dan.innerHTML="弹";
然后设置一下dan的位置:
dan.style.position="Absolute";
dan.style.marginLeft="500px";
dan.style.marginTop="480px";
这时可以刷新一下页面,看看阶段性成果,点击“炮”会产生一枚“弹”,但是不会动。
接下来,炮弹的移动是件比较复杂的事儿。
在屏幕上产生移动的效果,当然不能一步移动到位,而是移动一点,延迟很短的时间再移
动一点…。在各种编程语言中,都有类似delay、sleep这样名字的函数,做延时的效果。
但是经过查询,发现javascript没有这样的函数,而是代以setInterval、settimeout这
两个函数。这俩函数都有俩参数:延迟时间和到时间后执行的代码。Settimeout是只执行
一次,setInterval是循环不断执行,直到clearInterval为止。setInterval正好适合炮
弹不断移动的用途。
仔细研究发现,你交给setInterval一段代码,让它每隔一段时间执行一次,然后你就可
以继续干别的事情了,setInterval所执行的动作,与其他代码的运行过程可以是同时进
行的,甚至多枚炮弹可以各自同时移动,不相互影响,不需要程序员操心。本来,在别的
编程语言中这种功能叫“多线程”,是很复杂很高级的东西,我们不得不叹服javascript
的无所不能。
让我们尝试以setInterval函数为核心“让炮弹飞”。
setInterval的格式是: ID号 = setInterval(需要周期执行的代码, 间隔毫秒数);
其中ID号是setInterval返回值,是这个循环任务的代号,什么时候不要它继续循环执行
了,调用clearInterval时需要用到这个ID号。
为了方便,我们充分运用面向对象的方法,把这个号存为对象dan的成员变量:
dan.iid = setInterval(需要周期执行的代码,30);
这里30表示每30毫秒执行一次。
填写“需要周期执行的代码”这一参数时,麻烦来了。本来可以写上 this.move(),把移
动一小步的代码写进成员函数dan.move(),或者写一个通用函数move(炮弹对象),然后在
此处调用move(this),但是setInterval 的语法里,“需要周期执行的代码”是用字符串
方式传进去的,并且是脱离当前这枚炮弹对象运行的,this指针也没法传进去。打个比喻
,你给一名特工秘密传递一张纸条,让他每隔10分钟检查一下你房间周围有没有可疑人物
,纸条上如果写“我的房间….”,那么特工根本不知道你是谁,或者不知道你的房间是
哪个,所以必须写上“3楼201房间”才行。
具体到本例,我们只好给每一枚炮弹都编上号,好让“需要周期执行的代码”知道去挪动
哪一枚炮弹。
这需要一个“全局变量”,也就是页面上所有的代码都能使用的变量。做为全局变量,需
要把变量的声明和赋初值放在最前面,然后每当给一枚炮弹编号,就把这个变量+1:
var danNo = 0;
function KaiPao(){
var dan = document.createElement("div");
……
dan.id="PaoDanBianHao"+danNo;
danNo++;
……
然后就可以这样写“需要周期执行的代码”:
dan.iid = setInterval("document.getElementById('"+ dan.id
+"').move()",30);
先找到这枚炮弹,然后调用这枚炮弹的move()方法。当然,move()方法我们必须自己写好
:
dan.move = function(){
}
move()方法没什么技术含量,无非是获得对象的横坐标和纵坐标,加加减减之后在放回去
即可,但是麻烦也不少。炮弹本质上是一个div对象,其坐标存放在style. marginTop、
style. marginLeft里,这两个成员变量的值都是字符串型,并且后面缀着“px”两字,
而加加减减是数字游戏,必须修理一番,变成数字以便计算。这个过程要多次运行,我们
把它写成一个函数:
function pxToNum(px){
var s = px.substr(0,px.length-2);
var n = parseInt(s);
return n;
}
传进去一个字符串,砍去最后两个字符,转换成整数返回。
然后dan.move就可以这样写:
var y = pxToNum(this.style.marginTop);//纵坐标
var x = pxToNum(this.style.marginLeft);//横坐标
获得横纵坐标后,我们先实现一个比较简单的,垂直向上开炮的情况,每30毫秒向上移动
的距离可以先设置成5:
y -= 5;
然后把坐标值写回div.style,一般整数转换成字符串风险不大,所以就这样写
:
this.style.marginTop = "" + y + "px";
this.style. marginLeft= "" + x + "px";
前面的空字符串表示后面跟着的都要转换成字符串再相加。
现在可以试试,炮弹已经可以飞了。
这里需要考虑周全一些,当炮弹已经飞出屏幕了怎么办。我们可以先设计简单一点,这种
情况下就爆炸掉好了,调用dan.explode方法,这个方法还没写,待会写。现在在获得坐
标之后先进行判断:
if(x<5 || y<5){
this.explode();//爆炸
return;
}
然后定义explode():
dan.explode = function(){
this.innerHTML="<font size=10 color=red>啪!</font>";
clearInterval(this.iid);//不用继续移动了
}
可以在试运行一下,现在炮弹爆炸的效果初具,下一步延迟半秒把这个炮弹连同“啪”字
清理掉。上网查的结果,javascript删除页面元素的方法不尽如人意,现在时间不早,快
下课了,我们先简单处理一下,眼不见为净,把它设置为不可见:
setTimeout("document.getElementById('"+ dan.id +"').style.display='none'",500)
;
和前面setInterval一样,单双引号齐上阵,读起来费点劲。
下面我们要击落敌机,为了方便起见,在javascript最前面定义一个变量指向敌机,并把
敌机拉到炮口下挨打:
var DiJi1 = document.getElementById("DiJi");
DiJi1.style.marginLeft = "500px";
DiJi1.style.marginTop = "10px";
然后给敌机定义一个被击落的方法:
DiJi1.off = function(){
this.innerHTML="<font size=10 color=black >轰!</font>";
this.style.background="ff0000";
setTimeout("DiJi1.style.display='none'",1500);
}
然后在炮弹里写上“引信”方法:
dan.fuse = function(){
if(DiJi1.style.display!="none"){
var yy = pxToNum(this.style.marginTop) -
pxToNum(DiJi1.style.marginTop);//与敌机纵向距离
var xx = pxToNum(this.style.marginLeft) -
pxToNum(DiJi1.style.marginLeft);//与敌机纵向距离
if((xx < 20 && xx > -20) && (yy < 20&& yy > -20)){//距
离足够近
this.explode();//爆炸
DiJi1.off();
}
}
}
在炮弹每次移动后启动一下引信,dan.move里最后加上:
this.fuse();
可以运行一下,看看效果如何。
练习题:在本例基础上继续完善和发展,例如敌机也在飞,炮可以调整角度等等
。
--
FROM 114.251.89.*