Help us understand the problem. What is going on with this article?

Scene切り替え

More than 3 years have passed since last update.

SceneをまとめたものをChapterと定義する

イベント処理にはPub/Subを採用
pubsub-jsの使い方
http://qiita.com/mo4_9/items/2ac77bdc7399600bc2d4

処理の流れ

  1. ChapterManager.js - setChapter | 現在のChapterを決定
  2. ChapterManager.js - handleClick | 現在のChapterのhandleClick()を実行
  3. Chapter1.js - PubSub.publish | クリックされたオブジェクトに応じてイベントを発行
  4. ChapterManager.js - PubSub.subscribe | 購読中のイベントに通知

Chapter*.js

renderを含まない各Chapterを用意
Chapter1.js , Chapter2.js , Chapter3.js

./lib/Chapter1.js
import PubSub from 'pubsub-js';

export default class Chapter1 {
  constructor(opts = {}) {

    this.TOPIC_NAMESPACE = 'Chapter1';

    this.width = opts.width || window.innerWidth;
    this.height = opts.height || window.innerHeight;

    this.init();
  }

  init() {
    { // scene
      this.scene = new THREE.Scene();
    }
    { // camera
      this.camera = new THREE.PerspectiveCamera( 45, this.width / this.height, 1, 10000 ); // fov(視野角),aspect,near,far
      this.camera.position.set(100, 100, 100);
      this.camera.up.set(0, 1, 0);
      this.camera.lookAt({ x:0, y:0, z:0 });
    }
    { // controls
      this.controls = new THREE.OrbitControls(this.camera);
    }
    { // lights
      const ambientLight = new THREE.AmbientLight(0xffffff);
      this.scene.add(ambientLight);
    }
    { // axis
      const axis = new THREE.AxisHelper(1000);
      axis.position.set(0,0,0);
      this.scene.add(axis);
    }
    { // grid
      const grid = new THREE.GridHelper(50, 10); // size, step
      this.scene.add(grid);
    }
    { // cube
      const cgeometry = new THREE.CubeGeometry(20, 20, 20);
      const cmaterial = new THREE.MeshLambertMaterial({ color: 0xffcc66 });
      this.cube = new THREE.Mesh(cgeometry, cmaterial);
      this.cube.name = `cube-${this.TOPIC_NAMESPACE}`;
      this.scene.add(this.cube);
    }
    { // sphere
      const sgeometry = new THREE.SphereGeometry(10, 30, 30);
      const smaterial = new THREE.MeshLambertMaterial({ color: 0x66ccff });
      this.sphere = new THREE.Mesh(sgeometry, smaterial);
      this.sphere.name = `sphere-${this.TOPIC_NAMESPACE}`;
      this.sphere.position.set(10,50,-10);
      this.scene.add(this.sphere);
    }
  }

  handleClick(objs) {
    // クリックされたオブジェクトに応じて、発行するイベントを分岐する
    const isCubeClick = objs.some(obj => obj.object.name == 'cube-Chapter1'); // nameで判定
    const isSphereClick = objs.some(obj => obj.object == this.sphere); // objectで判定
    console.log(objs, isCubeClick, isSphereClick);
    if (isCubeClick) {
      PubSub.publish(`nextObjClick.${this.TOPIC_NAMESPACE}`);
    }
    if (isSphereClick) {
      PubSub.publish(`prevObjClick.${this.TOPIC_NAMESPACE}`);
    }
  }
}

ChapterManager.js

./lib/ChapterManager.js
import PubSub from 'pubsub-js';
import './OrbitControls';
import Chapter1 from './Chapter1';
import Chapter2 from './Chapter2';
import Chapter3 from './Chapter3';

