实用Flash应用程序—打字练习


  Flash的ActionScript给flash的功能增强提供了更大的空间,有了强大的编程功能作后盾,广大的Flasher终于可以振臂一挥,向世人说明:Flash不仅仅只是网络动画工具,出色的闪客一样可以用Flash做出实用的程序。

  今天我们就一起来看看怎样用Flash制作打字练习的实用程序。

  第1节、程序概述

  程序名为:TypeTest。整个程序的目的就是鼓励玩家不停的敲击键盘,打印出与原文一样的的文章或字符,达到熟练键盘,快速输入的目的。作品效果以及源文件下载

  在这篇教程的每一节,都会向程序中添加新的功能或特点,这样我们可以循序渐进地学习和讨论用到的技术,承前启后,保证你能完全理解整个进程,轻松掌握每一节内容。

  编写的ActionScript需要完成下面这些事情:

  让玩家对练习内容、练习时间及难易程度都有充分的控制权力;
  提示每次键盘输入是否正确;每次练习结束会有成绩显示。

  第2节、预备知识

  学会从外置的文本文件中引入变量:

  TypeTest的玩家总是一边不停的敲击键盘,一边参照原文内容。

  原文从何而来呢?我们设原文是字符型变量。既然是变量,那么变量的保存方式应该有以下几种。

  1.放在ActionScript中,并将其作为Flash电影的一部分。
  2.用外置的文本文件保存,从而可以被Flash电影所引用。
  3.保存为数据库文件,从而也可被Flash电影所引用。

  从现在我们程序的目标而言,选用第二个方案有着不可比拟的优势:

  原文的更新将会比ActionScript变量形似书写简单——只需要更改外置文本文件即可,而不需修改源代码,使玩家能够更新自己的练习库。又没有牵涉到数据库等一大堆复杂的内容。要被引用的文本文件的格式必须是 变量名= 这样的形式,如果一个文本文件中有多个变量必须用 & 符号分开。

  例如:Variable1=typeTest&Variable2=12344Variables3=English text file . 记住,所有的变量都是字符型的变量,如果要用在数字运算中我建议使用函数 Number(Variables2). 该函数返回 Variables2 所代表的数字 12344 。导入变量应使用函数:loadVariablesNum()。例如: fileName="English.txt"; loadVariablesNum(fileName,level); 从文本文件 English.txt 中导入变量到 Flash电影的level层中, level 大于等于0的整型数据。我这里把数据都导入到第 0 层中。即 loadVariablesNum(fileName,0)。

  Ok,预备知识到此结束,下面开始我们激动人心的程序吧!

  第3节、制作练习场景

  效果如图1 所示

  制作过程: 1.新增一个名为 typeSpaceMovie 的电影剪辑

  2.在 typeSpaceMovie 新增一图层,添加两个文字区域 /:text1 和 /:text2 . 并设置他们的大小, 和字体,字符大小完全一样。 在变量名前加 /: 表示这是告诉Flash这个变量在主 TimeLine 中也是可用的,而不仅仅是typeSpaceMovie 电影剪辑中的局部变量。

  第3节.制作练习场景 效果如图1 所示。实例(源文件)制作过程:

  1.新增一个名为 typeSpaceMovie 的电影剪辑

  2.在 typeSpaceMovie 新增一图层,添加两个文字区域 /:text1 和 /:text2 . 并设置他们的大小, 和字体,字符大小完全一样。 在变量名前加 /: 表示这是告诉Flash这个变量在主 TimeLine 中也是可用的,而不仅仅是typeSpaceMovie 电影剪辑中的局部变量。图1


