另类菜单及疯狂坦克游戏思维教程(AS2.0 OOP)

2006-11-13 15:22 | Army


请无视标题中这隐晦的句子,我实在想不出更好且更精简的表达方法了。

这次教程的创意很新奇,我无意间想起当年盛大昙花一现的游戏——《疯狂坦克》。随后,顺着这个思维,便实现了一个另类菜单的教程。同时,疯狂坦克的一些基本游戏原理我也会叙述一下。

请先下载两个源文件:
http://ff9.ffsky.cn/flash_teach\otherfile/exp.fla
http://ff9.ffsky.cn/flash_teach\otherfile/Gun.as

我来用图简略说明一下所用到的几个图形和MC元件:



ball是一个红色的小球,它的名字也叫ball,你可以把它当作从炮管里发射出来的炮弹。它被放在舞台上,但却不在舞台之内。稍后你会明白,这样做是为了在AS中初始化它的坐标值。
btn和btnr是个三角形按钮,也就是图片左上方的那3个按钮。箭头向上和向下的用来调整炮管角度,向右的是发射键。
file是三个菜单,也就是舞台上右边的那3个矩形,分明命名为file1、file2、file3。file的第一帧加上了stop();语句以便停在第一帧,后面4帧颜色变成了红色,以便炮弹轰上它时观察变色。
gun按钮是最下边那门炮,名叫gun_movie。它由炮身gun_body和炮轮gun_wheel组成。如果你乐意,也可以把它俩合在一起画出来。

以下是AS源文件:

class Gun {
  var mc:Button, x1:Number, x2:Number, rtNum:Number = 0;
  var bMove;

  function startMove():Void {
    startDrag(mc, true, x1, mc._y, x2, mc._y);
  }
  function stopMove():Void {
    stopDrag();
  }
  function setRotation(rt:Number):Void {
    if(mc._rotation >= -15 && mc._rotation <= 15) {
      mc._rotation += rt * 3;
      rtNum += rt;
    }
    if(mc._rotation < -15) { mc._rotation = -15; rtNum = -5; }
    if(mc._rotation > 15) { mc._rotation = 15; rtNum = 5; }
  }
  function checkHit():Void {
    if(_root.ball.hitTest(_root.file1)) { trace("Hit the file1!"); _root.file1.play(); }
    if(_root.ball.hitTest(_root.file2)) { trace("Hit the file2!"); _root.file2.play(); }
    if(_root.ball.hitTest(_root.file3)) { trace("Hit the file2!"); _root.file3.play(); }
  }
  function startFire():Void {
    trace("Fire!");
    _root.ball._x = mc._x;
    _root.ball._y = mc._y;
  }
  function fire():Void {
    _root.ball._x += 10;
    _root.ball._y -= Math.tan(Math.PI/180 * 45 - Math.PI/180 * rtNum * 3) * 10;
    stopFire();
    checkHit();
  }
  function ballMove():Void {
    bMove = setInterval(this, "fire", 10);
  }
  function stopFire():Void {
    if(_root.ball._x > 550 || _root.ball._x < 0) { clearInterval(this.bMove); trace("Stop!"); }
    if(_root.ball._y > 400 || _root.ball._y < 0) { clearInterval(this.bMove); trace("Stop!"); }
  }

  function Gun(mc:Button) {
    this.mc = mc;
    x1 = mc._x - 100;
    x2 = mc._x + 100;
  }
}


解释一下这段程序吧。
构造器Gun(mc:Button)接收按钮mc参数,然后实例化本身的mc属性,再初始化x1和x2为mc的横坐标值-100和+100。
startMove()是一个拖动炮的方法,里面使用了AS内置拖动MC的函数startDrag(),5个参数依次为要拖动的MC对象、是以MC中心为原点拖动还是以鼠标点击为原点拖动、拖动限制矩形区域的左边界、上边界、右边界、下边界。显然,我将这门炮限制只能左右拖动,且范围是[-100, +100]象素之间。
stopMove()是停止拖动方法,使用内置函数stopDrag()停止拖动。
setRotation()方法是设置炮的角度,当这门炮的调整角度在[-15, +15]之间时可用,这也就是第一个if句的意思。后面两个判断句是当炮的角度超过这个值时,重新置为边界值。rtNum是用来记录角度变化的临时变量,你可能认为这多次一举了,因为mc的_rotation也能记录,但是它的数据总是不尽人意,希望以后的版本能更好些。
checkHit()是检测碰撞方法,使用了AS内置的hitTest函数。当炮弹飞行撞击到某个file菜单时就会执行它,这里我设置撞到哪个菜单,哪个就继续播放下去。如是我们便可观看到被撞的file变红的样子。
startFire()是开火方法,它初始化了红色小球的坐标,使其与炮的坐标一致。这就是开头我所说的为何把红色小球放在舞台之外的用意,炮的位置是不固定的,也许你“打一枪换一个地方”,这样在每次开火之前初始化位置即方便又实惠(我可没在为哪家方便面做广告……)。
fire()方法定义了炮弹如何飞行。首先我让炮弹的横坐标值+10,然后通过三角函数计算让纵坐标+相应的值。再调用stopFire()和checkHit()方法,stopFire()后面会有介绍。
ballMove()是一个执行方法,调用AS内置的setInterval函数执行fire()。我同时将这个执行函数赋给bMove,以便清除。
stopFire()顾名思义停止开火,当炮弹飞出到屏幕之外时就应该清除一直执行的bMove函数。

另外的AS都是使用这些定义好的东西了,说明一下即可~
在主时间轴上有如下AS:

var g1:Gun = new Gun(gun_movie);

调整炮管角度的上下三角按钮分别是:

on(release) {
  g1.setRotation(-1);
}

on(release) {
  g1.setRotation(-1);
}


发射炮弹的按钮:

on(release) {
  g1.startFire();
  g1.ballMove();
}


炮本身的AS:

on(press) {
  g1.startMove();
}
on(release) {
  g1.stopMove();
}


好了,整体程序已经介绍完了,自己运行一下吧。如何,找到一些感觉了吗?
那我继续提难题了:如何让炮弹下落的更真实,也就是让它受万有引力的引向在飞行过程中有重力加速度?很显然,这就要在fire()方法里下功夫了。自然要定义一个重力加速度常量和单位时间内下落距离变量,让bMove每执行一次通过计算得出相应时间内下落的距离。这些东西就要靠你自己去完成了,我又要充当那个懒惰的引路人,把问题不负责任地留给你去解决……
记得重力加速度约等于9.8,还有一些公式:Vt =Vo + at; S = 1/2 * gt2