export default class ChapterManager{
  constructor(opts = {}) {
    this.RENDER_INTERVAL = 30;
    this.TICK_INTERVAL = 1500;

    this.width = window.innerWidth;
    this.height = window.innerHeight;
    this.container = opts.container || document.createElement('div');

    this.chapterList = [
      new Chapter1({
        width: this.width,
        height: this.height,
      }),
      new Chapter2({
        width: this.width,
        height: this.height,
      }),
      new Chapter3({
        width: this.width,
        height: this.height,
      }),
    ];

    this.init();
    this.setEvent();
    this.setChapter();
  }

  init() {
    { // renderer
      this.renderer = new THREE.WebGLRenderer({ antialias: true });
      this.renderer.setClearColor( 0x222222 ); // 背景色
      this.renderer.setPixelRatio(window.devicePixelRatio || 1);
      this.renderer.setSize( this.width, this.height );
      this.container.appendChild( this.renderer.domElement );
    }
  }

  setEvent() {
    const rect = this.renderer.domElement.getBoundingClientRect();

    this.renderer.domElement.addEventListener('mousedown', (evt) => {
      const mouseX = evt.clientX - rect.left;
      const mouseY = evt.clientY - rect.top;
      const position = {
        x: (mouseX / rect.width) * 2 - 1, // -1 ~ +1 の座標
        y: -(mouseY / rect.height) * 2 + 1, // -1 ~ +1 の座標
      };
      const objs = this.getIntersectObjects(position.x, position.y);

      if (objs.length > 0) {
        if (!this.currentChapter.handleClick) {
          return;
        }
        this.currentChapter.handleClick(objs);
      }
    }, false);

    PubSub.subscribe('nextObjClick.Chapter1', () => {
      this.setChapter(1);
    });
    PubSub.subscribe('prevObjClick.Chapter1', () => {
      this.setChapter(2);
    });

    PubSub.subscribe('nextObjClick.Chapter2', () => {
      this.setChapter(2);
    });
    PubSub.subscribe('prevObjClick.Chapter2', () => {
      this.setChapter(0);
    });

    PubSub.subscribe('nextObjClick.Chapter3', () => {
      this.setChapter(0);
    });
    PubSub.subscribe('prevObjClick.Chapter3', () => {
      this.setChapter(1);
    });
  }

  // sceneとcameraを現在のchapterに更新する
  setChapter(id = 0) {
    this.currentChapter = this.chapterList[id];
    console.log(this.currentChapter);
    this.scene = this.currentChapter.scene;
    console.log(this.scene);
    this.camera = this.currentChapter.camera;
  }

  getIntersectObjects(x, y) {
    const vector = new THREE.Vector3(x, y, 1);
    vector.unproject(this.camera);

    const ray = new THREE.Raycaster(this.camera.position, vector.sub(this.camera.position).normalize());
    const objs = ray.intersectObjects(this.scene.children);

    return objs;
  }

  start() {
    const startTime = Date.now();
    let previousTime = startTime;
    let previousRenderTime = previousTime;
    let previousTickTime = previousTime;
    this.loopId;

    const loop = (timestamp) => {
      const nowTime = Date.now();
      const elapsedTime = nowTime - startTime;
      const deltaTime = nowTime - previousTime;
      const deltaRenderTime = nowTime - previousRenderTime;
      const deltaTickTime = nowTime - previousTickTime;

      if (deltaRenderTime > this.RENDER_INTERVAL) {
        previousRenderTime = nowTime;
        this.render();
      }
      if (deltaTickTime > this.TICK_INTERVAL) {
        previousTickTime = nowTime;
        this.tick();
      }

      previousTime = nowTime;
      this.loopId = requestAnimationFrame(loop);
    };

    loop();
  }

  stop() {
    cancelAnimationFrame(this.loopId);
  }

  tick() {
  }

  render() {
    this.renderer.render( this.scene, this.camera );
  }
}
script.js
import ChapterManager from './lib/ChapterManager';

(() => {
  const chapterManager = new ChapterManager({
    container: document.getElementById('canvas-container'),
  });

  chapterManager.start();
})();
mczkzk
読ませる記事よりも公開メモやスニペットの投稿が多め。
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away