LoginSignup
1
1

More than 5 years have passed since last update.

Scene切り替え

Last updated at Posted at 2016-08-22

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();
})();
1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1