サイトトップ

Director Flash 書籍 業務内容 プロフィール

Macromedia Flash非公式テクニカルノート

ドット演算子と配列アクセス演算子

ID: FN1107002 Product: Flash CS3 and above Platform: All Version: 9 and above/ActionScript 3.0

インスタンスのプロパティにアクセスしたり、メソッドを呼出すときには「ドット演算子(.)」を使います[*1]。他方、Arrayクラスの配列インスタンスからエレメントを取出す角かっこ(ブラケット)「配列アクセス演算子([])」でも、配列にかぎらずさまざまなオブジェクトのプロパティにアクセスすることができます。本稿では、ドット演算子(.)と配列アクセス演算子([])について、その意味と使い方をご説明します。

[*1]「演算子」(operator)とは、演算記号のことです。演算子を用いて演算の対象とする値や変数は、「被演算子」または「オペランド」(operand)と呼ばれます。一般に式は、演算子とオペランドで構成されます。


01 ドット演算子
ドット演算子(.)は、オブジェクトのプロパティやメソッドを参照するために用いられます。たとえば、タイムラインの現行(再生ヘッドのある)フレームは、フレームアクションでそのタイムラインを参照するthisキーワードを参照にして、MovieClip.currentFrameプロパティで取得することができます(スクリプト001)[*2]

スクリプト001■MovieClip.currentFrameプロパティを参照

//タイムラインの現行フレームを変数nFrameに格納
var nFrame:int = this.currentFrame;

また、同じくタイムラインの第10フレームに移動して再生を続ける場合には、this参照に対してMovieClip.gotoAndPlay()メソッドを呼出します(スクリプト002)。

スクリプト002■MovieClip.gotoAndPlay()メソッドを呼出

// タイムラインの第10フレームに移動して再生
this.gotoAndPlay(10);

前掲スクリプト001のフレームアクションで、タイムラインにint型変数nFrameを宣言して、フレーム番号を納めました。この変数の値を[出力]パネルに表示するには、つぎのステートメントを書きます(スクリプト003)。

スクリプト003■タイムラインの変数値を参照

// タイムラインに設定した変数nFrameの値を出力
trace(this.nFrame);

ActionScriptではメソッドはプロパティに含まれます[*3]。また、変数もユーザーが定義したプロパティと見ることができます。したがって、ドット演算子(.)は、オブジェクトのプロパティにアクセスする機能をもつといえます。

ドット演算子(.)は、階層化されたMovieClipのターゲットパスを参照する場合にも用いられます。たとえば、タイムラインに配置されたMovieClipインスタンスmy_mcの水平座標を10ピクセル右に移動するには、つぎのようにターゲットパスを指定してスクリプトを書きます。

スクリプト004■タイムラインに配置したMovieClipの座標を移動

// タイムラインに配置したインスタンスmy_mcの水平座標に10加算
this.my_mc.x += 10;

this参照の後のドット演算子(.)に続けてmy_mcが指定されているのは、子インスタンスmy_mcがフレームアクションの書かれた親タイムラインのプロパティと捉えられていることを意味します。このようにドット演算子(.)は、オブジェクトがもつあらゆるプロパティにアクセスする手段なのです。

【ドット演算子(.)】
左側オペランド(前掲注[*1]参照)のオブジェクトを参照して、右側オペランドのプロパティにアクセスする。

[*2] this参照は多くの場合省くことができます。けれど、本稿では参照を明らかにするため、あえてthisキーワードを用います。

[*3] JavaやC++などのオブジェクト指向プログラミング言語においては、オブジェクトはデータを保持する「プロパティ」と、その操作手順を定めた「メソッド」で構成されるものと捉えられています。しかし、ActionScriptでは、オブジェクトはプロパティの集まりです。そして、プロパティにはデータ(変数)だけでなくメソッド(関数)も含まれます。

ActionScript 3 Language Specification」の4.14「Property」は、プロパティをつぎのように定めます(「名前」というのは「識別子」を指します)。

プロパティは、名前を値またはメソッドに関連づけます(A property associates a name with a value or method.)。

