【JavaScript】自前のクラスを作ってみました

JavaScript,Three.js

今さら…。

はじめに

Three.jsで地球とその周りをぐるぐるする雲を作りました。これです(動画)↓。

で、雲は全部で24個あるんですけどね。最初こんな感じで書いてました↓。

const clouds = [];
const phi = [];
const theta = [];
const scale = [];
const speed = [];
const degs = [];

//*****************************************
// 雲を作成する
//*****************************************
function createCloud() {
  
  const loader = new GLTFLoader();
  const cloudURL = "./gltf/cloud.glb";
  
  for (let i = 0; i < 24; i++) {
    
    loader.load(cloudURL, function(gltf) {

      const cloud = gltf.scene;
      
      // 緯度
      const p = Math.floor(Math.random() * (150 - 30) + 30);
      phi.push(p);

      // 経度
      const t = Math.floor(Math.random() * 359);
      theta.push(t);

      // 大きさを決めるための数値
      const d = (Math.floor(Math.random() * (10 - 5) + 5)) * 10;
      degs.push(d);

      // 大きさと速度
      let sc;
      let sp;
      if (i % 3 == 0) {
        sc = 0.5;
        sp = 0.2;
      } else if (i % 3 == 1) {
        sc = 0.4;
        sp = 0.3;
      } else if (i % 3 == 2) {
        sc = 0.3;
        sp = 0.4
      }
      scale.push(sc);
      speed.push(sp);

      scene.add(cloud);
      clouds.push(cloud);
    });
  }
}

//*****************************************
// 描画(毎フレーム行われる処理)
//*****************************************
function renderScene() {
  requestAnimationFrame(renderScene);
  
  for (let i = 0; i < 24; i++) {
    
  // 位置を変える
    theta[i] += speed[i];
    const pos = new THREE.Vector3();
    pos.setFromSphericalCoords(12, degToRad(phi[i]), degToRad(theta[i]));
    clouds[i].position.set(pos.x, pos.y, pos.z);

    // 向きを変える
    const dir = new THREE.Vector3();
    dir.copy(clouds[i].position).multiplyScalar(2);
    clouds[i].lookAt(dir);
    
    // 大きさを変える
    degs[i] += 0.1;
    clouds[i].scale.x = scale[i] + (Math.sin(degs[i]) * 0.05);
    clouds[i].scale.y = scale[i] + (Math.sin(degs[i]) * 0.05);
    clouds[i].scale.z = scale[i] + (Math.sin(degs[i]) * 0.05);
  }

  webGLRenderer.render(scene, camera);
}

どういう事かと言うとですね、雲が持っている情報として、

  • 緯度(phi)
  • 経度(theta)
  • 速度(speed)
  • 大きさ(scale)
  • 大きさを決めるための値(deg)

の5つがあるワケですね。で、これをそれぞれ格納するための配列を用意してたんです↓。

const phi = [];
const theta = [];
const speed = [];
const scale = [];
const degs = [];

で、描画する時に配列の値を読みだして雲に動きをつけていた、と。

これで問題なく動くんですけど、スマートじゃないなぁと思ってたんです。その時に閃いたんですよね。

「今こそクラスを作る時なんじゃないの?」って。

今までも、もともと用意されているクラスを使うことはあったんです。というか使わないとプログラム書けないし。

でも自分でクラスを作ったことはありませんでした。使い所(作り所)が分からなかったんですよね。でも今回のこの雲。実際に目に見えるモノという事もあって非常に分かりやすい。これはチャンス。

雲に関する情報を持つクラスを作る

//*****************************************
// 雲情報クラス
//*****************************************
class cloudInfo {

  //--------------
  // コンストラクタ
  //--------------
  constructor(phi, theta, speed, scale, deg) {
    this.cloud = null;     // 雲そのもの
    this.phi = phi;        // 緯度
    this.theta = theta;    // 経度
    this.speed = speed;    // 速度
    this.scale = scale;    // 大きさ
    this.deg = deg;        // 大きさを変化させるための値
  }

  //--------------
  // 動く用メソッド
  //--------------
  update() {
    // 位置
    this.theta += this.speed;
    const pos = new THREE.Vector3();
    pos.setFromSphericalCoords(12, degToRad(this.phi), degToRad(this.theta));
    this.cloud.position.set(pos.x, pos.y, pos.z);

    // 向き
    const dir = new THREE.Vector3();
    dir.copy(this.cloud.position).multiplyScalar(2);
    this.cloud.lookAt(dir);

    // 大きさ
    this.deg += 0.1;
    this.cloud.scale.x = this.scale + (Math.sin(this.deg) * 0.05);
    this.cloud.scale.y = this.scale + (Math.sin(this.deg) * 0.05);
    this.cloud.scale.z = this.scale + (Math.sin(this.deg) * 0.05);
  }
}

うーん。なかなかキレイ🙆‍♂️

コンストラクタで雲に関する情報を設定しています。そしてupdate()は雲を動かすためのメソッドです。

インスタンスを作成

//*****************************************
// 雲を作成する
//*****************************************
const cloudInfos = [];
function createCloud() {
  
  const loader = new GLTFLoader();
  const cloudURL = "./gltf/cloud.glb";
  let loadCnt = 0;
  
  for (let i = 0; i < CLOUDS_NUM; i++) {
    
    loader.load(cloudURL, function(gltf) {
      
      // 雲情報の設定
      const p = Math.floor(Math.random() * (150 - 30) + 30);
      const t = Math.floor(Math.random() * 359);
      let sp;
      let sc;
      if (i % 3 == 0) {
        sp = 0.2;
        sc = 0.5;
      } else if (i % 3 == 1) {
        sp = 0.3;
        sc = 0.4;
      } else if (i % 3 == 2) {
        sp = 0.4
        sc = 0.3;
      }
      const d = (Math.floor(Math.random() * (10 - 5) + 5)) * 10;

      // 雲情報オブジェクト作成
      const ci = new cloudInfo(p, t, sp, sc, d);
      ci.cloud = gltf.scene;

      // 影の設定
      for (let i = 0; i < ci.cloud.children.length; i++) {
        ci.cloud.children[i].castShadow = true;
      }

      scene.add(ci.cloud);
      cloudInfos.push(ci);
    });
  }
}

…なんか長いな🙅‍♂️

でもコンストラクタに渡す値をアレコレ作らないといけないのでしょうがないのかなぁ。まぁいいでしょう。

update()メソッドを呼んで雲を動かす

//*****************************************
// 描画
//*****************************************
function renderScene() {
  requestAnimationFrame(renderScene);
  
  //--------------
  // 雲を変化させる
  //--------------
  for (let i = 0; i < CLOUDS_NUM; i++) {
    cloudInfos[i].update();
  }

  webGLRenderer.render(scene, camera);
}

だいぶスッキリ🙆‍♂️

あとがき

全体のコードの長さはあんまり変わってないけど、どこで何してるのか分かりやすくなったと思います。

なによりも「やってみよう」と思った自分を褒めてあげたい。そんな事を考えながら硬いグミをむしゃむしゃ食べる蒸し暑い夜。ありがとうございました。

おしゃれ度

★☆☆☆☆

Posted by ナカタ