この解説はThree.jsでES2015のclassを利用する(継承)からの続きです。クラスのメソッドを呼び出すベスト・プラクティスな例を学んでいきましょう。

クラスのメソッドを利用する

時間経過でグループのアニメーションをさせたい場合の方法を紹介します。

時間経過はrequestAnimationFrame()メソッドを使いたいところですが、いたる所にrequestAnimationFrame()メソッドを記述するのはベストプラクティスとは言えません。複数のrequestAnimationFrame()メソッドがあったときに、どれがはじめに実行されるのか、処理の実行順がわかりにくいからです。また、負荷の観点からも複数のrequestAnimationFrame()メソッドを定義するのは最適とは言えません。

悪い例

良くないコード例から見てみます。

/** メッシュを継承した独自グループのクラスです。 */
class MyGroup extends THREE.Object3D {

  /** コンストラクターです。 */
  constructor() {
    super();
    // 任意の処理
    this.update();
  }

  /** 更新命令を定義します。 */
  update() {
    requestAnimationFrame(this.update);
  }
}

// 独自グループを作る
const myGroup = new MyGroup();
scene.add(myGroup);

tick();

// 毎フレーム時に実行されるループイベントです
function tick() {

  // レンダリング
  renderer.render(scene, camera);
  requestAnimationFrame(tick);
}

良くないのは親となるコードにも、子供のクラスにもrequestAnimationFrame()が使われているところです。うまく動くと思いますが、どちらのrequestAnimationFrame()が先に実行されるのか、明示的にわからなくなります。メインコードにレンダリングのためのrenderer.render(scene, camera);処理がありますが、その処理の実行前後で子供のMyGroupが実行されるか、保証されません。

改善例

メインとなるコードに一つだけrequestAnimationFrame()メソッドを用意し、そこからツリー構造で独自メソッドを叩いていくといいでしょう。

良い例

/** メッシュを継承した独自グループのクラスです。 */
class MyGroup extends THREE.Object3D {

  /** コンストラクターです。 */
  constructor() {
    super();
    // 任意の処理
  }

  /** 更新命令を定義します。 */
  update() {
  }
}

// 独自グループを作る
const myGroup = new MyGroup();
scene.add(myGroup);

tick();

// 毎フレーム時に実行されるループイベントです
function tick() {

  // 更新命令を実行します。
  myGroup.update();

  // レンダリング
  renderer.render(scene, camera);
  requestAnimationFrame(tick);
}

こうすれば、requestAnimationFrame()メソッドが実行されるのはメインコードの一箇所のみになります。メインコードにレンダリングのためのrenderer.render(scene, camera);処理の前に子供のMyGroupクラスのupdate()メソッドが実行されることが保証されます。