02 配列アクセス演算子
配列アクセス演算子([])を用いても、オブジェクトのプロパティが参照できます[*4]。その場合、ドット(.)をブラケット([])で置換え、プロパティは名前(識別子)を文字列(String型)にしてブラケット内に指定します。たとえば、タイムラインに定めた変数nFrameの値を[出力]する前掲スクリプト003は、つぎのように配列アクセス演算子([])で書替えられます(スクリプト005)。

スクリプト005■タイムラインの変数値を配列アクセス演算子で参照

// タイムラインに設定した変数nFrameの値を出力
trace(this["nFrame"]);

ドット演算子(.)と異なり、配列アクセス演算子([])にはプロパティを文字列で渡します。したがって、配列アクセス演算子([])に変数を指定すると、その変数から文字列を取出してその名前のプロパティにアクセスします(スクリプト006)。

スクリプト006■文字列が納められた変数を配列アクセス演算子に渡して参照

// タイムラインに設定した変数nFrameの値を出力
var property_str:String = "nFrame";   // 変数名の文字列"nFrame"をString型変数に格納
trace(this[property_str]);

プロパティを文字列で指定できるということは、上記スクリプト006のようにプロパティ名を変数に入れて扱えるということです。さらに、動的につなぎ合わせた文字列から、プロパティ名をつくることもできます(ただし、後述03「配列アクセスを使う場合はかぎられる」参照)。つまり、プロパティ名が、ダイナミックに変更できることを意味します。

そこで、操作するインスタンスのプロパティを、ランダムに定めてみましょう。つぎのスクリプト007は、タイムラインのインスタンスmy_mcをクリックするたびに、ランダムな方向に移動または回転します。

スクリプト007■インスタンスをランダムな方向に移動または回転する

// タイムラインにmy_mcを配置
var properties_array:Array = ["x", "y", "rotation"];
var nLength:uint = properties_array.length;
var nMove:int = 10;
my_mc.addEventListener(MouseEvent.CLICK, xRandomMove);
function xRandomMove(eventObject:MouseEvent):void {
  var nRandom:uint = uint(Math.random() * nLength);
  var property_str:String = properties_array[nRandom];
  my_mc[property_str] += nMove;
  nMove *= Math.pow(-1, nRandom);
}

配列の変数(properties_array)には、操作するプロパティ名の文字列"x"、"y"、"rotation"をエレメントとして納めました。インスタンス(my_mc)をクリックすると、リスナー関数(xRandomMove())が配列からひとつのプロパティ名をランダムに取出し、配列アクセス演算子([])で参照したインスタンスのプロパティに±10加(減)算しています。

ひとつ注意しなければならないのは、前掲スクリプト004を配列アクセス演算子([])で書替える場合です。スクリプト004は、つぎのようにthis参照が省けます。

my_mc.x += 10;

しかし、配列アクセス演算子([])を使ってつぎのように書くと、#1084シンタックスエラーになります(図001)[*5]

["my_mc"].x += 10;   // シンタックスエラー

図001■配列アクセス演算子の左側オペランドがないとシンタックスエラー

スクリプト004を書替えた前の例では、this参照と一緒にドット演算子(.)が除かれていることにご注意ください。ドット(.)を残したまま「.my_mc.x += 10」と書けば、ドット演算子でもシンタックスエラーになります。つまり、ドット演算子(.)も配列アクセス演算子([])もともに、左側オペランドにオブジェクトの参照がなければならないのです(スクリプト008)。

スクリプト008■タイムラインに配置したMovieClipの座標を配列アクセス演算子で移動
// タイムラインに配置したインスタンスmy_mcの水平座標に10加算
this["my_mc"].x += 10;
【配列アクセス演算子([])】
左側オペランドのオブジェクトを参照して、ブラケット内に指定した文字列の名前(識別子)のプロパティにアクセスする。

[*4] 配列すなわちArrayインスタンスのエレメント(要素)にアクセスするとき用いられるため、この名で呼ばれます。しかし、ここで説明する内容は、配列の話ではありません。もっとも、配列もオブジェクトですので、配列にも当てはめることはできます。

[*5] 左側オペランドなしに配列アクセス演算子を使うと、まったく違った意味になります。それは、配列つまりArrayインスタンスの生成です。たとえば、つぎのふたつのステートメントは同じ処理で、ともに文字列"my_mc"がひとつのエレメントとして納められた配列をつくります。

