この解説は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()
メソッドが実行されることが保証されます。