FF7AC北美官网Flash菜单教程(AS2.0 OOP)

2006-12-02 17:29 | Army

在领略了FF7AC日本官网的Flash菜单后,你是否还想进一步去感受一下北美官网的呢?如果回答是肯定的话,那么请往下看,这篇教程将完完全全地介绍北美官网那构思巧妙的菜单制作过程。
也许你会说:北美官网的菜单效果看起来并没有日本官网的好看啊!那只是表面现象。我制作日本官网的教程只用了几个小时,而北美的却几乎一整天!而且,北美官网的菜单有很多元素我都省略掉了——因为它太大了——如果全部都加上去的话,你会惊叹它的华丽丝毫不在日本官网之下!更重要的是,北美官网是我见过的用类编写的最大最繁杂的AS脚本,面对这么一个优秀的作品,又有谁不动心想去一探究竟呢?

这个教程的源文件地址和效果预览:

http://ff9.ffsky.cn/flash_teach\ff7ac_na/exp.rar

我先来用一张图片介绍一下元件组成:



整个文件只有1帧,舞台大小未480*320,库中border和border_m是那四个黑色渐变的矩形条,btn是中间正8边形,sound是一个按钮声音——它们都来自于官网。所有的这些元件组成了btn_b按钮,它被实例化成5个对象放在舞台上。我们可以清除地看见舞台上方并排排列的5个实例:btn1、btn2、……、btn5。

先来看看时间轴上的少量代码:

var lb1:LoadButton = new LoadButton(btn1, 50, 150, 240, 160);
var lb2:LoadButton = new LoadButton(btn2, 260, 80, 243, 90);
var lb3:LoadButton = new LoadButton(btn3, 350, 20, 310, 162);
var lb4:LoadButton = new LoadButton(btn4, 170, 240, 233, 227);
var lb5:LoadButton = new LoadButton(btn5, 420, 280, 172, 153);
var br1:BtnRoll = new BtnRoll();


这里声明了5个LoadButton对象和一个BtnRoll对象,LoadButton类有5个参数组成的构造器。来了解下它:

class LoadButton {
  var btnPosition_x:Number, btnPosition_y:Number, toPosition_x:Number, toPosition_y:Number;
  var mc:MovieClip;
  var bMoveTo, bFade, bMove;
  var num:Number, num1:Number = 0, num2:Number = 0;

  function initialize():Void {
    //trace("Initialize finished!" );
    randomNumGen();
    mc._x = btnPosition_x;
    mc._y = btnPosition_y;
    mc._alpha = mc._xscale = mc._yscale = 40;
  }
  function randomNumGen():Void {
    var num:Number = Math.floor(Math.random() * 3) + 2;
    this.num = num;
  }
  function btnToPosition():Void {
    //trace("toPosition..." + num1);
    mc._x += (toPosition_x - mc._x) * 0.15;
    mc._y += (toPosition_y - mc._y) * 0.15;
    num1++;
    if(num1 >= 40) {
      clearInterval(bMoveTo);
      _root.br1.initialize();
      btnMove();
    }
  }
  function btnMoveTo():Void {
    bMoveTo = setInterval(this, "btnToPosition", 15);
  }
  function btnFadeTo():Void {
    //trace("Enlarging..." + num2);
    mc._xscale = mc._yscale += num;
    mc._alpha += num - 1;
    num2++;
    if(num2 >= 20) clearInterval(bFade);
  }
  function btnFade():Void {
    bFade = setInterval(this, "btnFadeTo", 20);
  }
  function setPosition():Void {
    if(_xmouse <= 240) {
      mc._x += (toPosition_x - mc._x + 40 * (240 - _xmouse) / 240) * num / 20;
    }
    else {
      mc._x += (toPosition_x - mc._x - 40 * (_xmouse - 240) / 240) * num / 20;
    }
    if(_ymouse <= 160) {
      mc._y += (toPosition_y - mc._y + 30 * (160 - _ymouse) / 160) * num / 20;
    }
    else {
      mc._y += (toPosition_y - mc._y - 30 * (_ymouse - 160) / 160) * num / 20;
    }
    //trace("Move...");
  }
  function btnMove():Void {
    bMove = setInterval(this, "setPosition", 20);
  }

