Week 3 - Firing my laser

Pong: Week 3 - Firing my laser

Warning: This weeks entry is not performance optomised. I’m sorry! Proceed with caution.

Wow! Una did an amazing job. It was not what I was expecting, and even though I was expecting something I wasn’t expecting, Una didn’t fail to surprise me. I think we all know there is really only one response to Una’s giant, glowing, alien, crystal, …thing. We need to fire a massive laser out the end of it and burn the sky! Rather than starting with it on, I wanted to include a mechanism so that people can fire the laser. This was a great opportunity to explore how interaction works in WebVR.

Note: I later found out that Una might have been thinking along same lines. The alt text of the image in her blog post was “Gun-blaster-thing in Blender”.

Adding the cursor

A cursor is a little different in WebVR because it is fixed. It remains in the centre of the screen and you move the camera to position objects under the cursor, rather moving the cursor itself.

A-Frame provides a handy cursor component with an event system. You add a cursor like this:

<a-entity
    cursor="fuse: true; fuseTimeout: 3000"
    raycaster="far: 20; interval: 1000; objects: .clickable"
    geometry="primitive: ring; radius-inner: 0.1; radius-outer: 0.2;"
    material="color: white; shader: flat">
    <a-animation begin="click" dur="3000" easing="ease-in" attribute="scale" fill="backwards" from="0.1 0.1 0.1" to="0.3 0.3 0.3"></a-animation>
    <a-animation begin="cursor-fusing" dur="3000" easing="ease-in" attribute="scale" fill="forwards" from="0.3 0.3 0.3" to="0.1 0.1 0.1"></a-animation>
</a-entity>

I’m using a ring object for the pointer and animating it when the user approaches intractable objects. Using the raycaster attribute you change the default settings the cursor uses when detecting objects.

Note: Unfortunately I have a bug that I can’t seem to solve, where the maximum distance at which objects should be interactive is ignored. I’m going to come back to this on my next turn, but if you know why this happening please let me know.

You can listen for mouse click and hover events in objects in A-Frame, but there is also a built in ‘fuse’ mode. If the cursor is set to use a fuse, it will trigger a click event if a user looks at a specific object for a set amount of time.

This is especially because simple WebVR capable devices may not have a mouse or controller input. This makes interaction in WebVR a difficult problem, and doubly so because we can’t make any easy assumptions about users behavior. There are very few established conventions and users don’t have set expectations about how things should work. It will be interesting to see how this develops.

To listen for events you need to make a component (almost everything is a component in A-Frame). I created this one where I toggle the visibility of the laser, the lights and particle components when users interacts with the alien object. It looks like this:

AFRAME.registerComponent('cursor-listener', {
  init: function () {
    this.el.addEventListener('click', function () {
      var elms = document.querySelectorAll('.laser-on')
      for (var i = 0, len = elms.length; i < len; i++) {
          elms[i].setAttribute("visible", "true");
      }
    });
  }
});

It’s a bit crud but does the job.

Adding the laser

I agree with Una that Blender has somewhat of a steep learning curve, so I am glad that the model for the laser beam is simply cylinder, one of A-Frame’s built-in primitives.

Orienting and positioning the laser was not easy! I was able to find the correct rotation by trial and error, but the anchor point for the cylinder is in the middle. This means that currently the center of the cylinder is where I want the start to be.

Difficulty positioning the cylinder

If you’re having trouble following what I mean, hold a pen or a pencil between two fingers. Look where its tip is pointing. We want to move the center of the pencil to where its tip currently is. If we do this, keeping the current angle, the end of the pencil will now be where your fingers are.

The maths for this is complicated if not difficult and involves thinking about a whole lot of trigonometry in 3 dimensions.

Graph showing a vector in 3 dimensions

I started with this approach but luckily I soon realised there is an easier way. I read this article on relative positioning in A-Frame and learnt that position and rotations are additive inside entities so I can use an <a-entity> component to apply the rotation first. This will change the orientation of the z-axis for components nested in this entity. So if I rotate a container element, all I needed to do is move the cylinder half its height along the z-axis (up) which is now already pointing in the correct direction. I then manually tweaked the values to place it exactly where I wanted.

So much easier than this:

Calculations literally on the back of an envelope

I still think A-Frame could benefit from a scene editor of some kind, and yes I know there are a few examples, but there does not seem to be a killer app for this yet.

Lights, animation & particles

I needed to add some effects to my laser so I Googled how to create a vertex displacement shader and after much trial and error I ported this example. It looks amazing but it’s more than I needed. I’ll release it separately if I can find time to polish it off. Once I understood how this works I followed this tutorial by Paul Lewis to make a really simple animation for the laser.

For the light I made the laser emit the same crimson light with the light attribute that Una used:

<a-entity visible="false" class="laser-on" height="0" width="0" position="-1 7.5 1.5" rotation="0 0 53" scale="" visible="">
  <a-laser position="0 500 0" length="1000" opacity="0.8" scale="0.1" radius="1"
  light="type: point; intensity: .6; color: crimson;"></a-laser>
</a-entity>

For the particle system, again I did not build my own. I used this one by Chris Car that itself is a port of squarefeet’s Shader Particle Engine. Unfortunately I had some issues with it. These could be a problem for anyone looking to use this component with custom properties. As I said in the gitHub issue, I should fix the problem and submit a PR, but I am already behind on getting this out and wanted to show you something sooner rather than later. I fixed it for just my specific situation …for now.

A quick note on performance

Just a quick note on performance. I’m really sorry if I burnt your machine! Between the water reflections and the particles we’ve got some issues. I think it looks beautiful but we may need to find some performance gains, dial it back a bit, or make some tough design decisions. A good chance to explore another topic!

@Una, tag & sorry about the lag!