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

第57回インスタンスをクリックした点で回しながらドラッグする

まずは、お題をSWFムービーでお見せしよう。というのは、今回では完結せず、残りを次回に持ち越すことになるからだ。目指すコンテンツの動きを、先に確かめてもらいたい。矩形のインスタンスをドラッグすると、回るように動く。マウスを振り回せば、インスタンスがくるくる回る。そして放すと、滑るように減速して、やがて止まる。どこかで見た動きだと感じる読者も少なくないかもしれない。

お題 インスタンスがドラッグで回り放すと滑る(SWF)

課題は大きくふたつある。第1は、初めにマウスクリックした座標で、インスタンスを回すことだ。Matrix3Dクラスを使った3次元空間の回転は、第36回Matrix3Dクラスの後から加える変換で学習した。今回は2次元平面なので、Matrixクラスを使う。もっとも、考え方は基本的に変わらない。

課題の第2は、インスタンスを回す速さはどのように決めたらよいかだ。単純にマウスポインタを動かす速さだけでなく、インスタンスのどこをどの方向にドラッグするかによっても回り方は変わる。実は、ここでベクトルの外積が登場する。ただし、この課題については、次回のお楽しみということになる。

2次元平面のインスタンスを任意の座標で回すには

ということで、今回取組む課題は、クリックした座標でインスタンスを回しながらドラッグすることだ。まずは、Matrixクラスについて簡単に説明しよう。このクラスもMatrix3Dと同じく、行列によりインスタンスの座標を変換する。もちろん、2次元なのでz軸はもたない。行列による座標変換で大切なのは、原点が親タイムラインの基準点になるということだ。

すると、たとえばインスタンスの基準点を中心に変形したいときはどうするか。まず、変換の中心にしたい点が親インスタンスの基準点になるように平行移動する。そのうえでつぎに、変形(伸縮・拡大)を行えば、中心にしたい点(= 親インスタンスの基準点)が原点となって変換される。その後、インスタンスを改めてもとの位置に戻せばよい図1⁠。

図1 親インスタンスの基準点に移動して変形したうえでもとの位置に戻す
(1)親インスタンスの基準点に移動
図1 (1)親インスタンスの基準点に移動
(2)変形(伸縮・回転)
図1 (2)変形(伸縮・回転)
(3)もとの位置に戻す
図1 (3)もとの位置に戻す

それではドラッグは一旦脇に置いて、インスタンスをクリックした座標で回してみよう。使うメソッドは、平行移動がMatrix.translate()回転はMatrix.rotate()になる。Matrix3Dクラスと違い、先に加える変換(prepend)のメソッドはないので、⁠appendをあえてつける必要がなく)メソッド名は端的だ。なお、Matrix.rotate()メソッドの引数に渡す角度は、度数でなくラジアンであることに気をつけてほしい。

Matrixオブジェクト.translate(x座標, y座標)
Matrixオブジェクト.rotate(ラジアン角)

以下のスクリプト1を回したいMovieClipシンボルのフレームアクションに書くと、インスタンスはクリックした座標で回転を始め、マウスボタンを放すまで回り続ける図2⁠。なお、マウスポインタを動かしても、インスタンスの回転の中心はそのまま変わらない。

図2 マウスでプレスした位置を中心にインスタンスが回る
図2 マウスでプレスした位置を中心にインスタンスが回る 図2 マウスでプレスした位置を中心にインスタンスが回る
スクリプト1 マウスプレスした座標でインスタンスを回す
// フレームアクション: マウスプレスした座標で回すMovieClipシンボル
var nX:Number;
var nY:Number;
var angularVelocity:Number = 5 * Math.PI / 180;
addEventListener(MouseEvent.MOUSE_DOWN, xMouseDown);
function xMouseDown(eventObject:MouseEvent):void {
  nX = parent.mouseX;
  nY = parent.mouseY;
  addEventListener(Event.ENTER_FRAME, xDrag);
  stage.addEventListener(MouseEvent.MOUSE_UP, xMouseUp);
}
function xMouseUp(eventObject:MouseEvent):void {
  removeEventListener(Event.ENTER_FRAME, xDrag);
  stage.removeEventListener(MouseEvent.MOUSE_UP, xMouseUp);
}
function xDrag(eventObject:Event):void {
  var myMatrix:Matrix = transform.matrix;
  myMatrix.translate(-nX, -nY);
  myMatrix.rotate(angularVelocity);
  myMatrix.translate(nX, nY);
  transform.matrix = myMatrix;   // Matrixオブジェクトを再設定
}