图 1

  3.设置text1属性为“动态文本区域,多行,自动换行”。设置 text2属性为“输入文本、多行、自动换行”。

  4.回到主场景,引用电影剪辑 typeSpaceMovie 到主场景中, 并在text1中显示原文内容。 引用电影剪辑并将其显示到屏幕上可以使用 attachMovie 来完成这项任务。不过在引用之前,要正确的设置库。 选中库中的某个Symbol(元件),然后在库窗口的Options(选项)菜单中选择Linkage...(联接)。在 Symbol Linkage Properities (符号连接属性)对话框中选择 Export this Symbol (导出这个符号), 然后赋予它一个标志符。 这ActionScript 就能通过这个标着符访问这个被导出的元件了。例如 :我们把电影剪辑 typeSpaceMovie 的标志符 设为 typeSpaceM 再 通 过attachMovie("typeSpaceM","typeSpace",depth); 这条命令将符号 typeSpaceM复制到主场景上,命名为typeSpace,并给其设定一个深度值 depth(整数型变量)。 Flash必须为每一个电影剪辑分配一个深度值。当多个电影在屏幕上重叠时,具有最高深度值的电影剪辑将会显示在最上面。并且同个层只能有一个剪辑,如果尝试使用一个已存在的层次,新的电影剪辑将会取代原有的。

  5.整理代码:主场景只有一各关键帧: attachMovie( "typeSpaceM","typeSpace",0 ); loadVariablesNum("English.txt",0); stop();3.6.预览结果,哈!在text1 文字区域显示了 English.txt中的变量, text2文字区域可以进行键盘输入了。到此,练习场景的制作学习完毕。

  第4节、菜单选项和成绩判断

  预览上一节的成果,好像太单调了点。现在我们将做出类似于 windows 应用程序那样的菜单选项,增添以下效果:

  1.让玩家自己可以选择练习内容和时间。
  2.甚至可以自己拟定练习的难易程度。
  3.能判断玩家每次输入是否正确,并发出提示。
  4.统计最后成绩。

  4.1 创建声音效果

  4.1.1.从flash以外导入声音文件。

  通过:文件/导入... ,打开你想要的声音文件,Flash5支持的格式有 .wav 和.mp3。导入后的 .wav 文件会被Flash压缩, 即变成mp3格式。导入后,这个声音文件的一份被压缩的拷贝将出现你的元件库里。 同样, 选取这个声音元件,联接它,为它设一个标志符,使其在 ActionScript 中可用。我们导入了两个声音: error.wav 和 type.wav.

  4.1.2.创建声音对象并播放。必须创建声音对象方可使用。

  error = new Sound() // 创建一个声音对象;
  error.attachSound("errorSound"); //将输出符号为errorSound 的声音拷贝给声音对象error;
  error.start(begin,LoopNum);//播放声音error,参数begin表示开始 播放的开始时间(单位秒) //参数LoopNum表示循环播放的次数

  4.1.2 判断当前玩家输入是否正确;

  在主场景中用以下代码判断每次输入是否正确:

  var tempText1=text1.charAt(Selection.getBeginIndex()-1);
  var tempText2=text2.charAt(Selection.getBeginIndex()-1);
  if(tempText1!=tempText2 && textLength!=text2.length)
  { //当前输入错误时应触发的事件;
    error.start(0,1); // 报警一次;
    textLength=text2.length;//保证每按键一次只判断一次
  }

  if(tempText1!=tempText2 && textLength!=text2.length)
  { //当前输入正确时,也应该有提示
    type.start(0,1);// 提示正确;
    textLength=text2.length;
  }

  这段代码有点复杂,但务必要理解,本程序的核心代码就在此了。函数 charAt的原型是 myString.charAt(index);返回字符串 myString中由 index参数 指定位置的字符。

  如果index 不是一个 从0到字符串长度减 1的数,就返回字符串。

  属性length: 原型String.length . 返回字符串String的长度。 Selection: 它是个对象,让你设置和控制当前焦点可编辑文本域。当前聚焦的可编辑文本域是指用户的鼠标目前选中的可编辑文本区域。一次只允许有一个当前焦点可编辑文本域。所以,Selection对象不用创建。 Selection.getBeginIndex():返回光标位置。

  var:用于声明局部变量;这下初见端倪了吧。局部变量tempText2返回的是text2文本区域当前光标闪动位置的字符。tempText2返回的是文本区域text2相应位置上的字符。每当键盘输入一次,text2.length也会变化一次,我们在条件语句 if中添加代码 &&textLength!=text2.length 保证了只有键盘输入时才 判断tempText1和tempText2是否相等,达到了及时判断正误的效果。

  4.2创作菜单,及菜单间的转换

  友好的界面是程序重要的组成部分。我们预制了一个背景电影剪辑,两个菜单电影剪辑和一个typeSpace电影剪辑。


