Away3D TypeScriptではじめる3次元表現

第17回 ロゴがパーティクルで弾けるアニメーション

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

パーティクルを初期化する関数

パーティクルを初期化する関数は,前述のParticleAnimationSetオブジェクトをつくる関数(createColorAnimationSet())ParticleAnimationSet.initParticleFuncプロパティに定めたinitColorParticle()だ。

プロパティParticleProperties.startTimeParticleProperties.durationについてはよいだろう(第12回パーティクルのアニメーションを動かす参照⁠⁠。パーティクルのカラーと座標は,配列の変数(colorValuesとcolorPoints)からインデックス(index)にしたがって取り出し,それぞれ引数のオブジェクト(properties)ParticleInitialColorNode.COLOR_INITIAL_COLORTRANSFORMParticlePositionNode.POSITION_VECTOR3Dプロパティに定める。

今回のアニメーションの動きを決める鍵となるのは,ParticleBezierCurveNodeのプロパティの定めだ。名前から想像がつくように,ベジエ曲線で動き方を決める。その終点(アンカーポイント)ParticleBezierCurveNode.BEZIER_END_VECTOR3DコントロールポイントがParticleBezierCurveNode.BEZIER_CONTROL_VECTOR3Dで,ともにVector3Dオブジェクトを与える。前者(endPoint)は引数なしのVector3D()コンストラクタでつくられた値なのでデフォルト値の原点(0, 0, 0⁠⁠,つまりもとの位置だ。後者は,別に定めた関数getRandomVector3D()により,引数の距離以内のランダムなVector3Dオブジェクトが与えられる。

すると,パーティクルのアニメーションは,コントロールポイントにより一旦初めの位置から遠ざかり,その後終点に定めたもとの位置に戻ってくることになる。なお,関数getRandomVector3D()の距離とふたつの角度から座標を求める計算については,第13回パーティクルの動きにランダムな広がりを与えるを参照してほしい。

function initColorParticle(properties) {
  var BEZIER_END_VECTOR3D = ParticleBezierCurveNode.BEZIER_END_VECTOR3D;

  var index = properties.index;
  var endPoint = new Vector3D();
  var rgb = colorValues[index];
  properties.startTime = 0;
  properties.duration = 1;
  properties[BEZIER_END_VECTOR3D] = endPoint;
  properties[ParticleInitialColorNode.COLOR_INITIAL_COLORTRANSFORM] = new ColorTransform(rgb.x, rgb.y, rgb.z, 1);
  properties[ParticleBezierCurveNode.BEZIER_CONTROL_VECTOR3D] = getRandomVector3D(100);
  properties[ParticlePositionNode.POSITION_VECTOR3D] = colorPoints[index];
}
function getRandomVector3D(radius) {
  var angle0 = Math.random() * Math.PI * 2;
  var angle1 = Math.random() * Math.PI * 2;
  var x = radius * Math.cos(angle0) * Math.cos(angle1);
  var y = radius * Math.cos(angle0) * Math.sin(angle1);
  var z = radius * Math.sin(angle0);
  return new Vector3D(x, y, z);
}

パーティクルをアニメーションさせる関数

関数setListeners()は,RequestAnimationFrameオブジェクトでアニメーションのコールバック関数render()を定めている。この関数からAnimatorBase.update()メソッドを呼び出した。総経過時間を引数として,アニメーションの状態が改められる。これがこのアニメーションの2つ目の鍵だ。

RequestAnimationFrame()コンストラクタに渡したコールバック関数(render())は,引数として前回の呼び出しからの経過時間(deltaTime)を受け取る。その値を変数(time)に加えて,総経過時間をもたせた。ところが,AnimatorBase.update()メソッドにはこの値を直に与えず,Math.sin()メソッドで手を加えた値(_time)が渡されている。この式が,初めに掲げたサンプル1の動きをつくっているのだ。

var time = 0;

function setListeners() {
  timer = new RequestAnimationFrame(render);
  timer.start();

}

function render(deltaTime) {
  time += deltaTime;
  if (colorAnimator) {
    var _time = 1000 * (Math.sin(time / 5000) + 1);
    colorAnimator.update(_time);
  }
  view.render();
}

試しに,前掲コードをつぎのように書き替えて,AnimatorBase.update()メソッドに総経過時間(time)に調整係数を乗じて引数として渡してみよう。前述のとおり,ParticleBezierCurveNodeのプロパティでベジエ曲線のコントロールポイントをランダムな距離で外側に定めたため,パーティクルはばらばらに広がり出す。だが,終点(アンカーポイント)はもとの位置なので,やがて引き戻されていく。そして,その先は慣性にしたがってそのまま進み続け,やがて画面の外に消えてしまう。これが,総経過時間をひたすら増やした結果だ。

function render(deltaTime) {
  time += deltaTime;

  // var _time = 1000 * (Math.sin(time / 5000) + 1);
  // colorAnimator.update(_time);
  colorAnimator.update(time / 5);

}

三角関数のsinは,物理のバネ運動の式に用いられ,±1の範囲で増減を繰り返す図3⁠。前掲の式では1を加えているので,0~2の間をバネのように変化する。さらに調整係数で,変化の速さと幅を決めた結果,0から一定時間の間を行ったり来たりすることになった。そのため,パーティクルが広がったり,縮まったりというアニメーションを繰り返したのだ。

図3 sin関数はバネ運動のように値の増減を繰り返す

図3 sin関数はバネ運動のように値の増減を繰り返す

WikipediaHarmonic oscillatorより。

ようやく,ロゴひとつをパーティクルでアニメーションさせるコード1が説明し終えた。jsdo.itに掲げたサンプル1も試してみるとよいだろう。

次回からは,書き加えたコードの動きを確かめながら解説する。実は,今回のパーティクルのお題が,この連載の結びとなる。あとしばらくおつき合い願いたい。

著者プロフィール

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

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

URLhttp://www.FumioNonaka.com/

著書