javascriptecmascript-6three.jsassignevent-dispatching

can the three.js EventDispatcher be used to communicate between classes?


Core question

An event emitted on one class that supports EventDispatcher doesn't get picked up by another class that supports EventDispatcher.

How do I get the communication working app-wide so that I can pass messages around?

At the moment it seems that it only supports passing a message to its own class.

Background

I'm using this boilerplate which gives me three.js and es6:

https://github.com/paulmg/ThreeJS-Webpack-ES6-Boilerplate

I've updated the packages manually to use three.js r97.

I'm looking at the EventDispatcher class and I'm thinking this is a clean way for me to pass messages around the system while keeping it decoupled. For example, I have some HTML UI that watches a checkbox.

Trimmed down to the core example, I have interaction.js:

import {EventDispatcher} from 'three';

// Manages all input interactions
export default class Interaction {
  constructor() {
    // Add event dispatcher support
    Object.assign( this, EventDispatcher.prototype );

    // setup the trigger for the event
    let outer = this;
    $("#" + Config.uiElements.boxOpenStateId).change(function() {
      console.log("event emitting");
      outer.dispatchEvent( { type: 'boxOpenStateToggled', message: { isChecked: "example" } } );
      console.log("event emitted");
    });

    // setup a event listener
    this.addEventListener( 'boxOpenStateToggled', function ( event ) {
      console.log("event caught in Interaction.js", event);
    });
  }
}

And I have boxmanager.js (trimmed down to the core bits):

import {EventDispatcher} from 'three';

export default class BoxManager {
    constructor() {
        // Add event dispatcher support
        Object.assign( this, EventDispatcher.prototype );
    }

    setupBoxes(manager) {
        console.log("adding event listener on BoxManager.js");
        this.addEventListener( 'boxOpenStateToggled', function ( event ) {
            console.log("event caught by BoxManager.js", event);        
        } );
    }
}

When I trigger the boxOpenStateToggled event on Interaction the BoxManager event listener doesn't capture it.

I get a console log like this:

adding event listener on BoxManager.js
adding event listener on Interaction.js

(I trigger the event)

event emitting
event caught in Interaction.js {type: "boxOpenStateToggled", message: {…}, target: Interaction}
event emitted

What I was expecting here was to see "event caught in BoxManager" as well - is it a system-wide event dispatcher? Or can it only dispatch events internally on a per-class basis?

Showing my working

I wasn't sure if it was the way I was using it with so I have tried:

I can't find any real discussion or examples of using EventDispatcher or see where it is even used in the library.

Have I misunderstood its purpose? Am I using it wrong?


Solution

  • You could consider doing something like this:

    const myEventBus = new THREE.EventDispatcher()
    
    const foo = new Foo(myEventBus)
    const bar = new Bar(myEventBus)
    

    If you want to avoid passing the instance to objects like this, or making factories, you could do something like this:

    //Events.js
    const Events = new THREE.EventDispatcher()
    export default Events
    

    And then:

    //Foo.js
    import Events from 'common/Events'
    import {BOX_OPEN} from 'Bar'
    export default class Foo {
      constructor(){
        Events.addEventListener( BOX_OPEN, this.onBoxOpen);
      }
    }
    

    Dispatch

    //Bar.js
    
    export const BOX_OPEN = 'box_open'
    
    export default class Bar {
      constructor(){
        $("#" + Config.uiElements.boxOpenStateId).change(this.onChange);
      }
      onChange = () => {
         console.log("event emitting");
         Events.dispatchEvent({ type: BOX_OPEN, message: { isChecked: "example" } });
         console.log("event emitted");
      }
    }