["my_mc"];

new Array("my_mc");

したがって、つぎのステートメントならエラーなく動きます。配列をつくったうえで、そのArray.lengthプロパティにアクセスしていることになるからです。

trace(["my_mc"].length);   // 出力: 1

03 配列アクセスを使う場合はかぎられる
前項で述べたとおり配列アクセス演算子([])は、プロパティを変数にして動的に変えられます。これはドット演算子(.)にはまねのできないことです。けれど、プロパティに配列アクセスした方がよい場合はかぎられます。

どちらの演算子でも使えるときは、ドットアクセスの方が簡単で確実です。タイプする文字数は少なくて済むうえ、プロパティやメソッドがコードヒントで選べます。また、型指定されたプロパティや変数を参照するときは、ドットアクセスの方が配列アクセスと比べて最適化されます(「ActionScript 3.0におけるパフォーマンス向上のヒント」01「データ型を指定する」)。

配列アクセスを使う意味があるとすれば、いくつものプロパティをダイナミックに切替えて参照する場合でしょう。つぎのスクリプト009をMovieClipシンボルのフレームアクションに設定すると、キーボードから上下左右の湯印キーでインスタンスをその方向に1ピクセルずつ動かせます(図002)。

図002■上下左右の矢印キーでインスタンスが動く

スクリプト009■MovieClipインスタンスを矢印キーで上下左右に動かす

// MovieClip: キー操作で動かすインスタンス
var nMove:int = 1;
var keys_array:Array = new Array();
keys_array[Keyboard.LEFT] = ["x", -1];
keys_array[Keyboard.RIGHT] = ["x", 1];
keys_array[Keyboard.UP] = ["y", -1];
keys_array[Keyboard.DOWN] = ["y", 1];
stage.addEventListener(KeyboardEvent.KEY_DOWN, xKeyDown);
function xKeyDown(eventObject:KeyboardEvent):void {
  var nKeyCode:int = eventObject.keyCode;
  xMove(nKeyCode);
}
function xMove(nKeyCode:int):void {
  var key_array:Array = keys_array[nKeyCode];
  if (key_array) {
    this[key_array[0]] += key_array[1];
  }
}

配列の変数(keys_array)には各矢印キーコードのインデックスに入れ子の配列エレメントを加え、それそれの子配列に各キーで操作するプロパティの文字列("x"か"y")と移動ピクセル数(±1)を納めています。

キーボードイベントInteractiveObject.keyDown(定数KeyboardEvent.KEY_DOWN)のリスナー関数(xKeyDown())は引数のKeyboardEventオブジェクトから押されたキーのコード(KeyboardEvent.keyCodeプロパティ)を調べ、その整数値を関数(xMove())に渡して呼出します。

渡された値が矢印キーのコードなら配列(keys_array)のそのインデックスに子配列があるので、操作するプロパティの文字列と移動ピクセル数を取出して、配列アクセスによりインスタンスを動かします。このスクリプトの中身について詳しくは、gihyo.jp連載「ActionScript 3.0で始めるオブジェクト指向スクリプティング」第15回「配列を使ったキーコードとプロパティの扱い」の「配列アクセス演算子でプロパティを操作する」をお読みください。

このスクリプト009(関数xMove())はキーコードを条件にして振分ければ、ドットアクセスで書くこともできます。その方がスクリプトの速さでは勝るでしょう。けれど、それぞれのキーに応じた操作が配列(keys_array)にまとめられ、条件判定よりキーコードの振分けがすっきりすることは魅力です。ことさら速さが求められる場合でないなら、この手法を選ぶ理由は十分にあります[*6]

ActionScript 2.0までは、インスタンス名に決まりを定め、それらのインスタンスを配列アクセスでまとめて処理するというテクニックがよく用いられました。しかし、ActionScript 3.0では、インスタンス名にもとづく処理は、お勧めすべき利点がほとんど見当たりません[*7]。例をふたつほど挙げましょう。

