【Three.js】複数のシーンに共通するオブジェクトを扱うときの注意

スポンサーリンク
Three.js

先日、Three.jsで複数のシーンを扱おうとしたときに直面したことを共有しようと思います。備忘録の意味も込めて。

Three.jsで複数のシーンを扱うことは、まあ普通にありますよね。

例えば、Forest,Sea,Skyみたいなシーン名を定義して、シーンに応じてオブジェクトをあらかじめaddしておき、ユーザーのアクション(ボタンクリックとか、特定の場所に行くとか)でシーンを切り替える。

この流れを実装しようとしてました。

当然、各シーンに共通するオブジェクトはありますよね。ライトとか地面のメッシュとか。

それらをシーンにaddする際に起きた問題がこちら。

  • 最後にaddしたシーンにしか追加されていない

各シーン固有のオブジェクトはそれぞれのシーンにしっかり追加されているのに、共通のオブジェクトだけ上手くいかなかったんです。

Forest,Sky,Seaの順番に追加したら、Seaにしか追加されていなかったんです。

お恥ずかしながら、原因・解決策ともにとても単純なものでした。

以下で、ソースコードと一緒に見ていきます。

スポンサーリンク

失敗例(最後のシーンにしか追加されない)

問題が起きたソースコードはこちらです。

簡単にするために、実際の処理は省略し、流れだけ分かるようにしてあります。

let scenes = new Object();
let sceneObjs = 
  { //ここで各シーン固有のオブジェクトを定義
    Forest:{
      
    },
    Sea:{

    },
    Sky:{

    }
  }
let ambientLight = new THREE.AmbientLight(0xffffff);  //全シーン共通
let directionalLight = new THREE.DirectionalLight(0xffffff, 0.3);//全シーン共通
//各シーンにオブジェクトを追加
Object.keys(sceneObjs).forEach(sceneName => addObjToScene(sceneName));


let addObjToScene = function(sceneName){
  scenes[sceneName] = new THREE.Scene();
  scenes[sceneName].add(ambientLight);
  scenes[sceneName].add(directionalLight);
  Object.keys(scenes[sceneName]).forEach( objName => {
    // 各シーン固有のオブジェクトの読み込み・シーン追加
  });
}

上記コードの中で、2つのライトが全シーンに共通するオブジェクトになります。

この処理を実行すると、Seaにしかライトが追加されないんです。

何が原因か分かりますでしょうか。

とても単純で、

  • ライトのインスタンスが1つしかないから

です。

要は、実体が1つしかないから、複数のシーンにあらかじめ用意しておくことは不可能ということです。

スポンサーリンク

解決策は2つ!

解決策は2つあります。

  • シーンを切り替えるたびに、共通するオブジェクトだけ逐一該当シーンに追加する
  • シーンの数だけインスタンスを生成し、各シーンに追加する

私は後者を採用しました。

切り替えるたび毎回追加するのはなんとなく嫌だったので。

ソースコードはこちらになります。

上記のコードから、ライトのインスタンス生成のタイミング(変数のスコープ)を変えただけです。

let scenes = new Object();
let sceneObjs = 
  { //ここで各シーン固有のオブジェクトを定義
    Forest:{
      
    },
    Sea:{

    },
    Sky:{

    }
  }
Object.keys(sceneObjs).forEach(sceneName => addObjToScene(sceneName));  //各シーンにオブジェクトを追加


let addObjToScene = function(sceneName){
  scenes[sceneName] = new THREE.Scene();
  let ambientLight = new THREE.AmbientLight(0xffffff);  //全シーン共通
  let directionalLight = new THREE.DirectionalLight(0xffffff, 0.3);//全シーン共通
  scenes[sceneName].add(ambientLight);
  scenes[sceneName].add(directionalLight);
  Object.keys(scenes[sceneName]).forEach( objName => {
    // 各シーン固有のオブジェクトの読み込み・シーン追加
  });
}

今回はシーンの数だけインスタンスを生成しているので、問題なく動作しました。

すごく雑なソースコードですが、流れは理解していただけたと思います。

まとめ

いかがでしたでしょうか。

まとめると、複数のシーンに共通するオブジェクトを扱うときは、

  • シーンを切り替えるたびに、共通するオブジェクトだけ逐一該当シーンに追加する
  • シーンの数だけインスタンスを生成し、各シーンに追加する

のどちらかを採用しましょう。当記事では後者を扱いました。

参考にしていただければ幸いです。

以上です。

Three.jsの学習におすすめの書籍はコレ!

・はじめてのThree.js

この本には、Three.jsを用いてコンテンツを開発する際に、必要な情報がほぼすべて盛り込まれています。

Three.jsをここまで詳しく解説している本はおそらくこれしかないです。

私は大学4年生からの研究活動でThree.jsを用いていますが、初めての学習でこの本を使用しました。

自分が必要な情報をかいつまんでその都度学習することもできますが、私個人としては、一通り目を通してThree.jsでできることをざっと把握することをおすすめします。

なぜなら、できることの全貌を知っている方が、アイデアの幅が広がり設計・コーディングもしやすくなるからです。

ぜひ一度お調べになってみてはいかがでしょうか。

コメント

タイトルとURLをコピーしました