  function LoadButton(mc:MovieClip, btnPosition_x:Number, btnPosition_y:Number, toPosition_x:Number, toPosition_y:Number) {
    this.mc = mc;
    this.btnPosition_x = btnPosition_x;
    this.btnPosition_y = btnPosition_y;
    this.toPosition_x = toPosition_x;
    this.toPosition_y = toPosition_y;
    initialize();
    btnMoveTo();
    btnFade();
  }
}


构造器应该很简单,首先接受5个变量赋值给对象本身,然后调用自身的几个方法,这样便实现了创建对象时它的自动运行。
initialize()方法初始化对象所指定的MC的坐标值、透明度和缩放比,randomNumGen()是一个随机数生成器,用它生成一个随机数再加以适当计算便实现了按钮的大小、透明度随机变化的效果。
btnToPosition()方法是让按钮从初始位置移动到指定位置,这里依然用了极限计算思维,并添加一个num1计数器,当它大等于40时停止执行btnMoveTo()中的setInterval函数。
在这里我要纠正一个隐含的错误:你也许会认为,用按钮的坐标值是否等于指定坐标值来判断是否结束setInterval函数不是更好吗?我的回答是——不!因为计算机中的极限计算是“取整”的,它并不会“四舍五入”,因此最后按钮的坐标值始终无法等于指定坐标值——相差1——这样setInterval就永远不会结束!我在FF7AC日本官网教程里就犯了这个错误,所幸由于别的原因,bug被覆盖掉了……
另外,if判断最后的两句暂时还没用到,它俩一个是执行br1对象的initialize()方法(br1是在时间轴上创建的BtnRoll类),一个是执行后面用以使按钮跟随鼠标移动的btnMove()方法。
btnFadeTo()方法同样采用了一个num2计数器,它的作用是让按钮在移向指定位置时不断变大、透明度增加。什么,num是什么?看看上边,那个随机数生成器。这样5个按钮便可能拥有不同的大小和透明度了。
setPosition()方法是个比较难理解的地方,它实现了按钮跟随鼠标的位置在一定范围内“相反地移动”。在这之前我写过一篇关于时钟的教程,里面有类似的思维,不过这个的更巧妙。
以横坐标移动为例。当鼠标在舞台的左半边时(小等于240):先看看“40 * (240 - _xmouse) / 240”这个计算,毫无疑问它是一个比例数值,自己画张图就能明白,鼠标越靠左值越大,然后“(toPosition_x - mc._x + 40 * (240 - _xmouse) / 240)”再计算,得到按钮与右边界范围之差,最后再“* num / 20”,并实现“+=”自增运算,以实现整体效果。
当鼠标在舞台的右半边(大于240):这个和上面是相反的,更改下相减的顺序即可。由此得出按钮所移动的限制范围是[toPosition_x - 40, toPosition_x + 40],纵坐标的原理相同。
最后的btnMove()方法是实现上面的setInterval而已。
如何?可能有点糊涂,数学或逻辑思维欠点火候的话可要加油哦。现在你理解构造器中那些方法调用的含义了吧,接下来我要介绍BtnRoll类。

class BtnRoll {
  var mcAlpha1:Number, mcAlpha2:Number, mcAlpha3:Number, mcAlpha4:Number, mcAlpha5:Number;
  var mcScale1:Number, mcScale2:Number, mcScale3:Number, mcScale4:Number, mcScale5:Number;
  var bRollOver;
  var btnNum:Number = 0, startNum:Number = 0;