ひとつは、インスタンスをforループでまとめて処理したいときです。連番のインスタンス名をつければ、文字列とカウンタ変数をつなぎ合わせて配列アクセスで参照できます。たとえば、タイムラインに置いた3つのインスタンスmy0_mc〜my2_mcをすべて非表示にするには、つぎのようなforループの処理を書きます。

for (var i:uint = 0; i < 3; i++) {
  this["my" + String(i) + "_mc"].visible = false;
}

しかしこのような場合、処理するインスタンスを予め配列に納めておけば、名前に頼ることがありません。

var instances_array:Array = [my0_mc, my1_mc, my2_mc];
var nCount:uint = instances_array.length;
for (var i:uint = 0; i < nCount; i++) {
  instances_array[i].visible = false;
}

もうひとつは、別のインスタンスと名前で関連づけることです。組合わせたいインスタンスの名前に共通の番号を含めるといったやり方が考えられます。たとえば、サムネイルのインスタンスbutton0_mc〜button2_mcにマウスでロールオーバーしたとき、それぞれに対応する大きな画像のインスタンスmy0_mc〜my2_mcを表示するとしましょう(図003)。

図003■サムネイルを並べて対応する画像はすべて重ねておく

サムネイルと大きい画像は、インスタンス名の中の数字で対応させます。また、大きい画像はすべて重ねて非表示にしておき、ロールオーバーしたサムネイルに対応するインスタンスだけを表示します。フレームアクションは、つぎのスクリプト010のようになります。

スクリプト010■サムネイルにロールオーバーすると名前が対応するインスタンスを表示する

// メインタイムライン
// MovieClipインスタンスbutton0_mc〜button2_mcとmy0_mc〜my2_mcを配置
button0_mc.addEventListener(MouseEvent.ROLL_OVER, xShow);
button1_mc.addEventListener(MouseEvent.ROLL_OVER, xShow);
button2_mc.addEventListener(MouseEvent.ROLL_OVER, xShow);
function xShow(eventObject):void {
  var my_mc:MovieClip = MovieClip(eventObject.currentTarget);
  var name_str:String = my_mc.name;
  var show_mc:MovieClip = this["my" + name_str.substring(6)];
  for (var i:uint = 0; i < 3; i++) {
    this["my" + String(i) + "_mc"].visible = false;
  }
  show_mc.visible = true;
}

マウスイベントInteractiveObject.rollOver(定数MouseEvent.ROLL_OVER)のリスナー関数(xShow())は、まず引数に受取ったイベントオブジェクト(MouseEvent)のEvent.currentTargetプロパティからロールオーバーしたインスタンスを調べ、その名前の文字列をDisplayObject.nameプロパティで取出します。

つぎに、表示するインスタンスの名前が共通する初めの2文字("my")とロールオーバーしたインスタンス名からとった終わりの4文字(数字 + "_mc")を組合わせて、表示すべきインスタンスを配列アクセスで参照します(show_mc)。

そして、表示するインスタンスをすべて前掲と同じスクリプトで一旦非表示にした後、表示すべきインスタンスのDisplayObject.visibleプロパティをtrueにして改めて表示しています(なお、F-site「ラジオボタンのように複数の中のひとつだけ設定値を変える」参照)。[ムービープレビュー]を確かめると、ロールオーバーしたサムネイルに対応する画像のインスタンスが表示されます(図004)。

図004■サムネイルにロールオーバーすると対応するインスタンスが表示される

サムネイルのインスタンスがMovieClipであれば、対応する画像のインスタンスをその変数に設定できます[*8]。そうすればこのサンプルも、インスタンス名を用いなくて済みます。そのように書替えたのが、つぎのスクリプト011です。

スクリプト011■サムネイルにロールオーバーすると変数が参照するインスタンスを表示する

// メインタイムライン
// MovieClipインスタンスbutton0_mc〜button2_mcとmy0_mc〜my2_mcを配置
var instances_array:Array = [my0_mc, my1_mc, my2_mc];
var nCount:uint = instances_array.length;
button0_mc.addEventListener(MouseEvent.ROLL_OVER, xShow);
button1_mc.addEventListener(MouseEvent.ROLL_OVER, xShow);
button2_mc.addEventListener(MouseEvent.ROLL_OVER, xShow);
button0_mc.show_mc = my0_mc;
button1_mc.show_mc = my1_mc;
button2_mc.show_mc = my2_mc;
function xShow(eventObject):void {
  var my_mc:MovieClip = MovieClip(eventObject.currentTarget);
  var show_mc:MovieClip = my_mc.show_mc;
  xClearAll();
  show_mc.visible = true;
}
function xClearAll():void {
  for (var i:uint = 0; i < nCount; i++) {
    instances_array[i].visible = false;
  }
}