インスタンスをクリックするInteractiveObject.mouseDownイベント)とリスナー関数(xMouseDown())が呼出され、親インスタンスから見たマウス座標を変数(nXとnY)に記録する。また、インスタンスのDisplayObject.enterFrameイベントに回転のアニメーション(xDrag()⁠⁠、StageオブジェクトのInteractiveObject.mouseUpイベントにその停止(xMouseUp())のリスナー関数をそれぞれ登録している。

回転のアニメーションを行うリスナー関数(xDrag())は、まずインスタンスのプロパティDisplayObject.transformからTransform.matrixを参照して、インスタンスに適用されているMatrixオブジェクト(myMatrix)を得る。つぎに、前述のとおり、インスタンスのクリック位置を一旦親インスタンスの基準点に平行移動Matrix.translate()して、そこで回転Matrix.rotate())してから、もとの位置に戻してMatrix.translate()いる。

この座標変換の手順は、基本的にMatrix3Dクラスと変わらない。しかし、最後にひとつ大きく異なる点がある。座標変換を加えたMatrixオブジェクトは、改めてインスタンスのtransform.matrixに設定し直さなければならないことだ。この設定を忘れると、インスタンスは何も変わらない。

MatrixTransformerクラスのメソッドでインスタンスを任意の座標で回す

MatrixTransformerクラスのメソッドを使うと、インスタンスが任意の座標で回せる。MatrixTransformerクラスはMatrixオブジェクトを扱う静的メソッドで成立ち、Matrixクラスを補うクラスだ。静的メソッドMatrixTransformer.rotateAroundExternalPoint()は、Matrixオブジェクトを親から見た指定座標で回す[1]⁠。

MatrixTransformer.rotateAroundExternalPoint(Matrixオブジェクト, 親空間のx座標, 親空間のy座標, 度数角)

第4引数に渡す角度が度数であることに注意してほしい。クラスによって角度の単位がまちまちなのはいただけない。自衛のためには、角度絡みの新しいプロパティやメソッドを使うときは、必ずヘルプを確かめることしかない。

前掲スクリプト1の座標変換をMatrixTransformer.rotateAroundExternalPoint()メソッドで書替えたのがつぎのスクリプト2だ。アニメーションのリスナー関数(xDrag())の中身がすっきりした。また、フレームあたりの回転角(angularVelocity)は度数に直している。そして、fl.motionパッケージはフレームアクションに自動的には読込まれないので、MatrixTransformerクラスのimport宣言が加えられた。

スクリプト2 MatrixTransformer.rotateAroundExternalPoint()メソッドで指定座標を中心に回す
// フレームアクション: マウスプレスした座標で回すMovieClipシンボル
import fl.motion.MatrixTransformer;
var nX:Number;
var nY:Number;
var angularVelocity:Number = 5;   // * Math.PI / 180;
addEventListener(MouseEvent.MOUSE_DOWN, xMouseDown);
function xMouseDown(eventObject:MouseEvent):void {
  nX = parent.mouseX;
  nY = parent.mouseY;
  addEventListener(Event.ENTER_FRAME, xDrag);
  stage.addEventListener(MouseEvent.MOUSE_UP, xMouseUp);
}
function xMouseUp(eventObject:MouseEvent):void {
  removeEventListener(Event.ENTER_FRAME, xDrag);
  stage.removeEventListener(MouseEvent.MOUSE_UP, xMouseUp);
}
function xDrag(eventObject:Event):void {
  var myMatrix:Matrix = transform.matrix;
  /*
  myMatrix.translate(-nX, -nY);
  myMatrix.rotate(angularVelocity);
  myMatrix.translate(nX, nY);
  */
  MatrixTransformer.rotateAroundExternalPoint(myMatrix, nX, nY, angularVelocity);
  transform.matrix = myMatrix;
}