  function initialize():Void {
    mcAlpha1 = _root.btn1._alpha;
    mcAlpha2 = _root.btn2._alpha;
    mcAlpha3 = _root.btn3._alpha;
    mcAlpha4 = _root.btn4._alpha;
    mcAlpha5 = _root.btn5._alpha;
    mcScale1 = _root.btn1._xscale;
    mcScale2 = _root.btn2._xscale;
    mcScale3 = _root.btn3._xscale;
    mcScale4 = _root.btn4._xscale;
    mcScale5 = _root.btn5._xscale;
    startNum = 1;
  }
  function btnRollOver():Void {
    var i:Number;
    if(btnNum > 0) {
      for(i = 1; i <= 5; i++) {
        if(btnNum == i) {
          eval("_root.btn" + i)._xscale = eval("_root.btn" + i)._yscale += (eval("this.mcScale" + i) + 80 - eval("_root.btn" + i)._xscale) * 0.2;
          eval("_root.btn" + i)._alpha ++;
          if(eval("_root.btn" + i)._alpha >= 100) eval("_root.btn" + i)._alpha = 100;
        }
        else {
          eval("_root.btn" + i)._xscale = eval("_root.btn" + i)._yscale += (eval("this.mcScale" + i) - 40 - eval("_root.btn" + i)._xscale) * 0.2;
          eval("_root.btn" + i)._alpha += (eval("this.mcAlpha" + i) - 20 - eval("_root.btn" + i)._alpha) * 0.2;
          if(eval("_root.btn" + i)._alpha <= 20) eval("_root.btn" + i)._alpha = 20;
        }
      }
    }
    else{
      for(i = 1; i <= 5; i++) {
        eval("_root.btn" + i)._xscale = eval("_root.btn" + i)._yscale += (eval("this.mcScale" + i) - eval("_root.btn" + i)._xscale) * 0.2;
        eval("_root.btn" + i)._alpha += (eval("this.mcAlpha" + i) - eval("_root.btn" + i)._alpha) * 0.2;
      }
    }
  }
  function mouseRollOver():Void {
    clearInterval(bRollOver);
    if(startNum == 1) bRollOver = setInterval(this, "btnRollOver", 20);
  }
}


可能你是初次接触到两个以上的类,其实它和一个类是一样的。在Flash菜单中“编辑-首选参数-ActionScript-ActionScript2.0设置”中我们可以看到一个“.”符号,它代表当前文件夹(和Java非常相似),这样在创建对象时Flash便会在当前文件夹中搜寻相应的类。如果你在其它文件夹中创建类,则必须在这个类路径设置才行。
还记得LoadButton类的btnToPosition()方法中调用的_root.br1.initialize()方法吗?它就在这里,把5个按钮的透明度和缩放值赋给相应的变量,并设置startNum为1(稍后介绍这样做的原因)。
btnRollOver()方法是判断btnNum的值然后作出一系列的“措施”,从而让鼠标指向的按钮变大且透明度更高,其它的则变小且透明度降低(在一定范围内,它同样采用了极限运算)。可能你要问如何通过btnNum而实现这一系列的“措施”呢?别着急,还记得舞台上那5个按钮吗,看看它们的代码(btn1为例):

on(rollOver) {
  br1.btnNum = 1;
  br1.mouseRollOver();
}
on(rollOut) {
  br1.btnNum = 0;
}


当鼠标移上去时设置btnNum为1并调用mouseRollOver()方法,移开时btnNum归零。其它四个按钮都差不多,只不过btn2移上去时设置btnNum为2、btn3移上去设置为3……以此类推。
这下你明白了吧,以鼠标移到btn1上为例:在for循环中,发现btnNum大于0,于是根据相应的值用eval函数加出MC的名字,并让它的高宽百分比值不断增加,直到初始状态+80之时,还有透明度不断自增;而另外的4个MC就高宽百分比值不断减小,直到初始状态-40之时,还有透明度自减。当鼠标移开btnNum等于0时,一切又回归初始值。它是不是很饶人?没办法,我们都是苦命的IT人……
这里我要分享一下个人经验:透明度虽然在帮助里给人的印象是个0~100的整数值,但它和角度一样表里不一!无论你用自增还是自减或者加几减几,都得到的是一个古里古怪的小数。所以以后在使用它们的时候务必小心,同时也希望Adobe能更加完善这个地方。
mouseRollOver()方法很简单了,实现btnRollOver()的setInterval。这里我们看到那个startNum的用途了,它告诉编译器只有它为1时setInterval函数才执行!之所以这样做的原因是可能按钮在移向指定位置时(LoadButton的btnToPosition()方法)你就已经用鼠标指向它了,那么结果就可能造成冲突。我在LoadButton类中设置当移动完成后才通过调用BtnRoll类的initialize()方法设置startNum为1,如此就避免了这个错误。

Orz,我终于把它讲解完了,而你是否感受到北美官网的复杂与绚丽了呢?单只一个菜单就麻烦成了这样,其它的更可想而知!由于这篇教程是面向高级用户的,所以新手可能看不懂。建议你们先打好基础或者从头看下我在本版发布的AS系列教程,那样的话估计会顺畅多了~