第4章 オブジェクトを使ったスクリプティング
LoadVarsオブジェクトを使って外部ファイルを読み込むswfだ。
https://sites.google.com/site/itouhiro/2009/20090529oop_as04_1.swf
ここで外部ファイルはテキストファイル http://ai11.net/2009/flash/20090529data.txt だ。文字コード UTF-8 でBOMはあってもなくても同じだった。
スクリプトは _rootの1フレーム目に。
//filename = "20090529data.txt" filename = "http://ai11.net/2009/flash/20090529data.txt" extData = new LoadVars(); extData.onLoad = function(isSuccess){ if(isSuccess){ yaranaio_dialog_txt.text = "わたしの名前は "+ extData.name +" です。\n戦闘力は "+ extData.power +" ですよ。"; }else{ yaranaio_dialog_txt.text = "Error: '"+filename+"' cannot load!"; } } extData.load(filename);
powerというプロパティ名は定義してないから、undefinedになるかなと思ったけど空白になるんだな。
filenameのところ、URLで指定するんだね。
URLなしだと読み込みエラーになるんだよ。
- swfとtxtを同じディレクトリに置いて
- URLなし
という条件の場合
- テスト環境では読み込みできた。
- このブログで動作させると、このブログのURLと同じディレクトリつまり http://d.hatena.ne.jp/itouhiro/20090529data.txt を読みに行ってエラーになる。
しかしテキストファイルの形式がめんどうなことになってるな。変数名が必要だし、アンパサンド & 等を実体参照(&)で書かなきゃだし。
この本には、LoadVarsオブジェクトじゃなくてXMLオブジェクトを使ったらファイルの中身をまるごと読めると書いてある。
XMLオブジェクトを使ってさっきのテキストファイル読み込むと、こう。
https://sites.google.com/site/itouhiro/2009/20090529oop_as04_2.swf
ちなみにほんもののXMLファイルを読み込んで解析すると……FlashPlayer6では動くんだがFlashPlayer9や10では動かないswfができあがった。
https://sites.google.com/site/itouhiro/2009/20090529oop_as04_3_4.swf
ソースはこれ。ActionScript(AS)1.0ではXPathが使えないので使ってない。
filename = "http://dailynews.yahoo.co.jp/fc/computer/rss.xml" extData = new XML(); //external data extData.ignoreWhite = true; var str = "■Yahoo!ニュース[コンピュータ]"; extData.onLoad = function(src_str){ // source string if(src_str != undefined){ var child_node = extData.firstChild.firstChild.childNodes; str += child_node[i].firstChild.nodeValue + "\n"; for(var i=0;i<child_node.length;i++){ var title = child_node[i].firstChild.firstChild.nodeValue; if(title == null){ str += "?"; }else{ title += "("+child_node[i].firstChild.nextSibling.nextSibling.firstChild.nodeValue+")"; str += title+"\n"; } } yaranaio_dialog_txt.text = str; trace(str); }else{ yaranaio_dialog_txt.text = "Error: '"+filename+"' cannot load!"; } } extData.load(filename);
Flashは互換性高いと思ってたけど、動かなくなるswfもあるんだね。
FlashPlayer9や10では動かない理由を探したら、FlashPlayer7以降はセキュリティが厳しくなり、swfファイルのある場所と違うドメインからはXML.load
ができなくなったからだった。
http://livedocs.adobe.com/flash/8_jp/main/wwhelp/wwhimpl/common/html/wwhelp.htm?context=LiveDocs_Parts&file=00002871.html
以下のフラッシュは、 http://ai11.net/2009/flash/20090602oop_as04_4_4.swf に本体のswfファイルがある。そこで、同じドメインの http://ai11.net/index.xml を読み込んでみると、FlashPlayer10でも確かに読み込める。
https://sites.google.com/site/itouhiro/2009/20090602oop_as04_4_4.swf
クロスドメインポリシーファイル crossdomain.xml を読み出し先(たとえばhttp://dailynews.yahoo.co.jp/)が置いてくれれば、そのサイトのXMLも読めるんだが…。
他のサイトからファイルを直接読み込めないのは、残念だなあ……。
ところでAdobeのマニュアル見ると、AS2だとインスタンスの元になるものを「オブジェクト」じゃなくて「クラス」と呼ぶようになってるんだな。
これまで、StringオブジェクトとかObjectオブジェクトとか呼んできたけど、今後はObjectクラスと呼ぶことにしよう。
Flash MXまでの「ActionScriptリファレンスガイド」では、MovieClipの先頭の項目は「MovieClip(オブジェクト)」とされていました。ところが、Flash MX 2004では、「MovieClipクラス」という名称に変わっています。……
Flash MX以前の「ActionScript辞書」で、「MovieClip(オブジェクト)」というのは、明かに「クラス」を表しています。
(http://www.fumiononaka.com/TechNotes/Flash/FN0506003.html)
第5章 イベントを捉える―リスナー
キー入力を取得するには、ObjectクラスのインスタンスにaddListenerをさせるという手がある。
キーコードを16進数で表示するぞ。一度swfをクリックしないと反応しないかも。
https://sites.google.com/site/itouhiro/2009/20090529oop_as05_3.swf
Objectクラスのインスタンスを使う理由は、MovieClipクラスと違ってonKeyDownなどのキー取得メソッドがないので、キー入力を二重に受け取る心配がないから。
このインスタンスはMovieClipじゃないから画面に表示されないけど、それでもListenerをaddできる。
ふむふむ
次はキー入力の応用で、方向キーで動くswfだ。
シフトキーを押しながら方向キーも押すと、通常の10倍のスピードで動くぞ。
https://sites.google.com/site/itouhiro/2009/20090529oop_as05_4.swf
ソースは、シンボルqilinの1フレーム目にこれ。
ls = new Object(); //listener ls.onKeyDown = function(){ move4way(Key.getCode(), Key.isDown(Key.SHIFT)); } function move4way(direction, accel){ var movestep = accel ? 10 : 1; switch(direction){ case Key.LEFT : this._x -= movestep; _root.arrow_mc.disp_arrow(180,accel); break; case Key.RIGHT: this._x += movestep; _root.arrow_mc.disp_arrow(0,accel); break; case Key.UP : this._y -= movestep; _root.arrow_mc.disp_arrow(270,accel); break; case Key.DOWN : this._y += movestep; _root.arrow_mc.disp_arrow(90,accel); break; } } Key.addListener(ls);
シンボルarrowの1フレーム目にこれ。
var alphamask = 20; this.onEnterFrame = function(){ if(alphamask > 1){ alphamask -= 10; this._alpha = alphamask; } } function disp_arrow(degree,accel){ this._rotation = degree; alphamask = accel ? 100 : 50; }
ここで発見だが、onKeyDownだけでキー入力を取得する(onKeyUpを使わない)と、OSのキーリピートが使えるんだな。これは知らなかった。
方向キー押してる途中でシフト押すと、動きが止まっちゃうね。アッドリスナーだとここらへん融通が利かないな。
次はキリンをマウスでドラッグすると矢印もあとをついて移動する。
https://sites.google.com/site/itouhiro/2009/20090529oop_as05_9.swf
動作はたいしたことないが、Listenerの仕組みで情報を伝達している。
qilinシンボルの1フレーム目にこのframe action。
this.lis = new Array(); //listeners function addListener(myObj){ this.lis.push(myObj); trace(this._name +".lis="+this.lis +"(addListener)"); } function removeListener(myObj){ for(var i=0; i<this.lis.length; i++){ if(this.lis[i] == myObj){ this.lis.splice(i,1); trace(this._name +".lis="+this.lis +"(removeListener)"); return true; } } return false; } function broadcastMsg(distance){ for(var i=0; i<this.lis.length; i++){ this.lis[i].moveTgt(distance); //move target trace(this._name +":broadcastMsg: distance="+distance); } } this.myY = _y; function myMove(){ this.broadcastMsg(this._y - this.myY); } this.onPress = function (){ this.startDrag(); this.onEnterFrame = this.myMove; } this.onRelease = this.onReleaseOutside = function(){ delete this.onEnterFrame; this.stopDrag(); }
arrowシンボルの1フレーム目に、このframe action。
this.onEnterFrame = function(){ var tgt_mc = _parent.qilin_mc; //target movieclip: same dir-level if(typeof tgt_mc == "movieclip"){ tgt_mc.addListener(this); delete this.onEnterFrame; rd = Math.random()/3.0+0.1; //random trace(this._name +": _y="+ _y +":rd="+ rd); } }; this.myY = this._y; function moveTgt(distance){ //move target this.tgt_y = this.myY + distance; trace(this._name +":moveTgt: tgt_y("+ this.tgt_y +")=this.myY("+ this.myY +") + distance("+ distance +")"); if(this.onEnterFrame == undefined){ this.onEnterFrame = this.easeOut; } } function easeOut(){ var dist = this.tgt_y - this._y; //distance if(Math.abs(dist)<0.5){ this._y = this.tgt_y; //吸着 delete this.onEnterFrame; }else{ this._y += dist*rd; //距離が近くなるほど遅くなる移動 trace(this._name +":easeOut: _y("+ this._y +")+=dist("+ dist +") * rd("+ rd +")"); } }
このコードはどう動いてるの?
このswfで操作できるのは「キリンをマウスでドラッグする」だけだ。
キリンのアクション(=スクリプト)を見ると、
- onPress(マウスクリックの押下の瞬間)のときにstartDrag()とonEnterFrameへの登録を実施。
- onRelease(マウスクリックしたあとマウスボタンから指を離す)のときに、onEnterFrameへの登録を解除して、stopDrag()する。
- onEnterFrameに登録するのは、broadcastMsg関数を実行すること。broadcastMsg関数には、現在のキリンのY座標と、このフレームに入ったとき(実行開始時)のキリンのY座標の差を引数として渡す。
- broadcastMsg関数は「リスナーに登録されたオブジェクト」すべてにmoveTgt関数を実行させる。moveTgt関数は「矢印」シンボルに定義してある。
矢印の持つアクション(script)を見ると、
- moveTgt関数では、onEnterFrameへeaseOut()を登録する(毎フレームeaseOut()を実行する)。引数に、「現在の矢印のY座標と、このフレームに入ったとき(実行開始時)の矢印のY座標」の差を引数として渡す。
うーむ……
なんか入り組んでるな。
Listener使うと、EventHandler使うより、コード量が多くなる気がする
でもAS3ではListener使うのがむしろ標準的になってしまうんだ。EventHandlerも残ってるけど、クラスやオブジェクト指向でAS3スクリプト組むならListenerのほうが都合がいいらしい。慣れとこうかな。