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

第38回 z座標値に応じて重ね順を変える

この記事を読むのに必要な時間:およそ 6.5 分

面のz座標値で重ね順を入替える関数

第2に手を加える関数xSetOrder()は,面のSpriteインスタンスのz座標値により重ね順を入替えるよう定義し直す。ところが,ここで問題が生じる。面のインスタンスは親Spriteインスタンスに入れ子になっており,回すのはこの容れ物の親インスタンスだった。ということは,面のインスタンスの座標そのものは,1ピクセルたりとも動かないのだ。

このようなとき,インスタンスの基準とする座標空間を親インスタンス以外に変換することができる。それが,Transform.getRelativeMatrix3D()メソッドだ。インスタンスのDisplayObject.transformプロパティに対してこのメソッドを呼出すと,インスタンスのもつTransform.matrix3Dプロパティの値を,メソッドの引数に渡したDisplayObjectインスタンスから見た値に計算し直してくれる。

Transformオブジェクト.getRelativeMatrix3D(基準とするインスタンス)

Transform.getRelativeMatrix3D()メソッドは,Matrix3Dオブジェクトを返す。その座標値が欲しいときは,Matrix3D.positionプロパティを調べればよい。xyzの各プロパティをもったVector3Dオブジェクトが得られる。z座標値はVector3D.zプロパティだ。

面のインスタンスをz座標値順に並べ替えるには,第24回「インスタンスの管理と配列の並べ替え」インスタンスの重ね順を管理するで用いたArray.sort()メソッドが使える。そのためには,容れ物の親Spriteインスタンスから,面の子インスタンスを取出して配列に納めなければならない。親インスタンスの表示リストから,指定したインデックス番号の子インスタンスを取出すのはDisplayObjectContainer.getChildAt()メソッドだ。子インスタンスの数は,DisplayObjectContainer.numChildrenプロパティで調べられた。

親インスタンス.getChildAt(子インスタンスのインデックス)

定義し直した関数xSetOrder()は,以下のとおりだ。まず,容れ物である親インスタンスの表示リストから面の子インスタンスをすべて取出して,それらを新たな配列(faces_array)に納める※1⁠。つぎに,その配列エレメントとなった子インスタンスをArray.sort()メソッドで並べ替える。引数には比較関数(compare())を渡して,インスタンスのz座標値の順にする。

比較関数compare()は,渡される子インスタンスのメインタイムライン(this)から見たMatrix3DオブジェクトをTransform.getRelativeMatrix3D()メソッドにより求めて,Matrix3D.positionプロパティでz座標値を調べる。

ただし,z座標値は奥向きが正で,表示リスト内のインデックスは手前ほど値が大きい。比較関数からの戻り値を決めるとき,注意する必要がある。

function xSetOrder():void {
  var faces_array:Array = new Array();
  var nChildren:uint = mySprite.numChildren;
  for (var i:uint = 0; i < nChildren; i++) {
    faces_array.push(mySprite.getChildAt(i));
  }
  faces_array.sort(compare);
  for (var j:uint = 0; j < nChildren; j++) {
    var faceSprite:Sprite = faces_array[j];
    mySprite.setChildIndex(faceSprite, j);
  }
}
function compare(a:Sprite, b:Sprite):Number {
  var nA:Number = a.transform.getRelativeMatrix3D(this).position.z;
  var nB:Number = b.transform.getRelativeMatrix3D(this).position.z;
  if (nA < nB) {
    return 1;
  } else if (nA > nB) {
    return -1;
  } else {
    return 0;
  }
}

前回のスクリプト2に上述の修正を加えたのが,以下のスクリプト1だ。この段階でのムービーの動きは,前回のスクリプト2と変わらない第37回図6再掲⁠⁠。しかし,中身は面のインスタンスの入れ子を使って,その重ね順をz座標により定めている。面を増やしたり,垂直回転を加える準備が整った。

スクリプト1 Sprite内にふたつのビットマップを置いてマウスポインタの位置に応じて水平に回す