インスタンスを回しながらドラッグする

今回の課題としては、あとインスタンスを回しながらドラッグできればよい。回す速さは一定のままで、マウスボタンを放したら直ちに止まってしまってよい。残りは次回に取組む第2の課題だ。

手を加えるのは、前掲スクリプト2よりスクリプト1の方がやりやすい。ステートメント数が減るというのは、簡単になる反面、細かな調整はしにくくなりがちなのだ。

スクリプト1のアニメーションのリスナー関数(xDrag)における座標変換は、⁠1)インスタンスを都合の良い位置に移動し、⁠2)回転の変換を加えてから、⁠3)もとの位置に戻した。インスタンスをドラッグするには、⁠3)で新たなマウスポインタの座標に移動すればよい。たとえば、関数をつぎのように修正する。

function xDrag(eventObject:Event):void {
  var nNewX:Number = parent.mouseX;
  var nNewY:Number = parent.mouseY;
  var myMatrix:Matrix = transform.matrix;
  myMatrix.translate(-nX, -nY);
  myMatrix.rotate(angularVelocity);
  // myMatrix.translate(nX, nY);
  myMatrix.translate(nNewX, nNewY);
  transform.matrix = myMatrix;
  nX = nNewX;
  nY = nNewY;
}

今回の課題かぎりであれば、この修正で差支えない。しかし、次回の課題ではベクトルの演算が中心になる。そこで、xy座標はPointオブジェクトで扱うように整理しておこう。前掲スクリプト1をそのように書替えた結果がつぎのスクリプト3だ。インスタンスは初めにクリックした座標を中心に回りながら、ドラッグできるようになる図3⁠。

スクリプト3 インスタンスをクリックした座標で回しながらドラッグする
// フレームアクション: クリックした座標で回しながらドラッグするMovieClipシンボル
// var nX:Number;
// var nY:Number;
var lastMouse:Point;
var angularVelocity:Number = 5 * Math.PI / 180;
addEventListener(MouseEvent.MOUSE_DOWN, xMouseDown);
function xMouseDown(eventObject:MouseEvent):void {
  // nX = parent.mouseX;
  // nY = parent.mouseY;
  lastMouse = new Point(parent.mouseX, parent.mouseY);
  addEventListener(Event.ENTER_FRAME, xDrag);
  stage.addEventListener(MouseEvent.MOUSE_UP, xMouseUp);
}
function xMouseUp(eventObject:MouseEvent):void {
  removeEventListener(Event.ENTER_FRAME, xDrag);
  stage.removeEventListener(MouseEvent.MOUSE_UP, xMouseUp);
}
function xDrag(eventObject:Event):void {
  var currentMouse:Point = new Point(parent.mouseX, parent.mouseY);
  var myMatrix:Matrix = transform.matrix;
  // myMatrix.translate(-nX, -nY);
  myMatrix.translate(-lastMouse.x, -lastMouse.y);
  myMatrix.rotate(angularVelocity);
  // myMatrix.translate(nX, nY);
  myMatrix.translate(currentMouse.x, currentMouse.y);
  transform.matrix = myMatrix;
  lastMouse = currentMouse.clone();
}
図3 インスタンスは初めにクリックした座標を中心に回りながらドラッグされる
図3 インスタンスは初めにクリックした座標を中心に回りながらドラッグされる

次回はいよいよ、インスタンスの回る速さをベクトルの外積から導く。⁠Stay tuned!」⁠お見逃しなく!※2⁠。

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

おすすめ記事

記事・ニュース一覧