ActionScript 3.0で始めるオブジェクト指向スクリプティング

第15回配列を使ったキーコードとプロパティの扱い

前回の第14回キー操作とif以外の条件判定では、switchステートメントでキーコードを判定した。今回は、配列とも呼ばれるArrayクラスを使って、条件判定なしにキーコードの処理を行ってみよう。

配列と配列エレメントの扱い

Arrayクラスのインスタンスは「配列」とも称され、複数の値をひとつのインスタンスに納められる。いわば容れ物のような機能をもったインスタンスだ。納められた各値は、配列の要素とか「エレメント」という。配列エレメントには、0から始まる整数の整理番号がつけられて、管理される。この番号は、⁠インデックス」と呼ばれる。

配列インスタンスは、Dateクラスと同じく、new演算子を使って生成する。そして、値を加えるには、配列アクセス演算子[ ] でインデックスを指定し、値を代入する。たとえば、新規のArrayインスタンス(配列)のインデックス0に文字列"日曜日"を納めるには、つぎのようなスクリプトを記述すればよい。

var my_array:Array = new Array();
my_array[0] = "日曜日";
trace(my_array[0]);   // 出力: 日曜日

なお、Arrayインスタンスの生成は、以下のステートメントで行うこともできる。つまり、配列アクセス演算子[ ] を空で記述することが、空の配列を作成することになる。

var my_array:Array = [ ];

さらに、配列アクセス演算子[ ] の中にエレメントを記述すれば、エレメントの入った配列が生成される。つまり上記スクリプトの最初の2行は、つぎの1行で記述することもできる[1]⁠。

var my_array:Array = ["日曜日"];

上記スクリプトのとおり、Arrayインスタンスから値を取出すときにも、配列アクセス演算子[ ] でインデックスを指定する。さて、第7回Dateクラスの日付と文字列の操作で紹介したDate.dayプロパティは、曜日を0から6までの整数で返した。すると、以下のスクリプトは、今日の曜日を配列から取出した漢字で[出力]する図1⁠。

var my_array:Array = new Array();
my_array[0] = "日曜日";
my_array[1] = "月曜日";
my_array[2] = "火曜日";
my_array[3] = "水曜日";
my_array[4] = "木曜日";
my_array[5] = "金曜日";
my_array[6] = "土曜日";
trace(my_array[new Date().day]);
図1 今日の曜日を配列から取出した漢字で[出力]
図1 今日の曜日を配列から取出した漢字で[出力]

配列インデックスは、別に連番で使わなければいけない訳ではない。たとえば、いきなりインデックス10に値を代入してもよい。だからといって、インデックス0から9までのメモリが無駄に確保されたりはしない。Arrayインスタンス内では、インデックスはいわば変数名と同じで、今の例なら10という名前のメモリに値が納められているだけだからだ。

すると、これから扱おうとしているキーコードは、整数でしかも互いに値が重複することはない。配列で管理するには、きわめて好都合といえる。まずは、左右の矢印キーの操作について、Arrayインスタンスを使って処理してみようスクリプト1⁠。処理の大きな流れは、前回のスクリプト2と基本的に同じだ。

スクリプト1 配列を使って左右矢印キーによるインスタンスの移動を処理
// MovieClip: キー操作で動かすインスタンス
var nMove:int = 1;
var nShiftMove:int = 10;
var keys_array:Array = new Array();
keys_array[Keyboard.LEFT] = -1;
keys_array[Keyboard.RIGHT] = 1;
stage.addEventListener(KeyboardEvent.KEY_DOWN, xKeyDown);
function xKeyDown(eventObject:KeyboardEvent):void {
  var nKeyCode:int = eventObject.keyCode;
  var bShiftKey:Boolean = eventObject.shiftKey;
  xMove(nKeyCode, bShiftKey);
}
function xMove(nKeyCode:int, bShiftKey:Boolean):void {
  var nPixels:Number = keys_array[nKeyCode];
  if (nPixels) {
    x += nPixels*xGetPixels(bShiftKey);
  }
}
function xGetPixels(bShiftKey:Boolean):int {
  var nPixels:int = bShiftKey ? nShiftMove : nMove;
  return nPixels;
}

上記スクリプト1は、Arrayインスタンスを作成し、左右矢印キーのキーコードをインデックスに指定して、それぞれの水平移動の向きを±1として代入した。そして、関数xMove()は、押されたキーのコードをインデックスに指定して、配列から値を取出す。左右の矢印キーが押されれば、±1の値が得られる。

押したキーが左右の矢印以外で、インデックスに指定したエレメントが存在しなければ、未定義を意味するundefinedという値が配列から返される。しかし、Number型で指定した変数(nPixels)undefinedを代入すると、数値演算の対象にならないことを示すNaNという値に変わる。したがって、変数値が数値かNaNかを判定する必要がある。

上記スクリプト1の関数xMove()では、if条件に直接変数(nPixels)を指定している。すると、ifステートメントは、変数の値そのものを評価して、trueまたはfalseと判定する。そして、Number型の値は、NaNと0以外はtrueと評価される。よって、if条件に指定したNumber型変数(nPixels)trueと評価されれば、左右の矢印キーのいずれかが押されて変数に±1の値が入っていると判別できるので、インスタンスの座標をその値にしたがって移動させればよい[2]⁠。

