前回の第23回
![図1 複数のインスタンスが楕円軌道でアニメーションする 図1 複数のインスタンスが楕円軌道でアニメーションする](/assets/images/dev/serial/01/as3/0024/thumb/TH800_01.gif)
今回の課題は、
![図2 インスタンスの重ね順が3次元風の表現に一部対応していない 図2 インスタンスの重ね順が3次元風の表現に一部対応していない](/assets/images/dev/serial/01/as3/0024/thumb/TH400_02.gif)
マウスポインタの位置に応じてインスタンスの回転スビードを変える
まずは、
今のところ、
public class EllipticMotion extends MovieClip {
// ...[中略]...
public function EllipticMotion(nDegree:Number, myCenter:Point, myRadius:Point) {
degree = nDegree;
center = myCenter;
radius = myRadius;
addEventListener(Event.ENTER_FRAME, rotate); // 後に削除
setRotation();
}
しかし、
そうだとすれば、
Flashムービー
var instances_array:Array = new Array(); // 追加: 配列を変数として宣言・初期化
for (var i:int = 0; i < nCount; i++) {
var _mc:EllipticMotion = new EllipticMotion(nDegree * i,myCenter,myRadius);
addChild(_mc);
instances_array.push(_mc); // 追加: 配列にインスタンスを加える
}
これで、
var deceleration:Number = 0.1;
// ...[中略]...
addEventListener(Event.ENTER_FRAME, rotate);
function rotate(EventObject:Event):void { // 追加: リスナー関数
var nSpeed:Number = (stage.mouseX - myCenter.x)*deceleration;
var nLength:uint = instances_array.length;
for (var i:int = 0; i<nLength; i++) {
var _mc:EllipticMotion = instances_array[i]; // インスタンスの取出し
_mc.rotate(nSpeed); // インスタンスのrotate()メソッドを呼出す
}
}
rotate()関数本体では、
以上ふたつの修正を加えたフレームアクションが、
// タイムライン: メイン
var myCenter:Point = new Point(stage.stageWidth / 2, stage.stageHeight / 2);
var myRadius:Point = new Point(100, 50);
var nCount:uint = 6;
var nDegree:Number = 360 / nCount;
var instances_array:Array = new Array();
var deceleration:Number = 0.1;
for (var i:int = 0; i < nCount; i++) {
var _mc:EllipticMotion = new EllipticMotion(nDegree * i, myCenter, myRadius);
addChild(_mc);
instances_array.push(_mc);
}
addEventListener(Event.ENTER_FRAME, rotate);
function rotate(EventObject:Event):void {
var nSpeed:Number = (stage.mouseX - myCenter.x) * deceleration;
var nLength:uint = instances_array.length;
for (var i:int = 0; i < nLength; i++) {
var _mc:EllipticMotion = instances_array[i];
_mc.rotate(nSpeed);
}
}
この変更にともなって、
まずは、
public class EllipticMotion extends MovieClip {
// ...[中略]...
public function EllipticMotion(nDegree:Number, myCenter:Point, myRadius:Point) {
degree = nDegree;
center = myCenter;
radius = myRadius;
// addEventListener(Event.ENTER_FRAME, rotate); // 削除
setRotation();
}
続いて、
// private function rotate(eventObject:Event):void {
public function rotate(nSpeed:Number):void {
// degree += speed; // プロパティspeedは不要に
degree += nSpeed;
setRotation();
}
以上のふたつの修正を加えたのが、
// ActionScript 3.0クラス定義ファイル: EllipticMotion.as
// 回転するMovieClipシンボルの[クラス]に設定
package {
import flash.display.MovieClip;
import flash.events.Event;
import flash.filters.BlurFilter;
import flash.geom.Point;
public class EllipticMotion extends MovieClip {
private var _degree:Number = 0;
private const DEGREE_TO_RADIAN:Number = Math.PI / 180;
private var center:Point;
private var radius:Point;
private var cos:Number = Math.cos(degree * DEGREE_TO_RADIAN);
private var sin:Number = Math.sin(degree * DEGREE_TO_RADIAN);
public function EllipticMotion(nDegree:Number, myCenter:Point, myRadius:Point) {
degree = nDegree;
center = myCenter;
radius = myRadius;
setRotation();
}
private function get degree():Number {
return _degree;
}
private function set degree(nDegree:Number):void {
_degree = (nDegree % 360 + 360) % 360;
var radian:Number = _degree * DEGREE_TO_RADIAN;
cos = Math.cos(radian);
sin = Math.sin(radian);
}
public function rotate(nSpeed:Number):void {
degree += nSpeed;
setRotation();
}
function setRotation():void {
moveX();
moveY();
scale();
blur();
}
private function moveX():void {
x = center.x + cos * radius.x;
}
private function moveY():void {
y = center.y + sin * radius.y;
}
private function scale():void {
scaleX = scaleY = getIndexZ(0.8, 1);
scaleX *= getIndexZ();
}
private function blur():void {
var nBlur:Number = getIndexZ(4, 0);
var myBlur:BlurFilter = new BlurFilter(nBlur, nBlur/2);
filters = [myBlur];
}
private function getIndexZ(nMin:Number=-1, nMax:Number=1):Number {
var nIndexZ:Number = (nMax - nMin) * (sin + 1) / 2 + nMin;
return nIndexZ;
}
}
}
[ムービープレビュー]を試すと、
![図3 マウスポインタのステージ中央からの水平位置により回転の方向とスピードが変わる 図32 マウスポインタのステージ中央からの水平位置により回転の方向とスピードが変わる](/assets/images/dev/serial/01/as3/0024/thumb/TH400_03.gif)
インスタンスの重ね順を管理する
インスタンスの重ね順を3次元風の表現に合わせて変えるには、
var my_array:Array = [3, 0, 1, 4, 2];
my_array.sort();
trace(my_array); // 出力: 0,1,2,3,4
このArray.
var my_array:Array = [30, 0, 1000, 4, 200];
my_array.sort();
trace(my_array); // 出力: 0,1000,200,30,4
Array.
配列.sort(比較関数);
function 比較関数(引数1:データ型, 引数2:データ型):Number {
// ふたつの引数の比較評価の結果を数値で返す
}
Array.
引数の大小 | 戻り値 |
---|---|
引数1 < 引数2 | -1 |
引数1 = 引数2 | 0 |
引数1 > 引数2 | 1 |
たとえば、
var my_array:Array = [30, 0, 1000, 4, 200];
my_array.sort(compare);
function compare(nA:Number, nB:Number):Number {
if (nA < nB) {
return -1;
} else if (nA > nB) {
return 1;
} else {
return 0;
}
}
trace(my_array); // 出力: 0,4,30,200,1000
ところで、
var my_array:Array = [30, 0, 1000, 4, 200];
my_array.sort(Array.NUMERIC);
trace(my_array); // 出力: 0,4,30,200,1000
定数 | 説明 |
---|---|
CASEINSENSITIVE | 英字の大文字小文字を区別せずに並べ替える。 |
DESCENDING | 並べ替えの順序を、 |
NUMERIC | エレメントが数値の配列を、 |
RETURNINDEXEDARRAY | ターゲットの配列は変更せず、 |
UNIQUESORT | エレメントに重複がない配列のみを並べ替える。重複があれば、 |
本題に戻って、
function compare(a:EllipticMotion, b:EllipticMotion):Number {
// 各インスタンスの仮想3次元の奥行きを調べる
var nA:Number = a.getIndexZ();
var nB:Number = b.getIndexZ();
// 各インスタンスの奥行きに応じて並べ順を返す
if (nA < nB) {
return -1;
} else if (nA > nB) {
return 1;
} else {
return 0;
}
}
仮想3次元の奥行きの順に配列
表示リスト内におけるインスタンスのインデックスの位置を変えるのは、
親インスタンス.setChildIndex(子インスタンス:DisplayObject, インデックス:int):void
ここでメインタイムラインのフレームアクションに、
function setOrder():void {
instances_array.sort(compare); // 前掲比較関数で並べ替え
var nLength:uint = instances_array.length;
for (var i:int = 0; i<nLength; i++) { // 並べ替えた配列内のインスタンスを順に取出し
var _mc:EllipticMotion = instances_array[i];
setChildIndex(_mc, i); // 表示リスト内のインデックスを設定
}
}
以上、
// タイムライン: メイン
var myCenter:Point = new Point(stage.stageWidth / 2,stage.stageHeight / 2);
var myRadius:Point = new Point(100,50);
var nCount:uint = 6;
var nDegree:Number = 360 / nCount;
var instances_array:Array = new Array();
var deceleration:Number = 0.1;
for (var i:int = 0; i < nCount; i++) {
var _mc:EllipticMotion = new EllipticMotion(nDegree * i,myCenter,myRadius);
addChild(_mc);
instances_array.push(_mc);
}
addEventListener(Event.ENTER_FRAME, rotate);
function rotate(EventObject:Event):void {
var nSpeed:Number = (stage.mouseX - myCenter.x) * deceleration;
var nLength:uint = instances_array.length;
for (var i:int = 0; i < nLength; i++) {
var _mc:EllipticMotion = instances_array[i];
_mc.rotate(nSpeed);
}
setOrder(); // 追加: 関数呼出し
}
function setOrder():void { // 追加定義
instances_array.sort(compare);
var nLength:uint = instances_array.length;
for (var i:int = 0; i<nLength; i++) {
var _mc:EllipticMotion = instances_array[i];
setChildIndex(_mc, i);
}
}
function compare(a:EllipticMotion, b:EllipticMotion):Number { // 追加定義
var nA:Number = a.getIndexZ();
var nB:Number = b.getIndexZ();
if (nA < nB) {
return -1;
} else if (nA < nB) {
return 1;
} else {
return 0;
}
}
このまま[ムービープレビュー]を確かめると、 EllipticMotionクラスのgetIndexZ()が
function compare(a:EllipticMotion, b:EllipticMotion):Number {
var nA:Number = a.getIndexZ();
var nB:Number = b.getIndexZ();
![図4 メソッドにアクセスできない旨を伝える[コンパイルエラー] 図4 メソッドにアクセスできない旨を伝える[コンパイルエラー]](/assets/images/dev/serial/01/as3/0024/thumb/TH800_04.gif)
これは前掲スクリプト2でEllipticMotionクラスのrotate()メソッドについて、
// private function getIndexZ(nMin:Number=-1, nMax:Number=1):Number {
public function getIndexZ(nMin:Number=-1, nMax:Number=1):Number {
var nIndexZ:Number = (nMax-nMin)*(sin+1)/2+nMin;
return nIndexZ;
}
スクリプト2に追加する修正はこの点のみなので、 EllipticMotionクラスの定義全体を再掲することは控えよう。サンプルファイルをダウンロードして確かめてほしい。今度は[ムービープレビュー]がエラーなく行え、
![図5 仮想3次元の奥行きに応じてインスタンスの重ね順が設定される 図5 仮想3次元の奥行きに応じてインスタンスの重ね順が設定される](/assets/images/dev/serial/01/as3/0024/thumb/TH400_05.gif)
今回解説した次のサンプルファイルがダウンロードできます。