One of my goals when developing the Pearl Jam game was to make the 3d experience as tactile as possible. I wanted it to feel like you were really touching, grasping, and moving the cubes—in these respects, the system cursor is a bit on the cold side. That’s where the HandCursor3D comes in.
Part of the inspiration for this idea is from one of my favorite commercials of all time—HP Digital Crime Fighting by Goodby, Silverstein & Partners. The cursor is executed so stunningly that it becomes another actor (if not the lead) in the spot. The element of surprise is guaranteed every time—that thing we use to point and click is suddenly alive and breathing.
Last fall, digitalpictures created a handcursor in 3d space for saveyoursensible.com, one of the first implementations of its kind. However, the cursor was only 3d when the mouse was pressed and touching one of two objects. Moving around, between, and over the objects was still a 2d experience.
The area between objects is exactly the conundrum. The traditional mouse gives us an absolute x and y position, and it’s up to us to find the z/depth. When there’s a 3d object under the cursor, we have a point of reference to find the z position. When there’s nothing there, the result is infinite—literally. Take the analogy of shining a flashlight, you can only see the beam when there is an object in its path, otherwise it disappears into thin air.
My solution to the empty space is, well, to put something in it! I created an invisible sphere that surrounds the entire site—let’s call it a “forcefield” (I’ve never watched an episode of Star Trek in my life, by the way). Now when the cursor moves between the visible objects, it is technically “hitting” something, giving us that elusive z position.
Okay, now back to when the cursor is actually over the objects. To get that “touch” feeling, it needs to react to the shape of the object. For basic primitives like cubes and spheres that have square proportions, the DisplayObject3D.lookAt() method does the trick. Since the lookAt method always orients the mouse towards the origin, it gives the illusion of the cursor hugging around the corners and sides of the cubes on the site.
For objects with irregular and elongated shapes, the lookAt() illusion does not hold up as well—as you move farther from the object’s origin, the cursor plane turns perpendicular to the object. In this situation, there are a couple of options. The first, less elegant solution would be to surround the object itself in one or more invisible bounding spheres that approximate its general size and shape, making the angles less severe.
The second, and more accurate, solution would be to orient the cursor to the normal of the hit Triangle3D. Using the transform object from the core Mouse3D class is a good start, but I found the results to look a little strange and unnatural.
Since the forcefield sphere is surrounding the objects, the 2d to 3d hit test will always return a position on the sphere and never on any of the objects inside of it. So in order to know when the cursor is over one of these objects, we create a ViewportLayer for each object and listen to its mouse events. When a layer is rolled over, we set the interactive property of the forcefields sphere’s material to false. When no layer is rolled, we set it back to true. I added some subtle easing to the cursor position, which helps the transition between hitting the forcefield and the other objects.
Here is the basic usage:
-
var cursor:HandCursor3D = new HandCursor3D( viewport );
-
scene.addChild( cursor );
-
-
// register object layer
-
cursor.registerLayer( myObject.container );
-
-
// register forcefield
-
cursor.registerForcefieldObject( sphere );
Now that the basic logic is in place, there are a few more things that you can do to emphasize the effect. In the site and example here, the cursor gets the same fog treatment as the rest of the objects, a nice visual indicator of its position in z-space. Also, by toggling the ViewportLayer.forceDepth property, the cursor is always above the objects when hovering, but is sorted normally when it is locked in position and dragging.
Downlod the demo source here. The class is far from perfect, but is a good start for developers looking to expand on the idea.
Note: There is some funkiness when your mouse leaves the stage and comes back. It eventually resolves itself, but I'm looking into it.