これで、どのキーが押されて、どちらの方向にインスタンスを動かすべきかは、条件判定によることなく処理できた。前述のifステートメントは、単に有効なキーが押されたかどうかを確かめているだけだ。[ムービープレビュー]で試すと、左右の矢印キーでインスタンスが移動する。[shift]キーにより移動距離を変更することも可能だ。

配列アクセス演算子でプロパティを操作する

上下の矢印キーの操作も処理に加えようとしたとき、困ったことに気づく。その場合、操作するインスタンスのプロパティを、切替えなければならないということだ。つまり、たとえばつぎのステートメントで、プロパティxyに替える必要がある。

x += 1;

しかし、プロパティをそのまま変数に入れて、処理することはできない。たとえば、つぎのようなスクリプトを書いても、Number型の変数(myProperty)にプロパティxの数値が代入され、その値に1が加算されるだけだ。xプロパティが操作されることにはならない。

var myProperty:Number = x;
myProperty += 1;

そこで、配列アクセス演算子[ ] のもうひとつの使い方を学習しよう。配列アクセス演算子[ ] は、配列つまりArrayインスタンスだけでなく、他のインスタンスのプロパティにアクセスするために使うこともできる。

たとえば、フレームアクションで、タイムラインに配置されたインスタンスmy_mcの水平座標を10ピクセル右に動かすには、今まではつぎのようにステートメントを記述した。

my_mc.x += 10;

この同じ処理は、配列アクセス演算子を使って、つぎのように書替えることができる。

my_mc["x"] += 10;

配列アクセス演算子[ ] を用いたステートメントのポイントは、プロパティを文字列で記述するということだ。したがって、その文字列は変数で指定することもできる。

var myProperty:String = "x";
my_mc[myProperty] += 10;

これが可能であれば、文字列のプロパティ名を変更することにより、操作するインスタンスのプロパティは変えられる。しかし、ここでひとつ注意しなければならない。つぎのステートメントは、エラーになってしまうということだ図2⁠。

["x"] += 10;
図2 ターゲットなしに配列アクセス演算子[ ] を使うと配列の作成になる
図2 ターゲットなしに配列アクセス演算子[ ] を使うと配列の作成になる

なぜなら、上のステートメントの左辺は、インスタンスのxプロパティにアクセスしているのではない。文字列"x"をエレメントとしてもつ配列をつくってしまっているのだ。Arrayインスタンスそのものに、直接数値を加算することはできない。だから、エラーになる

配列アクセス演算子[ ] は、ドット演算子.と基本的に同じ役目だ。ドット.の左には、必ずプロパティの持ち主(ターゲット)となるべきインスタンスを指定しなければならない。スクリプトを記述しているインスタンス自身がターゲットであれば、thisキーワードを指定する。

this["x"] = 10;

残る小さな問題は、プロパティの文字列を配列(keys_array)にどうやって指定するかだ。キーコードをインデックスにして、プロパティの文字列に加えて移動方向を示す数値(±1)も納めなければならない。つまり、ひとつのインデックスにふたつの値を指定する必要がある。しかし、この点は実は本稿の最初に答えが出ている。

複数の値をひとつにしたければ、Arrayインスタンスを使えばよかった。つまり、配列エレメントとして配列を指定すればよいのだ。配列は、何重にでも入れ子にすることができる。そこで、上下左右の矢印キーのキーコードに対して、つぎのように配列をエレメントとして代入することにしよう。

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];

入れ子の配列の中のエレメントを取得するには、一旦エレメントの配列を取出し、改めてそのインデックスを指定すればよい[3]⁠。たとえば、上記配列から左矢印キーのキーコードのインデックスに納めたプロパティの文字列と方向の数値を調べるには、つぎのようなステートメントを記述する。

var key_array:Array = keys_array[Keyboard.LEFT];
trace(key_array[0], key_array[1]);   // 出力: x -1

これで、上下左右の矢印キーの操作を、配列で処理する準備は整った。スクリプトは以下のとおりだスクリプト2⁠。なお今度は、配列を格納した変数(key_array)if条件に指定されている。この場合、変数に配列が入っていればtrue入っていなければfalseと評価される[4]⁠。

スクリプト2 操作するプロパティを配列のキーコードのインデックスに定める
// MovieClip: キー操作で動かすインスタンス
var nMove:int = 1;
var nShiftMove:int = 10;
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;
  var bShiftKey:Boolean = eventObject.shiftKey;
  xMove(nKeyCode, bShiftKey);

}
function xMove(nKeyCode:int, bShiftKey:Boolean):void {
  var key_array:Array = keys_array[nKeyCode];
  if (key_array) {
    this[key_array[0]] += key_array[1]*xGetPixels(bShiftKey);
  }
}

function xGetPixels(bShiftKey:Boolean):int {
  var nPixels:int = bShiftKey ? nShiftMove : nMove;
  return nPixels;
}

[ムービープレビュー]で試すと、前回作成したスクリプト2と動作は変わらない。しかし、関数xMove()が、きわめてすっきりとした記述になったことを確認してほしい。次回は、またお題を変えて、疑似3D風のアニメーションを作成する予定だ。

今回解説した次のサンプルファイルがダウンロードできます。

おすすめ記事

記事・ニュース一覧