サムネイルのインスタンス(button0_mc〜button2_mc)にはみな同じ名前の変数(show_mc)を設けて、それぞれ対応するインスタンス(my0_mc〜my2_mc)の参照を設定しました。

リスナー関数(xShow())は、前掲スクリプト010と同じようにロールオーバーしたインスタンスを調べて、名前の文字列(DisplayObject.nameプロパティ)を取出すことなく、変数から対応するインスタンスの参照を得ます。そして、そのインスタンスを表示しています。

なお、インスタンスをすべて非表示にする処理は、関数(xClearAll())として定めました。このスクリプトの中身について詳しくは、gihyo.jp連載「ActionScript 3.0で始めるオブジェクト指向スクリプティング」第31回「マウスのロールオーバー」の「サムネイルへのロールオーバーで表示画像を切替える」をお読みください。

上記スクリプト011でしいて配列アクセスを使うとすれば、初期設定でしょう。つぎのようなforループで処理すれば(関数xInitialize())、とくにインスタンスの数が多いときにはスクリプトを書く手間が少しだけ省けます(図005)。

var instances_array:Array = [];   // [my0_mc, my1_mc, my2_mc];
var nCount:uint = 3;   // = instances_array.length;
/*
button0_mc.addEventListener(MouseEvent.ROLL_OVER, xShow);
button1_mc.addEventListener(MouseEvent.ROLL_OVER, xShow);
button2_mc.addEventListener(MouseEvent.ROLL_OVER, xShow);
button0_mc.show_mc = my0_mc;
button1_mc.show_mc = my1_mc;
button2_mc.show_mc = my2_mc;
*/

xInitialize();
function xInitialize():void {
  for (var i:uint = 0; i < nCount; i++) {
    var button_mc:MovieClip = this["button" + String(i) + "_mc"];
    var instance:MovieClip = this["my" + String(i) + "_mc"];
    button_mc.addEventListener(MouseEvent.ROLL_OVER, xShow);
    button_mc.show_mc = instance;
    instances_array[i] = instance;
  }
}

図005■初期設定を関数内のforステートメントで処理する

もっとも、本当にインスタンスの数が増えたら、予めタイムラインに置いて名前をつけるという作業そのものが現実的でなくなります。そこで、インスタンスを動的につくるようになると、インスタンスは直接配列に加え、必要な設定をすればよく、名前は要らなくなります。そうした意味でも、配列アクセスを使う場面はかぎられることになるでしょう。

[*6] 条件判定を使った場合のスクリプトや速さの比較について、詳しくはF-site「キーボードのキー操作を扱うふたつの手法」をお読みください。さらに、配列でなくVectorインスタンスを使えば、スクリプト009と同じ仕組みを使いつつ、速さも条件判定と遜色なくなります。詳しくは、F-site「キーボードのキー操作をVectorオブジェクトで扱う」をご覧ください。

[*7] ActionScript 3.0では、文字列のインスタンス名を示すDisplayObject.nameプロパティは応用の幅が広くありません。詳しくは、F-site「MovieClipインスタンスとインスタンス名」をお読みください。

[*8] サムネイルのインスタンスがMovieClipでなく、たとえばSpriteですとインスタンスに変数が設定できません。そのようなときはDictionaryクラスを使うと、インスタンス名には頼らずに済みます。詳しくは、gihyo.jp連載「ActionScript 3.0で始めるオブジェクト指向スクリプティング」第32回「Dictionaryクラスを使う」をお読みください。


作成者: 野中文雄
作成日: 2011年7月5日 ActionScript 2.0にもとづいて作成したFN0507001「ドット演算子と配列アクセス演算子」を、ActionScript 3.0用に全面改訂。


Copyright © 2001-2011 Fumio Nonaka.  All rights reserved.