图2

  4.2.1 背景如图2所示。它主要的功能是作为下面两个电影剪辑menuE和 menuSettingE的背景。另外,还有一个按钮Exit,用于退出此程序。

  on (release)
  {
    fscommand ("quit");
  }


图 3

  4.2.2 如上图3:按钮Setting :

  on (release)
  {
    /:showType = "menuSetting";
  }

  showType也是个全局变量,在程序主场景的第一帧就给其赋予初值。showType用于菜单menuE和菜单 menuSettingE之间的切换, 当按下并释放按钮Setting ,showType就等于"menuSetting". 当主场景侦测到这个变化,就立刻切换到菜单 menuSetting.同理,按钮Starttest响应的事件也只是为了切换到显示typeSpace的场景上。同时还设置其他一些全局变量。

  on (release)
  {
    /:showFram = false;  //让背景关闭
    /:showTest = true;  //显示typeSpace
    /:nowTimer = getTimer();  //开始一次计时
    /:startTimer = true;  //计时已经开始
    /:loadText=true;  //开始从外部文本导入变量(原文)
  }

 
图 4

  4.2.3 图 4显示了电影剪辑menuSettingE的效果。这里就是玩家设定练习时间和选择原文的场面。代码比较简单,就不一一作介绍了。

 
图 5 typeSpace

  4.2.4  图5 显示typeSpace。这才是整个程序的核心。

  在这个剪辑中,最重要的是提供了练习的场所和向玩家显示剩余时长。前面提到过函数 getTimer(); 他返回此Flash程序从开始到调用他所经历的时长。因此,在主场景第一帧就要调用一次,在开始练习时又不停的调用,直到二者的差值达到预先设定的时长。

 
