AS2をAS3っぽい記述に近づける工夫
- 2009 年 8月 5 日
AS2を使うとAS3がAS3を使うとAS2が頭から抜けていくワタシ。
仕事的には未だ混在な状況にありクライアントの都合に応じていかんともしがたいところもある。
冗長になりがちでまどろっこしく思っていたAS3も慣れると、今度はAS2の記述に戸惑ったりするんで出来る限り同じような記述にしたいところです。
同じようなこと考える人はいるもので、調べてみると参考になるのがありました。
AS3移行時はAS3ならこう記述するって習得していった訳だけれど、逆にAS3っぽくAS2を記述するいくつかの工夫をメモ。
■AS2でドキュメントクラス
AS3でこれを使うとメインのタイムラインにクラスを関連付けることが出来ます。
作業上タイムラインに書くより記述が整理されてスマートだと思うし、参照するにもされるにも便利な気がします。
で、探してみたらAS2でもドキュメントクラスと同様にクラスを使う方法が以下にありました。
F-site | AS2でもドキュメントクラスを設定したい
boostworthyisryantaylor » AS2: Document Class
スゴいなぁ。早速試してみる。
実行順なんかも確認したかったので適当に代入を入れながら試しています。
・タイムラインに記述
import DocumentClass; this.b = "B"; //this.__proto__ = Function( DocumentClass ).prototype; this.__proto__ = DocumentClass.prototype; Function( DocumentClass ).apply( this, null ); this.d = "D"; this.field.text += "\n" + "a:" + this.a; this.field.text += "\n" + "b:" + this.b; this.field.text += "\n" + "c:" + this.c(); |
- Function( DocumentClass )って、AS3でよく使うキャストしているってことだよね。
- タイムラインの__proto__への代入はクラスのprototypeでも割りあたるっぽい。
- 既に初期化されてるタイムラインに対し、コンストラクタが実行出来ないから関数に変換してスコープ渡して実行しているってことだよね。
・ドキュメントクラスファイル
class DocumentClass extends MovieClip{ public function DocumentClass(){ this.d = "d"; this.field = this.createTextField( "fielda", this.getNextHighestDepth(),0,0,0,0 ); this.field.autoSize = true; this.field.selectable = false; this.field.setNewTextFormat( new TextFormat( "_ゴシック", 12 ) ); this.field.text = "DocumentClass"; this.onLoad = function():Void{ this.field.text += "\n" + "onload"; this.field.text += "\n" + "d:" + this.d; } } public var field:TextField; public var a:String = "a"; public var b:String = "b"; public var d:String; public function c():String{ return "c"; } } |
- MovieClipを継承させているけど、他のオブジェクトも継承出来る。
- その場合、クラスファイルからは継承したオブジェクトに無いプロパティはエラーになるけど、タイムライン側からはエラーにならない。(例:Dateを継承させたら、_xはクラスファイルではエラー、タイムラインでは有効)
- onLoad動くんだなぁとか、apply実行後、タイムラインから期待どおりドキュメントクラスの変数が参照出来てるところがポイント。
■AS2でマウスイベントリスナー
MovieClipのマウスイベントは、AS3だとリスナーを設定して処理するけれどAS2だとあらかじめ設定されたハンドラに関数を定義します。楽なんだけれど一つのイベントに対して二つ以上のイベントを設定したい時は不便だ。
前にもちょっと調べたEventDispatcherなど使って、ちまちまと設定していけば対処できるけど楽な方法がありました。
as2.0でas3.0的イベント実装方法 | blog.ks-product.com
LowLevelEvents クラス
UIEventDispatcher クラス
拡張されるイベントについては、
UIObject クラス
これも試してみる。
先ほどのDocumentClassのコンストラクタでSquareをアタッチ。
public function DocumentClass(){ this.d = "d"; this.field = this.createTextField( "fielda", this.getNextHighestDepth(),0,0,0,0 ); this.field.autoSize = true; this.field.selectable = false; this.field.setNewTextFormat( new TextFormat( "_ゴシック", 12 ) ); this.field.text = "DocumentClass"; this.onLoad = function():Void{ this.field.text += "¥n" + "onload"; this.field.text += "¥n" + "d:" + this.d; } this.square = this.attachMovie( "Square", "square1", this.getNextHighestDepth(), { _x:Stage.width/2, _y:Stage.height/2 } ); } |
Squareクラスはこんな感じで拡張。
import mx.events.LowLevelEvents; import mx.events.UIEventDispatcher; import flash.geom.ColorTransform; class Square extends MovieClip{ public function Square(){ LowLevelEvents; UIEventDispatcher.initialize(this); this.field = this.createTextField( "field", this.getNextHighestDepth(), 0,-12,0,0 ); this.field.autoSize = "center"; this.field.selectable = false; var format:TextFormat = new TextFormat( "_ゴシック", 10 ); format.align = "center"; this.field.setNewTextFormat( format ); this["addEventListener"]("mouseDown", this.mosueDownHandler); this["addEventListener"]("mouseDown", this); this["addEventListener"]("mouseUp", this); this["addEventListener"]("mouseMove", this); this["addEventListener"]("mouseOver", this); this["addEventListener"]("mouseOut", this); this["addEventListener"]("mouseDownSomewhere", this); this["addEventListener"]("mouseUpSomewhere", this); this["addEventListener"]("mouseMoveSomewhere", this); this["addEventListener"]("mouseChangeSomewhere", this); this["dispatchEvent"]( { type:"mouseDown", target:"ターゲット" } ) } public var field:TextField; public var bg:MovieClip; public function mosueDownHandler(e:Object):Void{ var trans:ColorTransform = new ColorTransform(); trans.rgb = 0xffffff * Math.random(); this.bg.transform.colorTransform = trans; } public function mouseDown(e:Object):Void{ this.field.text = e.type; this.field.text += "\n" + e.target; } public function mouseUp(e:Object):Void{ this.field.text = e.type; this.field.text += "\n" + e.target; } public function mouseMove(e:Object):Void{ this.field.text = e.type; this.field.text += "\n" + e.target; } public function mouseOver(e:Object):Void{ this.field.text = e.type; this.field.text += "\n" + e.target; } public function mouseOut(e:Object):Void{ this.field.text = e.type; this.field.text += "\n" + e.target; } public function mouseDownSomewhere(e:Object):Void{ this.field.text = e.type; this.field.text += "\n" + e.target; } public function mouseUpSomewhere(e:Object):Void{ this.field.text = e.type; this.field.text += "\n" + e.target; } public function mouseMoveSomewhere(e:Object):Void{ this.field.text = e.type; this.field.text += "\n" + e.target; } public function mouseChangeSomewhere(e:Object):Void{ this.field.text = e.type; this.field.text += "\n" + e.target; } } |
- LowLevelEventsを宣言して、UIEventDispatcher.initialize(this)で初期化すればマウスイベントが拡張される。
- コンストラクタ内だと拡張されたメソッドがドット演算子で参照できないっぽい。配列演算子で参照できた。
- mouseMoveSomewhere、mouseChangeSomewhereってこの場合発生しないのかな。
- イベントオブジェクトとしてtypeとtargetが入ってくる。
- dispatchEventでイベント送信可能。受け取って欲しいtype持つオブジェクトを引数に渡す。
- リスナーはオブジェクトでも関数でも渡せるのがポイント。mouseDownSomewhere、mouseUpSomewhereは通常のムービークリップに無いイベントだなぁ。
■ Delegateでスコープの変更
関数内のスコープ。これAS2に戻った時よく混乱する。AS2だとハンドラに設定した関数のスコープはハンドラが設定されているオブジェクトになるけど、AS3は関数が定義されているオブジェクトになる。これにはDelegateクラスがいい感じ。
Delegate クラス
FN0408001 – イベントの委譲 – Flash : テクニカルノート
先のドキュメントクラスを変更して試します。
import mx.utils.Delegate; class DocumentClass extends MovieClip{ public static var Field:TextField; public function DocumentClass(){ Field = this.createTextField( "fielda", this.getNextHighestDepth(),Stage.width / 2,10,0,0 ); Field.autoSize = "center"; Field.selectable = false; Field.setNewTextFormat( new TextFormat( "_ゴシック", 12 ) ); var square1:MovieClip = this.attachMovie( "Square", "square1", this.getNextHighestDepth(), { _x:Stage.width/6 * 2, _y:Stage.height/6 * 2 } ); var square2:MovieClip = this.attachMovie( "Square", "square2", this.getNextHighestDepth(), { _x:Stage.width/6 * 4, _y:Stage.height/6 * 2 } ); var square3:MovieClip = this.attachMovie( "Square", "square3", this.getNextHighestDepth(), { _x:Stage.width/6 * 2, _y:Stage.height/6 * 4 } ); var square4:MovieClip = this.attachMovie( "Square", "square4", this.getNextHighestDepth(), { _x:Stage.width/6 * 4, _y:Stage.height/6 * 4 } ); square1.addEventListener( "mouseDown", mouseDownHandler ); square2.addEventListener( "mouseDown", Delegate.create( this, mouseDownHandler ) ); square3.onPress = this.onPressHandler; square4.onPress = Delegate.create( this, onPressHandler ); } public function mouseDownHandler(e:Object):Void{ Field.text = String( this ); } public function onPressHandler():Void{ Field.text = String( this ); } } |
- Delegate.create( スコープ, 関数 )として、関数のスコープを設定します。
サンプル:Delegateでスコープの変更(上記の出力結果)
- Delegateを設定した関数のスコープは変更されているのが確認できる。
■深度
AS2だと基本インスタンス追加時に深度を指定するには、数値を指定するか、getNextHighestDepthメソッドを使うって感じですよね。
大抵あとから登場するものは、上に重なることが多いのでgetNextHighestDepth()を多用するのだけれど、マニュアルにもある通りコンポーネントがあるときは深度が正しく設定されないのでDepthManagerを使います。このDepthManagerなら一番下の深度に配置するとか、オブジェクトとオブジェクトの深度に移動させるとか出来る。
コンポーネントを使わない場合にもDepthManagerを使う方法を野中先生が説明されております。
DepthManager.createChildAtDepthが動作しない
DepthManager
これも先のドキュメントクラスを変更して試します。
import mx.managers.DepthManager; import mx.core.ext.UIObjectExtensions; class DocumentClass extends MovieClip{ public function DocumentClass(){ UIObjectExtensions; var square1:MovieClip = this.attachMovie( "Square", "square5", 198, { _name:"square1", _x:60 + 110 * 0, _y:60 + 110 * 0 } ); var square2:MovieClip = this["createChildAtDepth"]( "Square", DepthManager.kTopmost, { _name:"square2", _x:60 + 110 * 1, _y:60 + 110 * 0 } ); var square3:MovieClip = this["createChildAtDepth"]( "Square", DepthManager.kBottom, { _name:"square3", _x:60 + 110 * 2, _y:60 + 110 * 0 } ); var square4:MovieClip = this["createChildAtDepth"]( "Square", DepthManager.kBottom, { _name:"square4", _x:60 + 110 * 0, _y:60 + 110 * 1 } ); var square5:MovieClip = this.attachMovie( "Square", "square5", 199, { _name:"square5", _x:60 + 110 * 1, _y:60 + 110 * 1 } ); var square6:MovieClip = this["createChildAtDepth"]( "Square", DepthManager.kTop, { _name:"square6", _x:60 + 110 * 2, _y:60 + 110 * 1 } ); var square7:MovieClip = this["createChildAtDepth"]( "Square", DepthManager.kTopmost, { _name:"square7", _x:60 + 110 * 0, _y:60 + 110 * 2 } ); var square8:MovieClip = this.attachMovie( "Square", "square8", this.getNextHighestDepth(), { _name:"square8", _x:60 + 110 * 1, _y:60 + 110 * 2 } ); var square9:MovieClip = this.attachMovie( "Square", "square9", this.getNextHighestDepth(), { _name:"square9", _x:60 + 110 * 2, _y:60 + 110 * 2 } ); square8.setDepthBelow( square5 ); square1.field.text = square1._name + "\n" + square1.getDepth(); square2.field.text = square2._name + "\n" + square2.getDepth(); square3.field.text = square3._name + "\n" + square3.getDepth(); square4.field.text = square4._name + "\n" + square4.getDepth(); square5.field.text = square5._name + "\n" + square5.getDepth(); square6.field.text = square6._name + "\n" + square6.getDepth(); square7.field.text = square7._name + "\n" + square7.getDepth(); square8.field.text = square8._name + "\n" + square8.getDepth(); square9.field.text = square9._name + "\n" + square9.getDepth(); } } |
- DepthManagerだけだと駄目で、UIObjectExtensionsもインポートして記述しないと動かないです。
- これもコンストラクタ内でドット演算子でメソッドの参照ができませんでした。
- わざとattachMovieで深度指定も入れてみたのだけれど相対的な深度関係が維持されている様子。
- 赤いのはステージに直接置いたムービークリップ。これも管理対象になるようですね。
- DepthManagerを使った時のgetNextHighestDepth()は1048576以上になる。
誤りがあるかもしれません。ご参考程度に。