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

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

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

今回,目指すお題の動きはこうだった。矩形のインスタンスをドラッグすると,回るように動く。マウスを振り回せば,インスタンスがくるくる回る。そして放すと,滑るように減速して,やがて止まる。どこかで見た覚えのある動きだ。その前編の第57回インスタンスをクリックした点で回しながらドラッグするでは,ドラッグすると一定の速さで回るところまでできた第57回スクリプト3)⁠インスタンスを回すアニメーションのリスナー関数(xDrag())はつぎに抜書きしたとおりだ。

var lastMouse:Point;
var angularVelocity:Number = 5 * Math.PI / 180;   // 一定値

function xDrag(eventObject:Event):void {
  var currentMouse:Point = new Point(parent.mouseX, parent.mouseY);
  var myMatrix:Matrix = transform.matrix;
  myMatrix.translate(-lastMouse.x, -lastMouse.y);
  myMatrix.rotate(angularVelocity);   // 回転
  myMatrix.translate(currentMouse.x, currentMouse.y);
  transform.matrix = myMatrix;
  lastMouse = currentMouse.clone();
}

後編の今回は,まずマウスでドラッグする位置と向き,および速さによって回転に加速を与える。そして仕上げは,マウスボタンを放すと,慣性で減速しながらやがて止まるようにする。

ドラッグする位置と向きと速さによって回転を加速する

前編で,インスタンスを回す速さは外積から導くと予告した。外積を求めるには,ベクトルがふたつ要る図1)。ひとつは,インスタンスの中心,物理でいう重心からドラッグしている座標までのベクトルだ。この距離は離れているほど,回す勢いがつきやすい。つぎに,マウスを動かす速さだ。ただし,その向きも合わせて考えなければならない。ひとつ目のベクトルに対して垂直な力が,インスタンスを回転させる。例によって,物理のもう少し詳しい説明は本稿の最後に行う。

図1 インスタンスを回す速さはふたつのベクトルで決まる

図1 インスタンスを回す速さはふたつのベクトルで決まる

インスタンスのドラッグで回転を加速するアニメーションのリスナー関数(xDrag())はつぎのようになる。フレームアクション全体は,スクリプト1として後に掲げる。新たに定めた関数(crossProduct2D())が,ベクトルの外積から回転に用いる値を取出して返す。なお,ベクトルの計算に用いた変数名は,前掲図1に書添えてある。

var lastMouse:Point;
var angularVelocity:Number = 0;
var deceleration:Number = 0.8;
var ratio:Number = 5 / width / height;

function xDrag(eventObject:Event):void {
  var currentMouse:Point = new Point(parent.mouseX, parent.mouseY);
  var myMatrix:Matrix = transform.matrix;
  var position:Point = new Point(x, y);
  // 外積を求めるふたつのベクトルの計算
  var radius:Point = lastMouse.subtract(position);
  var force:Point = currentMouse.subtract(lastMouse);
  var moment:Number = crossProduct2D(radius, force);
  angularVelocity += moment * ratio;
  myMatrix.translate(-lastMouse.x, -lastMouse.y);
  myMatrix.rotate(angularVelocity);
  myMatrix.translate(currentMouse.x, currentMouse.y);
  transform.matrix = myMatrix;
  lastMouse = currentMouse.clone();
  angularVelocity *= deceleration;
}
function crossProduct2D(point0:Point, point1:Point):Number {
  var vector0:Vector3D = new Vector3D(point0.x, point0.y, 0);
  var vector1:Vector3D = new Vector3D(point1.x, point1.y, 0);
  var crossProduct3D:Vector3D = vector0.crossProduct(vector1);
  return crossProduct3D.z;
}

ベクトルはPointオブジェクトで定めた。インスタンスの基準点は中心に定めたので,その座標(position)が重心になる。ひとつ目のベクトル(radius)は,ドラッグしている座標(lastMouse)から重心の座標をPoint.subtract()メソッドで差引いて求める。メソッドからは差のベクトルを示すPointインスタンスが返される。ふたつ目のベクトル(force)は,マウスポインタの現在座標(currentMouse)とひとつ前の座標(lastMouse)との差だ。

引かれるPointオブジェクト.subtract(引くPointオブジェクト)

新たに加えた関数(crossProduct2D())は,渡されたふたつのPointオブジェクトをVector3Dインスタンスに直したうえで,Vector3D.crossProduct()メソッドによりふたつのベクトルの外積(crossProduct3D)を求める。そして,外積のVector3Dオブジェクトからz座標値Vector3D.zプロパティ)を取出して返している。ドラッグするインスタンスの回転は,この戻り値(moment)に比例して加速させる。

ただし,外積の大きさはふたつのベクトルの長さの積をもとに計算される。他方で,角度のラジアン値はわずか3.14で1周してしまう。数値の違いが大きすぎるので,調整用の係数を変数(ratio)に定めた。そして,前述関数(crossProduct2D())の戻り値(moment)にこの係数を乗じたうえで,回転角(angularVelocity)に加速度として加えた。

これで,インスタンスをドラッグする位置と方向,およびその大きさによって回転が加速される図2)⁠もっとも,加速しっ放しではまずい。減速の係数も定め(deceleration)⁠リスナー関数(xDrag())の最後で回転角(angularVelocity)に乗じた。フレームアクション全体は,つぎのスクリプト1のとおりだ。

スクリプト1 インスタンスをドラッグで加速して回す

// フレームアクション: マウスでドラッグして回すMovieClipシンボル
var lastMouse:Point;
var angularVelocity:Number = 0;
var deceleration:Number = 0.8;
var ratio:Number = 5 / width / height;
addEventListener(MouseEvent.MOUSE_DOWN, xMouseDown);
function xMouseDown(eventObject:MouseEvent):void {
  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;
  var position:Point = new Point(x, y);
  var radius:Point = lastMouse.subtract(position);
  var force:Point = currentMouse.subtract(lastMouse);
  var moment:Number = crossProduct2D(radius, force);
  angularVelocity += moment * ratio;
  myMatrix.translate(-lastMouse.x, -lastMouse.y);
  myMatrix.rotate(angularVelocity);
  myMatrix.translate(currentMouse.x, currentMouse.y);
  transform.matrix = myMatrix;
  lastMouse = currentMouse.clone();
  angularVelocity *= deceleration;
}
function crossProduct2D(point0:Point, point1:Point):Number {
  var vector0:Vector3D = new Vector3D(point0.x, point0.y, 0);
  var vector1:Vector3D = new Vector3D(point1.x, point1.y, 0);
  var crossProduct3D:Vector3D = vector0.crossProduct(vector1);
  return crossProduct3D.z;
}

図2 マウスのドラッグでインスタンスの回る速さが加速する

図2 マウスのドラッグでインスタンスの回る速さが加速する

著者プロフィール

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

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

URLhttp://www.FumioNonaka.com/

著書

コメント

コメントの記入