图 6

  4.2.5 图 6是本程序最后一个 电影剪辑 showScores;不言而喻,它用于在每次练习结束后返回成绩。

  5、场景中帧的划分

  把主场景划分为4段循环的帧结构。

  4.2.5.1.第一帧,用于初始化全局变量。

  fscommand ("fullscreen", "true");
  fscommand ("showmenu", "false");
  baseDepth = 1;
  testDepth = 10;
  menuDepth = 2;
  menux = 118;//菜单的横坐标
  menuy = 102;//菜单的纵坐标
  timeBarWidth = 76;//时间棒的总长度
  startTimer = false;
  typePause = false;
  nowTimer = getTimer();//计时一次
  showType = "menu"; //显示菜单
  menushowFram = true;// 显示背景
  backToFram = false;//是否返回到开始界面
  loadText = false;//是否开始倒入原文
  level = 1;//难度级别为1
  text1 = "";
  text2 = "";
  textLength = 0;
  loopNum = 0;

  back = false;
  again = false;
  timeOver = false;
  typeSound = new Sound();
  typeSound.attachSound("type");
  errorSound = new Sound();
  errorSound.attachSound("error");
  typeSoundPlay = true;time = 3;
  errorNum = 0;
  speedNum = 0;
  choose = new String("normalText");
  inputText = choose;
  attachMovie("fram", "fram", baseDepth);//显示开始界面的背景

  4.2.5.2  第二帧到第三帧;

  第2帧设一个标签“mainLoop":

  if(loadText==true)
    gotoAndPlay("loadNum");//跳到下载原文的循环中
  if (showFram == false)
  {
    fram.removeMovieClip();//把背景从屏幕上抹去
    removeMovieClip (showType);//把菜单从屏幕上抹去
    attachMovie("typeSpace", "typeSpace", testDepth);
    gotoAndPlay ("typeLoop");//跳到练习的场景中
  } else
  {
    attachMovie(showType, showType, menuDepth);//跳到另一个菜单中
  }
  _root[showType]._x = menux;//设置横坐标
  _root[showType]._y = menuy;//设置纵坐标第3帧的代码:
  gotoAndPlay("mainLoop");

  4.2.5.3第3个循环段:

  从第5帧到第6帧。

  第5帧标签“typeLoop";

  if (backToFram == true)
  {  //响应在typeSpace剪辑上的按钮BACK的事件返回到开始界面
    removeMovieClip ("typeSpace");
    gotoAndPlay (1);
  }
  if(again==true)//响应在typeSpace剪辑上的按钮AGAIN的事件
  {  //场景不变,重复上一次的练习
    removeMovieClip("showScores");
    text1.scroll=1;
    text2="";
    loopNum=0;
    again=false;
  }//以下的代码是用于练习过程中实时判断,检测我们分为几个小节进行分析。

  1.让原文文本向上卷动。

  if(Key.isDown(Key.PGDN))
  {  
    text1.scroll+=1;
  }
  if(Key.isDown(Key.PGUP)){
    text1.scroll-=1;
  }

  2.排除SHIFT键码错误。

  SHIFT用于大小写转换,但他也有自己的键码值。所以我们必须屏蔽掉SHIFT的返回值:

  if(Key.getCode() !=Key.SHIFT )
  {//屏蔽掉SHIFT的返回值后
    var tempText1=text1.charAt(Selection.getBeginIndex()-1);
    var tempText2=text2.charAt(Selection.getBeginIndex()-1);
    if(tempText1 !=tempText2 && textLength1!=text2.length)
    {
      if(Key.getCode() != Key.BACKSPACE)
      {
        errorSound.start(0,1);
      }
      textLength1=text2.length;
    }
    if(tempText1 ==tempText2 && textLength1!=text2.length)
    {
       textLength1=text2.length;
      if(typeSoundPlay==true)
      {
        typeSound.start(0,1);
      }
    }
  } // end of : if(Key.getCode() != Key.SHIFT)

  3.判断何时结束一次练习,并进行成绩统计

  if (tempTime-nowTimer>=(time*60000))
  {//时间结束
    timeOver=true;//事件结束标志符
    _root.typeSpace.scores =int( (text2.length*(1+ loopNum))/(time) );//显示成绩
    speedNum=_root.typeSpace.scores;if(back==false)
    {  //响应练习结束后没按下BACK按钮的事件
      attachMovie( "showScores","showScores",10+testDepth );
    }
    showScores._x=138;showScores._y=144;
    for(i=0;i<TEXT2.LENGTH;I++)< p>
    { //show the error Num
      if( text1.charAt(i) !=text2.charAt(i) )
      {
        errorNum++;//统计错误
      }
    }
    _root.typeSpace.error=errorNum;
    stop ();//时间到就让程序停止在此
    if(back==true)//响应练习结束后按下BACK按钮的事件
    {
      removeMovieClip("typeSpace");
      removeMovieClip("showScores");
      gotoAndPlay(1);
      back=false;
    }
  }// end of if (tempTime-nowTimer>=(time*600))

  4.让时间棒准确的显示剩余时长:

  _root.typeSpace.timeBar._width = (1-(tempTime- nowTimer)/(60000*time))*timeBarWidth;

  第6帧,标签“typeEnd" gotoAndPlay("typeLoop");

  4.2.5.4

  第4个循环段第8帧到第10帧.

  第8帧 标签“loadNum"

  text1="load...";
  ranFile=random(4);
  if(choose=="normalText")
  {  //导入文本的路径及其名称
    inputTextinputText= choose add "/" add level add random(4) add ".txt";
  }
  elseinputText=choose add "/" add random(4) add ".txt";loadVariablesNum(inputText,0);

  第9帧 标签“empty"

  第10帧 标签“loadNumEnd"

  if(text1=="load...")
  {
    gotoAndPlay("empty");
    trace("gotoAnd");
  }
  else
  {
    loadText=false;
    trace("load over");
    gotoAndPlay("mainLoop");
  }

  5.结束语

  到此程序分析完毕。全部代码总共也只有100行左右。发布成的可执行文件大小也不过30k左右。让人想到了在可爱的dos时代,一张软盘行遍天下的历史。Flash编程的特点就是要兼顾帧和层的概念,控制好帧的移动,就控制了程序的走向;掌握了“层”就是掌握了Flash动画技术的精髓。