- ベストアンサー
AS3 記述方法
ボタンが複数存在し、それぞれのボタンの処理をまとめたい時に どうするか考えています。 たとえばステージに3つのボタンインスタンス 「btn1」「btn2」「btn3」があるとします。 クリックすると、それぞれに応じた値を得るために ------------------------------------------------------------- btn1.addEventListener(MouseEvent.CLICK,syori1); function syori1(eventObject:MouseEvent):void{ trace("あんぱん"); } btn2.addEventListener(MouseEvent.CLICK,syori2); function syori2(eventObject:MouseEvent):void{ trace("しょくぱん"); } btn3.addEventListener(MouseEvent.CLICK,syori3); function syori3(eventObject:MouseEvent):void{ trace("カレーぱん"); } ------------------------------------------------------------- としているのですが、 たとえばもしこれが100個のボタンがあれば 100回同じような事を記述しないといけないので あまりよろしくないな・・・と思い、 ------------------------------------------------------------- for(var i=1; i<=3 i++){ this["btn"+i].addEventListener(MouseEvent.CLICK,syori); } function syori(eventObject:MouseEvent):void{ trace("ジャム"); } ------------------------------------------------------------- としたのですが、これではどのボタンをクリックしても"ジャム"がトレースされます。(当然ですが・・・) このような時、まとめて記述するにはどうすればよいでしょうか?
- みんなの回答 (5)
- 専門家の回答
質問者が選んだベストアンサー
「クリックした時にある文字列を表示する」といった、動作自体はどのボタンも共通で表示する内容だけが異なるのであれば、パラメータだけを持つコンポーネントにしてはいかがでしょう。 コンポーネントと言うと、Flash に最初から付いているスクロールバー等の UI コンポーネントや、FLV を再生する FLVPlayback コンポーネントが有名です。 これらはパラメータの他に複雑なスクリプトを組み込んで高度な機能を実現しているので難しいものに見えますが、コンポーネントの本質は、”編集可能なパラメータを持っているムービークリップ”というだけです。 Flash に入っているコンポーネントのような立派な機能を作らなくても、ムービークリップシンボルにコンポーネントパラメータを定義すれば、コンポーネントのでき上がりなのです。 コンポーネントパラメータは、普通のムービークリップが持っている変数などと同様に インスタンスの参照.パラメータの変数名 ↑このような形でスクリプト内で利用できます。 また、スクリプトをいじらなくても、ムービーの編集中に「パラメータ」パネルで値を自由に編集できることも、コンポーネントの利点の1つです。 --------------------------------------------------------- 作り方の一例です。 ムービークリップシンボルを作り、「ライブラリ」でシンボル名を選択した状態で右クリックのメニューから「コンポーネント定義...」を選び、「コンポーネント定義」パネルを開きます。 今回は単純にパラメータを定義するだけなので、「パラメータ:」の項目だけ見てください。 パラメータの定義に関する項目は次の通りです。 ・+と-ボタン +ボタンでパラメータを追加、-ボタンで削除します。 ・名前 「パラメータ」パネルでパラメータ名として表示される名前です。 日本語でも構いません。 ・変数 コンポーネントパラメータの実体となる変数の名前です。 スクリプト内で使いますので、ActionScript の文法に従って付けてください。 ・値 パラメータに設定されるデフォルトの値です。 ・タイプ パラメータのデータ型です。 今回は String 型のパラメータを1つ追加します。 +ボタンを1回押して新規にパラメータを追加し、変数とタイプを設定してください。 ここではさしあたって、「変数」の名前を” item_name ”とします。「タイプ」は” String ”を選びます。 「名前」や「値」は必要に応じて設定してください。 これでコンポーネントシンボルは完成です。 コンポーネントシンボルができましたら、普通のムービークリップと同様にステージにインスタンスを配置して選択し、「パラメータ」パネル(通常は「プロパティ」パネルとタブになって並んでいます)を開いてください。コンポーネントが持っているパラメータが表示され、値を編集することができます。 左側が「コンポーネント定義」パネルの「名前」で付けた名前、右側が実際の値です。右側をクリックすると値を編集できます。 いくつかインスタンスを作って、それぞれ何か値を設定してみてください。 コンポーネントもスクリプトで扱う以上は、ムービークリップ同様にインスタンス名が必要です。おなじみの手法ですが、共通の名前+連番で名前を付けてください。 ここでは仮に、インスタンス名を btn1 ~ btn3 とします。 以上で準備は完了です。 次はスクリプトを書きます。 ************************************* ステージに、コンポーネントパラメータ” item_name ”を持つコンポーネントシンボルのインスタンス btn1 ~ btn3 があるものとします。 各インスタンスをクリックした時にパラメータの内容を表示するスクリプトは、次のようになります。 このスクリプトは、インスタンスが存在するタイムラインのフレームに記述してください。 (↓各行頭に全角のスペースが入っています。コピーする際は、全て半角のスペースかタブに置き換えてください) /**************************************************/ //クリック時に実行する処理 function Item_Disp( event_obj:MouseEvent ):void { //クリックされたコンポーネントが持っている //item_nameパラメータの内容を出力 trace( event_obj.target.item_name ); } //イベントリスナーの登録 for( var i:int = 1 ; i <= 3 ; i++ ) { this[ "btn" + i ].addEventListener( MouseEvent.CLICK , Item_Disp ); } /**************************************************/ 「ムービープレビュー」で動作を確認してみてください。 クリックした時に「パラメータ」パネルで設定した通りの内容が表示されれば、成功です。 ムービークリップにパラメータを定義するとは、このシンボルから作られたどのインスタンスにも、同じ名前・同じ規格のソケットを持たせるようなものです。 ソケットの規格に合うものでさえあれば、インスタンスごとにどんな値を設定しても構わないのです。 実は、ソケットの名前も規格も同じならば、もとになったシンボルが違っていても、スクリプト上では同じように扱うことができます。 今回は3つとも同じシンボルから作ったインスタンスを利用しましたが、例えば異なるシンボルのインスタンスがいくつあっても、シンボルに” item_name ”という String 型のコンポーネントパラメータさえ定義しておけば、上記のスクリプトの for ループの回数を変えるだけで済みます。 もっとも、このような使い方が正しいかどうかはまた別の話です。コンポーネントの理念からすれば、外観だけを差し替えられるようなシンボルを設計するのが、正しいコンポーネントの使い方なのかもしれません。 --------------------------------------------------------- 一般的に「同じ動作で必要な値だけが違う」という処理は、関数に引数を渡すことで実装します。 しかし、イベント発生時に呼び出される関数には、ユーザーから引数を渡すことができません。 としますと、引数に代わる方法でイベント処理関数にパラメータを渡せるのなら、ボタンの数だけ関数を作る必要はなくなるわけです。 今回はコンポーネントパラメータを使いましたが、ムービークリップと表示する値をパラメータとして受け取り、”指定のムービークリップをクリックした時に指定された値を表示する”といったクラスを作る方法もあるかと思います。 コンポーネントにはクラスを組み込むこともできます。 今回はコンポーネントのパラメータ機能だけを借りた作例でしたけれど、クラスを組み込んでフレームアクションで行っている処理と同様の機能を実装すれば、いっぱしのコンポーネントになります。 クラスやコンポーネントについては私も勉強中で詳しくは説明できませんが、興味がありましたら研究してみてください。
その他の回答 (4)
- BlurFiltan
- ベストアンサー率91% (1611/1754)
#1&3 です。 > どういう手順でどういう処理が行われているかつかめまていません。 > > 特にreturn functionの部分が・・・。 return での戻り値が その function なんですよ。 ですから, addEventListener(MouseEvent.CLICK,syori(i)); の syori(i) の部分に,匿名関数 function (eventObject:MouseEvent):void{ trace(hikisu); }; が戻るんです 【図】↓。 とりあえずここまでが最低ライン理解する部分です。 これだけを見ると, addEventListener() の第2引数に匿名関数(無名関数)を入れただけに見えます。 第2引数に匿名関数を入れた例は,そこらじゅうにたくさんありますが, たとえば次のページとかもその例です。 http://hakuhin.hp.infoseek.co.jp/main/as3/event.html#EVENT_02 ただ, 単に 第2引数に匿名関数 を入れた場合とは違って, syori(hikisu) の引数をちゃんと受け継いだ形で,syori(i) の部分に関数が戻ります。 したがって,各ボタンで別々の i が動作します。 #4の方の > ActionScript 1.0 や 2.0 でよくある失敗に、 … … 以下の説明がその点について詳しいと思います。
- DPE
- ベストアンサー率85% (666/776)
#2です。 おもしろいですね、こんな方法があったとは。 まだまだ勉強が足りませんでした ^^ゞ ところで、この方法ですが。 何かの数値や文字列などの値が入っている変数(例えば for ループのループカウンタに使った i など)を addEventListener の第2引数で呼び出す関数に渡すと、”その時の変数の値”がイベント処理関数でも使用できるようです。 変数の”参照”が渡っているのではないらしく、addEventListener を実行した後に変数の値を書き換えても、イベント発生時には前の値が使用されます。 例えば /*********************************************/ var item:String = "ジャムパン"; function Syori( hikisu ):Function { return function ( event_obj:MouseEvent ):void { trace( hikisu ); }; } //イベントリスナーの登録 for( var i:int = 1 ; i <= 3 ; i++ ) { this[ "btn" + i ].addEventListener( MouseEvent.CLICK , Syori( item ) ); } //itemの値を変更する item = "クリームパン"; /*********************************************/ ↑ addEventListener が実行される時点での item の値は”ジャムパン”ですが、実行後は”クリームパン”に変わります。 プレビューして btn1 ~ 3 をクリックしてみると、現時点の値である”クリームパン”ではなく、addEventListener を実行した時点での値だった”ジャムパン”が出力されます。 つまり、Syori 関数を呼び出した時の item の値が、そのままイベント処理関数にも渡されるということです。 だとすれば、for ループの中で i の値を Syori 関数に渡しても、イベント発生時に addEventListener を実行した時点での i の値が使用できる点も説明がつきます。for ループのカウンタを使った例は私も試してみましたけれど、ループが実行された時の値である 1 ~ 3 の値が各ボタンのイベント処理関数に渡っていました。 ActionScript 1.0 や 2.0 でよくある失敗に、 for( i = 1 ; i <= 3 ; i++ ) { this[ "btn" + i ].onPress = function() { gotoAndStop( i ); }; } といった例があります。 onPress に登録される関数内の i は「 i の値を見に行け」という指示ですから、実際に実行されるときには”この関数が呼び出された時点”での i の値が採用されます。 従って、どのボタンを押しても最終的な i の値である 4 が採用され、フレーム 4 に移動しようとすることになります。 先の方法はこの話と異なり、Syori 関数に渡す引数が参照ではない場合は、「 指定された変数の値を見に行け」との指示がイベント処理関数に伝わるのではなく、Syori 関数が受け取った値そのものが定数のような形でイベント処理関数に設定されるようです。 すると、例えば常に動いているムービークリップの x や y プロパティなど、addEventListener を実行した時点とクリックされた時とでは値が変わっている可能性があるものを引数として渡したい時は、この方法は使えないことになります。 この場合は、ムービークリップ自体の参照を Syori 関数に渡し、イベント処理関数側で受け取った参照をもとに x や y プロパティを見るようにすれば、イベントが発生した時点での値を取得できます。 --------------------------------------------- 「 btn1 に 1 、btn2 に 2 …」「ボタンの名前と同じフレームラベル」など、全くの任意の値ではなくボタンの管理番号や名前と直接関係のある値をイベント処理関数で使うのでしたら、なにもこんな難しいスクリプトを考えなくても /*********************************************/ //クリック時に実行する処理 function Frame_Jump( event_obj:MouseEvent ):void { //記録されているフレームに移動 gotoAndStop( event_obj.target.frame_no ); } for( var i:int = 1 ; i <= 3 ; i++ ) { //各ボタンに対応する番号を記録 this[ "btn" + i ].frame_no = i; //イベントリスナーを登録 this[ "btn" + i ].addEventListener( MouseEvent.CLICK , Frame_Jump ); } /*********************************************/ ・・・で、充分ではないでしょうか。 i の値をムービークリップ内の変数に記録しておく、ActionScript 1.0 や 2.0 でもおなじみの手法です。 ボタンの名前と同じ名前を使って対応できることであれば、それも立派な作戦です。 ボタンの管理番号や名前と直接は関連性が全くない値を使う場合でも、配列変数を利用して関連性を後から作る方法もあります。 例えば、それぞれのボタンに管理番号を記録しておくと、表を見る要領で単なる番号ではない値を取得することもできます。 どのような方法をとるにしても、そのデータを作る手間だけはどうしても避けられないと思います。
お礼
>DPEさん addEventListenerの関数に対して引数を渡す方法は 検索したら、たまたまでてきたのでラッキーって感じでした♪ 今回のような複数ボタンで処理をまとめたりするような事象は コンテンツ制作において基本でありながら、その内容に応じて いろんな方法論が思い浮かびますよね。 どの観点から見るかによって、こちらの方が効率が良いとか 後々使いまわしができるとかが変わってきますし。 今回はすごく勉強になりました。 でもまた数ヶ月経ったら同じような質問をしていそうな自分が怖いですw その時はまたよろしくお願いします。m(_ _)m
- BlurFiltan
- ベストアンサー率91% (1611/1754)
#1です。 > 「btn1」の時に「1」が「btn2」の時は「2」という想像をしていたのですが、 > 結果はどのボタンを押しても「4」が返ってきます。orz やってみましたが 「4」 は返って来ませんよ。 「btn1」の時に「1」が,「btn2」の時は「2」が,「btn3」の時は「3」が返ってきます。 Flash CS4 にて「Flash Player 9」SWF をパブリッシュしても 「Flash Player 10」SWFをパブリッシュしても,結果は同じでした。 その方法もなかなか良いんじゃないでしょうか? こういうこと↓ですよね。 ----------------------------------------------- for (var i=1; i<=3; i++) { this["btn"+i].addEventListener(MouseEvent.CLICK,syori(i)); } function syori(hikisu):Function{ return function (eventObject:MouseEvent):void{ trace(hikisu); }; } ----------------------------------------------- > 更に上記の記述方法は、スクリプトエラーは出ないものの > スクリプトウィンドウで「自動フォーマット」すると > エラーがでるので(というかコードがちゃんと整理されず、 > この後スクリプトエラーがでる) それは自動フォーマット機能のバグでしょう。 自動フォーマット機能はバグだらけですよ。 Google 検索「ActionScript バグ 自動フォーマット」 http://www.google.co.jp/search?hl=ja&q=ActionScript+%E3%83%90%E3%82%B0+%E8%87%AA%E5%8B%95%E3%83%95%E3%82%A9%E3%83%BC%E3%83%9E%E3%83%83%E3%83%88&lr=&aq=f&oq= 私がよくぶち当たるのが, if文 の 「<」(より小さい less than 演算子) と 「-」 マイナス です。 ---例-------------- var n:Number=-8; if (n < -4) { trace(n); } ------------------- どうやら「n < -4」 を 「n <- 4」 と受け取るようです。 「<-」 みたいな演算子はないからでしょう。 でも,if (n > -4) にすると大丈夫なんです。 理解不能。 ですから深くは考えません。 そのバグはどうでも良いとして, 最初のスクリプトでうまく行きますよ。
補足
>BlurFiltanさん どうも! ご指摘いただいた通りのスクリプトで確かに動作しました。w (元々自分で書いときながら・・・w) なんか色々実験しててごっちゃになってました。 全部「4」が返ってくるのは、いくつか試した方法で ------------------------------------------------------------ for (var i=1; i<=3; i++) { this["btn"+i].addEventListener(MouseEvent.CLICK,syori); function syori(eventObject:MouseEvent):void{ trace(i) } } ------------------------------------------------------------ でした。(お騒がせしました。) ちなみに今回動作確認できましたスクリプトの ------------------------------------------------------------ function syori(hikisu):Function{ return function (eventObject:MouseEvent):void{ trace(hikisu); }; } ------------------------------------------------------------ ですが、 なんか漠然とした意味は解るのですが、イマイチはっきり どういう手順でどういう処理が行われているかつかめまていません。 特にreturn functionの部分が・・・。 時間がありましたら、少し噛み砕いて日本語訳でwご享受願えればありがたく。 自動フォーマット機能に関してはそういうものなんだ~と理解致しました。^^
- BlurFiltan
- ベストアンサー率91% (1611/1754)
処理に同じ部分があって,部分的に違うとか言うのでしたら話は別ですが, 処理が違うのですから,普通は処理ごとに違う関数を作るでしょう。 それが普通ですが...。 あえて同じ function にまとめたいのでしたら, 次のように if文 で分岐しても良いかもしれません。 ------------------------------------------------------- for (var i=1; i<=3; i++) { this["btn"+i].addEventListener(MouseEvent.CLICK,syori); } function syori(eventObject:MouseEvent):void { if (eventObject.target==btn1) { trace("あんぱん"); } else if (eventObject.target == btn2) { trace("しょくぱん"); } else if (eventObject.target == btn3) { trace("カレーぱん"); } } ------------------------------------------------------- もしくは,ハッシュ的データを作るとか。 -------------------------------------------- var traceOBJ:Object = new Object(); traceOBJ.btn1="あんぱん"; traceOBJ.btn2="しょくぱん"; traceOBJ.btn3="カレーぱん"; for (var i=1; i<=3; i++) { this["btn"+i].addEventListener(MouseEvent.CLICK,syori); } function syori(eventObject:MouseEvent):void { trace(traceOBJ[eventObject.target.name]); } -------------------------------------------
補足
>DPEさん、BlurFiltanさん 回答ありがとうございます♪^^ お二方はいつもいつもご丁寧に返信いただき、また大変解りやすい内容ですごく助かります。 今回のようなケースの事象はコンテンツを作る際結構遭遇する場面ですが、 まともにボタンの数だけ100回addEventListenerとfunctionを定義する方法や、 BlurFiltanさんに教えて頂いたやり方、DPEさんのコンポーネント化等考えればいくらでもやり方がありますよね。 今回質問させて頂いた経緯としては実際のところ ステージ上にボタンを10個程配置し「btn1」「btn2」・・・ それぞれのボタンがクリックされたらムービークリップ「menu」を gotoAndStop("●●●")とする感じです。 たとえば「btn1」をクリックしたらMC「menu」が任意のフレームに飛ぶ という事ですね。 で、最初に考えていたのが「btn1」ならMC「menu」の1フレ「btn2」なら2フレというテストをしていて ----------------------------------------------------- btn●.addEventListener(MouseEvent.CLICK,syori); ----------------------------------------------------- のsyoriに対して引数を持たす事ができないかな?と考えたのです。 で、色々調べた結果 http://flashjp.com/as3/event.php などを見ると addEventListemerの関数に対して同時に引数を渡す→受け取るという方法がありました。 実際これで ----------------------------------------------------- for (var i=1; i<=3; i++) { this["btn"+i].addEventListener(MouseEvent.CLICK,syori(●●●)); } function syori(hikisu):Function{ return function (eventObject:MouseEvent):void{ trace(hikisu); } } ----------------------------------------------------- 引数●●●の値を受け取る事はできました。 最初はこの●●●の部分に「i」を指定することで 「btn1」の時に「1」が「btn2」の時は「2」という想像をしていたのですが、 結果はどのボタンを押しても「4」が返ってきます。orz そして思ったのがこれって結局for文でまとめてるから引数を渡せたとしても 「i」指定でそれぞれ「1」「2」「3」が返ってこないのなら 「btn1~3」全て同じ引数を渡す事になる。w このaddEventListener時に引数を渡すやり方では、処理の方を1つにまとめる事ができるという認識にいたりました。 更に上記の記述方法は、スクリプトエラーは出ないもののスクリプトウィンドウで「自動フォーマット」すると エラーがでるので(というかコードがちゃんと整理されず、この後スクリプトエラーがでる) 正規の記述方法(認められていない書き方?)ではないのかな?と思ったりします。 で、最終的には 定義の方(addEventListener)も 処理の方(function)も 1つにまとめる方法を探っていて(100個のボタンを想定して) ----------------------------------------------------- for (var i=1; i<=3; i++) { this["btn"+i].addEventListener(MouseEvent.CLICK,syori); } function syori(eventObject:MouseEvent):void{ var hikisu:String = eventObject.target.name menu.gotoAndStop(hikisu) } ----------------------------------------------------- 等としてeventObject.target.nameでボタンのインスタンス名を取得して、 MC「menu」内のフレーム名もそれに合わせて作って、 そこに飛ばすような事を考えています。(これはまだテストしていませんが・・・) ただこれだとeventObject.target.nameでインスタンス名ですが、 やはりaddEventListenerの時に(このタイミングに限りませんが) ボタンそれぞれ、"任意の値"を持たすには DPEさん、BlurFiltanさんが教えてくれた方法をとる必要がありそうですね。