A-Frame Hello World Tutorial

Hello World!

1. Begin by setting up the skeleton of the HTML file, namely including the A-Frame script and including the a-scene open and close tags. The a-scene tag is extremely powerful, and is one of the reasons WebVR is so user-friendly to use and develop for—it takes care of lighting, camera angles, renderer, and any other boilerplate stuff we might need.

2. In A-Frame, each element is rendered with an HTML tag, with extensible parameters. For instance, we  can render a sphere as <a-sphere position="0 1.25 -5" radius="1.25" color="#EF2D5E"></a-sphere>.

3. Let's render a sphere, cube, cylinder, ground, and skybox. The easiest way to get started with A-Frame development is through Glitch, an online HTML editor:

Creating a Minecraft-esque engine in A-Frame

1. Begin by setting up the skeleton of the HTML file, namely including the A-Frame script and including the a-scene open and close tags. The a-scene tag is extremely powerful, and is one of the reasons WebVR is so user-friendly to use and develop for—it takes care of lighting, camera angles, renderer, and any other boilerplate stuff we might need.

2. Create a ground upon which to place objects. In this case, to play nicer with controllers, we will use a-cylinder objects, and establish dimensions within the html tag, as per A-Frame convention. Note that A-Frame dimensions are measured in meters to correspond to WebVR's real-world returned dimensions. 

Lastly, we'll plaster a simple texture onto the ground, hosted at https://cdn.aframe.io/a-painter/images/floor.jpg

3. Establish a preload schema. Since network requests can negatively impact render performance, we can preload the texture such that the scene doesn't start rendering until its assets have been fetched. With the a-assets tag, we can place any images/sounds/other into it, and refer to them with a selector, as shown below. 

4. Add a background. With the a-sky tag, we can easily render a skybox—essentially a sphere with an internal texture. Again, we'll source the texture from https://cdn.aframe.io/a-painter/images/sky.jpg, and ensure that the dimensions match our established ground.

5. Add actual blocks (or voxels)! A block entity can be treated as an a-box with an a-entity attached, like so:

<a-box color="red" depth="0.5" height="0.5" shader="flat" width="0.5"></a-box>

<a-entity geometry="primitive: box; depth: 0.5; height: 0.5; width 0.5" material="color: red; shader: standard"></a-entity>

As you can see, the dimensions are extremely extensible and allow for a great range of flexibility when it comes to rendering elements. 

For this example, we'll set each block entity to a random color when we place it. A-Frame supports custom components with AFRAME.registerComponent. This means that after writing the Javascript, we can simply add the random-color attribute to an entity's tag and it will call the getRandomColor() function and attach a color to it! See the script below to see how to do so. (Refresh the page if you want to prove that it's random!)

6. Add in a snap grid and mixins. With the snap component, we can make sure an entity "rounds" its position to a nearest square of the grid, like so:

<a-entity geometry="primitive: box; height: 0.5; width: 0.5; depth: 0.5" material="shader: standard" random-color snap="offset: 0.25 0.25 0.25; snap: 0.5 0.5 0.5"></a-entity> 

Secondly, we can use a mixin to create a reusable entity or bundle of components. For instance, we can bundle components under a-assets once and refer to them later on in an a-entity block:

<a-assets>

    <a-mixin id="red" material="color: red"></a-mixin>

    <a-mixin id="blue" material="color: blue"></a-mixin>

    <a-mixin id="cube" geometry="primitive: box"></a-mixin>

  </a-assets>


  <a-entity mixin="red cube"></a-entity>

  <a-entity mixin="blue cube"></a-entity>

7. And here we have our basic framework! What's left is to add support for VR controllers such as the Vive. Fortunately, this is quite easy: all we have to do is include self-explanatory components:

<!-- Vive. -->

<a-entity vive-controls="hand: left"></a-entity>

<a-entity vive-controls="hand: right"></a-entity>


<!-- Or Rift. -->

<a-entity oculus-touch-controls="hand: left"></a-entity>

<a-entity oculus-touch-controls="hand: right"></a-entity>

For now though, we can use the more abstract hand-controls, which are general controller entities. We can assign the left hand controller to a teleport function, which we can include from a script:

<script src="https://aframe.io/releases/0.5.0/aframe.min.js"></script>

<script src="https://unpkg.com/aframe-teleport-controls@0.2.x/dist/aframe-teleport-controls.min.js"></script>


<!-- ... -->


<a-entity id="teleHand" hand-controls="left" teleport-controls></a-entity>

<a-entity id="blockHand" hand-controls="right"></a-entity>

Then, for the right hand, we can assign a block spawner:

<script src="https://aframe.io/releases/0.5.0/aframe.min.js"></script>

<script src="https://unpkg.com/aframe-teleport-controls@0.2.x/dist/aframe-teleport-controls.min.js"></script> 

<script src="https://unpkg.com/aframe-controller-cursor-component@0.2.x/dist/aframe-controller-cursor-component.min.js"></script>


<!-- ... -->


<a-entity id="teleHand" hand-controls="left" teleport-controls="type: parabolic; collisionEntities: [mixin='voxel'], #ground"></a-entity>

<a-entity id="blockHand" hand-controls="right" controller-cursor></a-entity>

This provides the "click" functionality for where our controllers are pointing. To tie in the block spawning with our code, we need a bit of JS:

document.querySelector('#blockHand').addEventListener(`click`, function (evt) {

    // Create a blank entity.

    var newVoxelEl = document.createElement('a-entity');


    // Use the mixin to make it a voxel.

    newVoxelEl.setAttribute('mixin', 'voxel');


    // Set the position using intersection point. The `snap` component above which

    // is part of the mixin will snap it to the closest half meter. 

    newVoxelEl.setAttribute('position', evt.detail.intersection.point);


    // Add to the scene with `appendChild`. this.appendChild(newVoxelEl);

});

In our HTML, we change our right hand controls to  <a-entity id="blockHand" hand-controls="right" controller-cursor intersection-spawn="event: click; mixin: voxel"></a-entity>

Lastly, for those without controllers (like a lot of us), we can add cursor and camera panning functionality to regular desktop and mobile users:

<a-entity id="blockHand" hand-controls="right" controller-cursor intersection-spawn="event: click; mixin: voxel"></a-entity>


<a-camera>

    <a-cursor intersection-spawn="event: click; mixin: voxel"></a-cursor>

</a-camera>

Putting it all together, we get this!