Here’s how to build one in webVR using A-Frame

The bow and arrow is the Hello, World of tracked-controller virtual reality.

As an object set, it requires the use of both hands at once, coordination between multiple entities, a physics system, positional sound, haptics, and a bit of 3D math to tie it all together.

In this article, I’ll talk about building a two-handed bow and arrow using A-Frame, a browser-based virtual reality framework, but I recommend repeating the exercise in Unity or Unreal Engine.

Building a bow and arrow is a way to quickly test the capabilities of motion-controllers inside of existing tools, and to compare new frameworks and engines as they arise.

All of the code is available on Github.

webVR + A-Frame

We’re not quite there yet, but at some point soon users will be able to enter virtual reality by simply visiting a web page. No application to download and install (potentially losing users in the process).

Additionally, the best parts of the web will be accessible to your applications: webRTC, websockets, hyperlinks, live-streams, etc.

A-Frame is a framework that implements an Entity-Component-System pattern, which allows us to compose reusable modules to achieve something that is more than the sum of its parts. An entity is a container for components. The components affect the appearance and behavior of the entity. Systems are ways to manage collections of components. This kind of pattern should be familiar to Unity Developers.

If you need to release to the paying public today, I’d recommend sticking with a game engine. If your time horizon is a bit longer, webVR is an exciting place to try new ideas.

Read more about webVR or dive into an Introduction to A-Frame.

The Build

The key to working in VR right now is persistence. Whether you’re in A-Frame, Unity, or Unreal, this is for the most part uncharted territory. It’s going to take experimentation and lots of testing.

Don’t give up! — seek help. Ask questions on StackOverflow or GitHub issues. If you’ve really hit a wall, go to bed. You’d be surprised how many times you’ll wake up to some helpful advice and a new perspective.

Patience can be a virtue — you never know what great new stuff is coming this week.

Hand Interaction Design

When interacting with objects in virtual reality using motion controllers, you must first decide if and when to show the user’s hands and how those hands themselves will interact with the objects in the scene.

For this demo, I made the decision to show your hands all the time. There are games that decide not to show your hands at all once you’ve picked something up — some evidence shows that people transfer their hand presence onto the held object.

I do not attempt to transition the hand into real-world grasp around the edge of the bow. This ‘freeform’ grab trades a realistic bow grip for constant hand presence. Here’s a good started article about the different kinds of hand interactions.

Stylistically, the hands that come with the A-Frame implementation lead us toward a more toon or sketch-like aesthetic for the piece, but these could be swapped for something more realistic.

Assets

For this project, I used .obj models. I recommend using a site like Clara.io to find models. You may have to realign your assets along a different axis depending on how they were exported from the modeling program where they were made.

Regarding sounds, there are a number of royalty-free sample sites out there, or you can record your own. Sounds can be played at a given position, adding richness and depth to your scenes.

All of the assets I used are available on GitHub.

Component Design

A-Frame is just HTML. Markup is cool!

<a-entity bow-and-arrow position="-0.5 1 -0.5"></a-entity>

So what is that “bow-and-arrow” attribute? That’s an A-Frame component — it’s the JavaScript that turns the HTML into something we can use. The position attribute is also an A-frame component, included with the main A-Frame library.

Loading components is as simple as including the scripts in the head of your document.

<script src="components/bow-and-arrow.js"></script

Here’s a skeleton of what our bow and arrow component might look like :

AFRAME.registerComponent(‘bow-and-arrow’, {

schema: {

lineColor: {

type: 'color',

default: '#E20049'

},

},

init: function(){... // called on start},

tick:function(){... // once a render frame},

shootArrow:function(){...// add velocity to a physics object}

});

Schema let you pass in values when you add your component to an entity, and access those values as data from your JavaScript. To change the color of bow’s meshline, you simply change the HTML.

<a-entity bow-and-arrow="lineColor:#FFF" position="-0.5 1 -0.5"></a-entity>

To get the bow working, we need to add these methods to the component:

Listen to event for picking up the bow with primary hand

Play nocking sound at bow position

Measure distance between hands (shot force) while aiming

Move the arrow back in the nock proportional to the shot force

Draw a line to the back of the arrow

Detect trigger release from back hand

Apply impulse force to arrow toward front of bow to shoot arrow

Play shooting sound at bow position

Vibrate the controller when we release an arrow

Rotate the arrow toward its velocity to get a nice arc

Collision detection to play hit sound at collision position and cleanup

As much as possible, you should check first to see if there is a component that does that you need in the A-frame Examples, A-Frame package registry, or linked on Awesome A-Frame. However, you may end up modifying or creating your own components for custom behaviors.

I was able to find existing components that helped with the process: grab, aabb-collider, aframe-meshline, and the aframe-physics-system.

I ended up writing my own components to rotate the arrow toward its velocity and distribute ‘trees ’around you using a natural-looking Poisson Disc-based forest distribution. Some of the box-trees are dynamic, and will move when you shoot them. Others are static, and your arrows will bounce right off.

Haptics are sorely missed, but coming soon. I stubbed out some methods for when that feature arrives. The feeling of pulling back the bowstring in Valve’s Longbow scene from “The Lab” is an early standout in haptic interactions — the pulses, audio feedback, and the real-world sound of the controller motors themselves all combine to give a deep sense of immersion.

I’m still working on improving the method of rotating the bow while aiming the arrow. Consensus is that once the arrow is nocked, the bow should be rotated based on the line between your hands, as in “The Lab”.

Technical Notes

webVR is being standardized in front of our eyes. There are versions of both Chromium and Firefox Nightly that support webVR. They are at different stages of their development, and bug fixes hit at different times. Therefore, you might have to switch between one and the other depending on what is working or not at the moment. Personally, I found that Firefox Nightly worked best for me during development.

If you find a component that you like, download a copy locally. Things change quickly and you never know when some upstream change might impact your project. The same goes for A-Frame itself. For now you should update libraries manually to avoid breaking changes.

A note on performance — creating and inserting A-Frame entities into the DOM on the fly can be ‘expensive’ in the sense that it may cause garbage collection. This can result in hiccups in the scene. To avoid these skips and delays, I needed to create a pool of arrow objects to draw from. The physics bodies for the arrows, which are not naturally pooled, need to have their velocities reset each time they are recycled. The result is an instantaneous impulse to the arrow at trigger release.

I was pleasantly surprised with how well the physics engine performs. Be sure to check out Cannon.js. And of course, any knowledge of the Three.js 3D library will come in handy for custom components and performance tweaks.

Conclusion

It can take a little while to get the hang of A-Frame, but once you do, it’s pretty rewarding. At the beginning I struggled to know when should I write an A-Frame component?— the answer is almost always. To make the most of the pattern, compose using small pieces.

A-Frame is a very approachable way to do VR, and the ecosystem is taking off. It’s also just plain cool to be creating virtual reality using a browser, HTML and JavaScript.

Onward!

You can get in touch with me on LinkedIn