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

第12回マウスポインタとスクロールの連動

前回は、MovieClipインスタンスを水平スクロールさせ、その移動方向をマウスポインタの位置に対応させた。今回はさらに、スクロールスピードも、マウスポインタの位置に連動させよう。

前回作成したスクリプト4図1に修正を加えていく(サンプルファイルは第11回「MovieClipをスクロールさせる―条件判定」2ページ目からダウンロードできる⁠⁠。

図1 インスタンスをマウスポインタの位置に応じて水平方スクロールさせるフレームアクション
図1 インスタンスをマウスポインタの位置に応じて水平方スクロールさせるフレームアクション

マウスポインタの中央からの水平位置にスピードを比例させる

まず、マウスポインタのステージ中央からの水平位置に、インスタンスがスクロールするスピードを比例させたい。その場合、ステージ中央とマウスポインタとの水平座標の差を取ればよい。その結果の数値は、マウスポインタの水平位置がステージの左側か右側かによって、正負の符号が変わる。そのため、前回のスクリプト4に加えられている、スクロール方向を判定するif条件の処理は、もはや要らなくなる。

したがって、水平スクロールするピクセル数nNewSpeedを決める処理は、元のスクリプト4を以下のように修正する。なお、修正の経過がわかるように、元のステートメントはコメント化して残した[1]⁠。また、ブロックコメント区切り記号を使うと、開始の演算子/*から終了の演算子*/まで、複数の行をまとめてコメントにすることができる。

// var nNewSpeed:Number;   // つぎのステートメントに変更
var nNewSpeed:Number = nStageCenter-stage.mouseX;
/* スクロール方向の判定は不要になる
if (stage.mouseX&nStageCenter) {
  nNewSpeed = nSpeed;
} else {
  nNewSpeed = -nSpeed;
}
*/

もっともスピードを、ステージ中央からのマウスポインタの水平位置に単純に比例させたのでは、少し問題がある。マウスポインタをステージ中央付近で動かしていればよいものの、ステージの端にポインタを移動すると1フレーム当たりの移動距離がステージ幅の約半分のピクセル数になってしまうからだ。2フレームでステージ外に飛出してしまったのでは、左右どちらに移動しているのかももはやわからない。

そこで、スピード制限が必要になる。最高速度は、毎フレーム±20ピクセルとしよう。つまり、ステージ中央とマウスポインタとの水平座標の差が±20を超えたら(20より大きいか、-20より小さかったら⁠⁠、移動ピクセル数を±20に設定し直すということだ。これは、if条件を使う処理の復習問題として丁度よいだろう。上述の修正で変数nSpeedが使われなくなったので、改めて最高速度の正の値20を納めることにする。

以上の修正を加えたMovieClipインスタンスのフレームアクションが、以下のスクリプト001である。[ムービープレビュー]で確かめると、マウスポインタの水平位置が中央から遠くなるほど、インスタンスのスクロールスピードが増す図2⁠。

スクリプト1 マウスポインタの中央からの水平位置にスクロールスピードを比例させる
// MovieClip: スクロールさせるインスタンス
var nSpeed:Number = 20;
var nStageLeft:Number = 0;
var nStageRight:Number = stage.stageWidth;
var nStageWidth:Number = nStageRight-nStageLeft;
var nStageCenter:Number = (nStageRight+nStageLeft)/2;
var nSensitivity:Number = 0.2;
addEventListener(Event.ENTER_FRAME, xScroll);
function xScroll(eventObject:Event):void {
  var nNewSpeed:Number = (nStageCenter-stage.mouseX)*nSensitivity;
  if (nNewSpeed>nSpeed) {
    nNewSpeed = nSpeed;
  } else if (nNewSpeed<-nSpeed) {
    nNewSpeed = -nSpeed;
  }
  x += nNewSpeed;
  if (x>nStageRight) {
    x -= nStageWidth;
  } else if (x&nStageLeft) {
    x += nStageWidth;
  }
}
図2 インスタンスのスクロールスピードがマウスポインタの中央からの水平位置に比例する
図2 インスタンスのスクロールスピードがマウスポインタの中央からの水平位置に比例する

なお、上記スクリプト1には、新たに変数 nSensitivityが追加された。そして、ステージ中央とマウスポインタとの水平座標の差に、この値が掛合わされて、水平移動するピクセル数 nNewSpeedに代入されている。この変数nSensitivityは、マウスポインタの水平位置にスクロールスピードが比例する度合いを調整する係数だ。

この係数がない場合、スクロールするピクセル数そのものは、変数nSpeedを用いたif条件のステートメントによって±20の範囲内に抑えられている。しかし、移動ピクセル数を単純にステージ中央とマウスポインタとの水平座標の差に比例させれば、ポインタをステージ中央から左右各20ピクセル動かしただけで最高速度に達してしまう。

比例係数として変数nSensitivityの値0.2を掛け合わせることによって、ステージ中央からマウスポインタを左右100ピクセル(= 20/0.2)動かさないと、最高速度にはならなくなる。つまり、スピードの微調整がしやすくなるということだ。

プロパティ値の有効桁数

できあがったスクリプト1は、実際に使ったとき少々不具合を生じることがある。このフレームアクションの設定されたインスタンスを複数、横にすき間なくびったりと並べて、10分程度以上スクロールさせ続けてみる。すると、すき間がなかったはずのインスタンス間に間隔が空いたり、逆に重なったりしてしまう図3⁠。

図3 スクールさせ続けるとインスタンス同士の間隔にばらつきが生じる
図3 スクールさせ続けるとインスタンス同士の間隔にばらつきが生じる

これはインスタンスの座標のプロパティ値に、誤差のあることが原因だ。たとえば、[プロパティ]インスペクタで、x座標値として「123.456789」と入力してみよう図4⁠。すると、実際に設定される値は「123.4」となり、以降の端数は切捨てられてしまう。

図4 x座標に小数点以下の数値を入れると端数が切捨てられる
図4 x座標に小数点以下の数値を入れると端数が切捨てられる

ActionScriptでも、DisplayObject.xプロパティに小数点以下の端数がある数値を設定すると、小数点以下第2位までの値に丸められてしまうのだ。前述のように複数のインスタンスにフレームアクションを設定した場合も、それぞれに毎フレーム設定される座標値は端数が毎回切捨てられる。そのカットされる数値の大きさは、当然インスタンスによって変わってくる。その違いが誤差になる訳だ。

小数点以下第3位以降の端数が小さいといっても、デフォルトのフレームレート(12fps)で1秒間に12回10分以上処理を繰返せば、累積の差が1ピクセル以上になっても不思議はない。その差が、インスタンス同士の間隔のばらつきとして現れることになる。

しかし、変数は最大で小数点以下15桁の値を保持できる[2]⁠。したがって、座標値を変数に入れて扱えば、目に見える誤差が出ない十分な精度で処理できる。そこで、座標値を新たな変数nXに入れて処理するよう修正したのが、次のスクリプト2だ。

スクリプト2 座標値の処理を変数で行うように修正
// MovieClip: スクロールさせるインスタンス
var nSpeed:Number = 20;
var nStageLeft:Number = 0;
var nStageRight:Number = stage.stageWidth;
var nStageWidth:Number = nStageRight-nStageLeft;
var nStageCenter:Number = (nStageRight+nStageLeft)/2;
var nSensitivity:Number = 0.2;
var nX:Number = x;
addEventListener(Event.ENTER_FRAME, xScroll);
function xScroll(eventObject:Event):void {
  var nNewSpeed:Number = (nStageCenter-stage.mouseX)*nSensitivity;
  if (nNewSpeed>nSpeed) {
    nNewSpeed = nSpeed;
  } else if (nNewSpeed<-nSpeed) {
    nNewSpeed = -nSpeed;
  }
  nX += nNewSpeed;
  if (nX>nStageRight) {
    nX -= nStageWidth;
  } else if (nX<nStageLeft) {
    nX += nStageWidth;
  }
  x = nX;
}

注目してほしいのは、変数nXにDisplayObject.xプロパティの値を代入したのは、変数宣言の初期化時のみだということである。リスナー関数xScroll()内では、変数nXに対して座標計算の処理を行う。そして、関数xScroll()の最後のステートメントで、DisplayObject.xプロパティに変数nXの値を設定している。つまり、変数宣言時を除いて、処理は変数値をプロパティに代入する一方通行のみのなのである。

これはたとえば、JPEG画像を作成する場合と同じ考慮だ。 JPEG画像に修正が必要なとき、そのJPEGファイルを開くのではなく、劣化しない形式(PNGやPSDなど)で保存しておいた元の画像に手を加えるだろう。JPEGファイルを開いて再びJPEG保存すれば、画像がさらに劣化してしまうからだ。

変数も宣言時にプロパティの初期値を保持したら、以降は2度とプロパティ値で上書きしない。それをしてしまうと、プロパティのもつ誤差により、変数値が「劣化」することになる。したがって、上記スクリプト2では、値の設定を変数からプロパティへの一方通行としているのだ。これで、横に並べたインスタンスをスクロールさせ続けても、ピクセル単位の誤差が生じることは防げる図5⁠。

図5 値を変数からプロパティの一方通行にすれば誤差が拡大しない
図5 値を変数からプロパティの一方通行にすれば誤差が拡大しない

マウスポインタに連動してインスタンスをスクロールさせるムービーはこれで完成だ。次回は、キーボードのキーで、インスタンスをコントロールしてみたい。

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

おすすめ記事

記事・ニュース一覧