I may have found my strategy to introduce kids to object-oriented programming in 3D Game Programming for Kids. Sticking with just the prototypical nature of JavaScript looks promising. There are still a few concerns, however. The concern that I would like to address today is event handling.
I continue to work with "ramps" in a game that I am prototyping:
So far, I allow the mouse to click and drag these ramp, though the event handlers are a bit bulky. The onClick handler, for instance, looks like:
  var ramp = {
    // ...
    onClick: function(event) {
      this.mouse_x = event.clientX;
      this.mouse_y = event.clientY;
      var position = new THREE.Vector3(
        event.clientX - width/2,
        height/2 - event.clientY,
        500
      );
      var vector = new THREE.Vector3(0, 0, -1);
      var ray = new THREE.Ray(position, vector);
      var intersects = ray.intersectObject(this.mesh);
      this.isActive = (intersects.length > 0);
    },
    // ...
  };
Nearly all of that code is responsible for determining if a click has occurred. The only thing that needs to occur on a click is to mark it as active. So ideally, my onClick handler would look like:  var ramp = {
    // ...
    onClick: function(event) {
      this.isActive =  true;
    },
    // ...
  };This property can then be used in the onMouseMove handler to decide whether or not to move the current ramp. Really, I could even move isActive into a library that supports draggging, but I will leave that for another day.I define a function that adds event listeners to the prototype of the supplied object:
  var mouse_objects = [];
  function addMouseHandlers(object) {
    mouse_objects.push(object);
    if (mouse_objects.length > 1) return;
    
    function rendererXY(event) {
      return {
        x: event.clientX - window.innerWidth/2,
        y: window.innerHeight/2 - event.clientY
      };
    }
    
    document.addEventListener("mousedown", function(e){  
      var click_pos = rendererXY(e),
          position = new THREE.Vector3(click_pos.x, click_pos.y, 500),
          vector = new THREE.Vector3(0, 0, -1),
          ray = new THREE.Ray(position, vector);
      mouse_objects.forEach(function(o){
        if (!o.onClick) return;
        if (!o.mesh) return;
        if (ray.intersectObject(this.mesh).length === 0) return;
        o.onClick({x:x, y:y});
      });
    });
    document.addEventListener("mousemove", function(e){
      // ...
    });
    document.addEventListener("mouseup", function(e){
     // ...
    });
  }
That more or less works, but it requires the supplied object to conform to some fairly strict properties.  The object must have a mesh (to see if the click intersected with the Three.js object) and it must define an old-style onClick handler.I don't know that I can make this much cleaner — at least without switching to a more classical inheritance. I call it a night here so that I can reconsider this approach.
Day #650

How about a library method that takes an object and an event and determines intersection. The object would be required to support a "boundingBox" method. Then any object can say:
ReplyDeleteonClick: function(event) {
this.isActive = intersects(this, event);
}
Although, a better approach may be to have a ClickWatcher object that has a reference to the objects on the screen. It handles clicks and then looks for the object that was clicked and calls its onClick. That's more like how the dom works.
I tried toying with that idea, but I find I'm fighting a losing battle in my attempts to keep `this` out of the book: http://japhr.blogspot.com/2013/02/think-of-children-avoid-this.html. I may need to abandon the idea of using objects for these ramps (or anything that uses events).
Delete