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