サイトトップ

Director Flash 書籍 業務内容 プロフィール

Macromedia Flash非公式テクニカルノート

同じスクリプトで動かしているMovieClipの間隔がばらつく

ID: FN0303005 Product: Flash

Platform: All
Version: MX and Above

複数のMovieClipを並べて、同じ処理構造でアニメーションさせるスクリプトを設定すれば、それらの間隔は一定で変わらないはずです。ところが、その間隔に小さな誤差が生じて、ばらついてしまうことがあります。

■アニメーション開始時

等間隔にぴったりと並んでいる

 

■アニメーションをある程度継続すると

間隔にばらつきが生じて、重なったり離れたりするものが現れる。

間隔をぴったりつめていたり、他のグラフィックとの正確な位置関係を維持したい場合には、このばらつきが問題になります。サンプルSWF(再生にはFlash Player 6以降が必要です)は、左右の加速度的なアニメーションを周期的に繰返します。数分程度経過すると、上部の×印をつけた4つのMovieClipの間隔にばらつきが生じてきます。それに対して、下部の○印のMovieClipには、以下で述べる誤差を回避する対処法が施されています(ソースFLAファイル/Flash MX用Zip圧縮:約5KB)。

1. 誤差が発生する理由
MovieClipの'_x'や'_y'プロパティに小さな端数を含む小数値を指定すると、設定される座標値は端数が切捨てられます。たとえば、メインのタイムラインにMovieClipインスタンスmy_mcを配置して、メインのタイムラインのフレームに以下のスクリプトを記述してみましょう。

// メインのタイムライン
// フレームアクション
_root.my_mc._x = 123.456789;
trace(_root.my_mc._x);

「出力」ウィンドウには、つぎのように表示されます。

//「出力」ウィンドウの結果
123.45

つまり、設定された小数値の第3位以下が切捨てられたということです。もっとも、小数第3位以下の差は、ごくわずかです。これだけでは、目に見える差異にはなりません。

問題は、座標値として小数を加算し続けた場合です。この処理は、加(減)速度運動をシミュレートする場合に典型的に用いられます。たとえば、メインのタイムラインに配置したMovieClipを、目的の座標値100に減速しながら移動するには、以下のMovieClipアクションを設定します。

// MovieClip: メインのタイムラインに配置
// MovieClipアクション
// 目的の座標値100に減速しながら近づく
onClipEvent (enterFrame) {
  _x += (100-_x)*0.2;
  trace(_x);   // 設定されたx座標値確認用
}

このように小数を座標値に加算し続けると、そのたびに端数が切捨てられます。MovieClipを単独で見る分には問題のない誤差でも、複数のMovieClipで一定時間以上の処理を続けて比較すれば、切捨てられた値の累計が目に見える差異にまで拡大するのです。

2. 誤差の発生を防ぐには
それでは、この誤差を生じないようにするには、どうしたらよいでしょうか。つぎのスクリプトは、上記サンプルで誤差が発生した方の×印のついたMovieClipに設定したものです。アニメーションするムービークリップシンボルの第1フレームアクションとして記述されています。

// MovieClip: 周期的なアニメーションを行う
// フレームアクション
//[1]初期設定
// アニメーションの範囲の両端の座標および幅を変数設定
nLeft = -this._width/2;
nRight = Stage.width+this._width/2;
nWidth = Stage.width+this._width;
//[2]アニメーションのためのイベントハンドラメソッド定義
this.onEnterFrame = function() {
  //[3]sinカーブに比例した小数値を座標に加算
  _x -= 20*(Math.sin(Math.PI/180*(++n)));
  //[4]両端の座標の外側に行ったら反対側の端に戻す
  if (_x<nLeft) {
    _x += nWidth;
  } else if (_x>nRight) {
    _x -= nWidth;
  }
};

このスクリプトの内容を解析することは、本稿の目的ではありません。ですから、処理内容は簡単に触れるだけにとどめます。

[1]MovieClipのアニメーションは、その両端の座標を定め、その外側に行ったら反対側の端に移動するというものです。ですから、スクリプトの初期設定のステートメントでは、それらの値を変数に格納してまいす。

[2]アニメーションの処理は、MovieClipのイベントハンドラメソッドに定義しています。'MovieClip.onEnterFrame'メソッドは、Flashが画面の描画を更新するたびに実行される処理になります。

[3]MovieClipの'_x'プロパティに小数値を加算しています。誤差を発生させる例として、ここではsinカーブの数値を増幅して加算する値としました(この計算式自体には、あまり意味はありません)。このステートメントが誤差を発生させる原因となっています。

[4]アニメーション範囲の両端を超えたら他方の端に移動する処理を、'if'アクションで行っています。他方の端に位置を移動するとき、ここでも座標値に加算をしています。

問題の原因となり得るのは、'MovieClip._x'プロパティに値を加(減)算している[3]と[4]のステートメントです。これらを修正したのが以下のフレームアクションです。上記サンプルでは、○印をつけたMovieClipに設定されています。

nLeft = -this._width/2;
nRight = Stage.width+this._width/2;
nWidth = Stage.width+this._width;
n_x = _x;   //[1]x座標を浮動小数値で格納するための変数
this.onEnterFrame = function() {
  //[2]x座標の処理は変数を対象として行う
  n_x -= 20*(Math.sin(Math.PI/180*(++n)));
  if (n_x<nLeft) {
    n_x += nWidth;
  } else if (n_x>nRight) {
    n_x -= nWidth;
  }
  _x = n_x;   //[2]処理後の変数値を'_x'プロパティに設定
};

誤差を回避するために変更した点に絞って、ご説明します。

[1]最大のポイントは、x座標値を格納するための変数n_xを別に設けたことです。こうすることで、端数が切捨てられずに、完全な浮動小数値(有効桁数15)で座標計算をすることが可能になります。初期設定では、まずx座標の初期値を変数に代入します。

[2]以降は、先のスクリプトで'_x'プロパティに対して行っていた処理を、すべて変数n_xを対象として行います。

[3]すべての座標値の処理が終わった後に、その結果の値を'_x'プロパティに設定します。もちろん、ここで設定された'_x'プロパティの値は、また端数が切捨てられます。けれども、変数には切捨て前の座標値が保持されており、次回はその値を元に処理されます。ですから、端数の切捨てられた誤差が「累積」することは避けられるのです。

 

_____

作成者: 野中文雄
作成日: 2003年3月10日


Copyright © 2001-2006 Fumio Nonaka.  All rights reserved.