// タイムライン: メイン
// [ライブラリ]のビットマップに[クラス]としてImage0とImage1を設定
var mySprite:Sprite = new Sprite();
var nX:Number = stage.stageWidth / 2;
var nY:Number = stage.stageHeight / 2;
var nUnit:Number = 100 / 2;
var frontSprite:Sprite = xCreateFace(Image0, 0, 0, -nUnit);
var backSprite:Sprite = xCreateFace(Image1, 0, 0, nUnit, 180);
var nDeceleration:Number = 0.3;
mySprite.z = 0;
var myMatrix3D:Matrix3D = mySprite.transform.matrix3D;
mySprite.x = nX;
mySprite.y = nY;
addChild(mySprite);
mySprite.addChild(frontSprite);
mySprite.addChild(backSprite);
addEventListener(Event.ENTER_FRAME, xRotate);
function xCreateFace(myBitmapData:Class, nX:Number, nY:Number, nZ:Number, nRotationY:Number = 0):Sprite {
  var myBitmap:Bitmap = new Bitmap(new myBitmapData(0, 0));
  var faceSprite:Sprite = new Sprite();
  faceSprite.addChild(myBitmap);
  myBitmap.x = -myBitmap.width / 2;
  myBitmap.y = -myBitmap.height / 2;
  faceSprite.x = nX;
  faceSprite.y = nY;
  faceSprite.z = nZ;
  faceSprite.rotationY = nRotationY;
  return faceSprite;
}
function xRotate(eventObject:Event):void {
  var nRotationY:Number = (mouseX - nX) * nDeceleration;
  myMatrix3D.appendTranslation(-nX, -nY, 0);
  myMatrix3D.appendRotation(nRotationY, Vector3D.Y_AXIS);
  myMatrix3D.appendTranslation(nX, nY, 0);
  xSetOrder();
}
function xSetOrder():void {
  var faces_array:Array = new Array();
  var nChildren:uint = mySprite.numChildren;
  for (var i:uint = 0; i < nChildren; i++) {
    faces_array.push(mySprite.getChildAt(i));
  }
  faces_array.sort(compare);
  for (var j:uint = 0; j < nChildren; j++) {
    var faceSprite:Sprite = faces_array[j];
    mySprite.setChildIndex(faceSprite, j);
  }
}
function compare(a:Sprite, b:Sprite):Number {
  var nA:Number = a.transform.getRelativeMatrix3D(this).position.z;
  var nB:Number = b.transform.getRelativeMatrix3D(this).position.z;
  if (nA < nB) {
    return 1;
  } else if (nA > nB) {
    return -1;
  } else {
    return 0;
  }
}

第37回図6 インスタンスの重ね順が正しく変わる(再掲)

第37回図6 インスタンスの重ね順が正しく変わる(再掲) 第37回図6 インスタンスの重ね順が正しく変わる(再掲)

※1
今回の処理では,関数xSetOrder()で毎回面の子インスタンスの配列をつくる必要はない。面をつくる関数xCreateFace()で初めに配列をつくり,子インスタンスの数ともども変数にとっておけば済む。
しかし,処理内容によっては重ね順を定める面のインスタンスが毎回変わる場合もありうる。前回も紹介した「ActionScript 3.0による 三次元表現 in Apple Store, Ginza」でひとつの例を解説した。興味がある読者はセミナーの公開映像(vol.4)をご覧いただきたい。

著者プロフィール

野中文雄(のなかふみお)

ソフトウェアトレーナー,テクニカルライター,オーサリングエンジニア。上智大学法学部卒,慶応義塾大学大学院経営管理研究科修士課程修了(MBA)。独立系パソコン販売会社で,総務・人事,企画,外資系企業担当営業などに携わる。その後,マルチメディアコンテンツ制作会社に転職。ソフトウェアトレーニング,コンテンツ制作などの業務を担当する。2001年11月に独立。Web制作者に向けた情報発信プロジェクトF-siteにも参加する。株式会社ロクナナ取締役(非常勤)。

URLhttp://www.FumioNonaka.com/

著書