Build an AFrame.IO Scene on Oculus Quest with Teleportation

FireFox Mixed Reality

Hey web developers! Looking for a fun way to build VR experiences on the Oculus Quest? This tutorial will provide a brief guide to drafting an AFrame.IO VR experience that includes GLTF model loading and teleportation controls. As web developers, we have the unique opportunity to link data, models, and services to WebXR experiences. We really love seeing AFrame.IO work well on the Oculus platform. These are exciting times and trends!

AFrame.IO Script for Oculus WebXR

Fork the script at https://aframeexamples.glitch.me. In 2023, I feel that @ProfStemkoski has created one of the best collections of AFrame.IO templates. I like how he keeps his examples relatively small. It makes it easier to find a starting point for your project. Under the “quest-extras.html”, you’ll find an approachable example for starting with a “player movement” component that works with Oculus Quest. This example also shows an example for object interactivity via raycasting.

<!DOCTYPE html>
<html>

<head>
    <title>A-Frame: Quest movement and interaction</title>
    <meta name="description" content="Moving around an A-Frame scene with Quest touch controllers.">
    <script src="https://aframe.io/releases/1.3.0/aframe.min.js"></script>
    <script src="js/aframe-environment-component.js"></script>
    <script src="js/controller-listener.js"></script>
    <script src="js/player-move.js"></script>
    <script src="js/raycaster-extras.js"></script>
</head>

<body>

<script>
// if raycaster is pointing at this object, press trigger to change color
AFRAME.registerComponent("raycaster-color-change", {
    init: function () 
    {
        this.colors = ["red", "orange", "yellow", "green", "blue", "violet"];
        this.controllerData = document.querySelector("#controller-data").components["controller-listener"];
        this.hoverData      = this.el.components["raycaster-target"];
    },

    tick: function()
    {
        if (this.hoverData.hasFocus && this.controllerData.rightTrigger.pressed )
        {
            let index = Math.floor( this.colors.length * Math.random() );
            let color = this.colors[index];
            this.el.setAttribute("color", color);
        }

        if (!this.hoverData.hasFocus || this.controllerData.rightTrigger.released)
        {
            this.el.setAttribute("color", "#CCCCCC");
        }
    }
});


</script>

<a-scene environment="preset: default;" renderer="antialias: true;">

    <a-assets>
        <img id="gradient" src="images/gradient-fade.png" />
    </a-assets>

    <a-sky 
        color = "#000337">
    </a-sky>

    <!-- use a simple mesh for raycasting/navigation -->
    <a-plane
        width="100" height="100"
        rotation="-90 0 0"
        position="0 0.01 0"
        visible="false"
        class="groundPlane"
        raycaster-target>
    </a-plane>

    <a-entity 
        id="player" 
        position="0 0 0" 
        player-move="controllerListenerId: #controller-data;
                     navigationMeshClass: groundPlane;">

        <a-camera></a-camera>

        <a-entity 
            id="controller-data" 
            controller-listener="leftControllerId:  #left-controller; 
                                 rightControllerId: #right-controller;">
        </a-entity>

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

        <!-- experiment with raycasting interval; slight performance improvement but jittery appearance in world -->
        <a-entity
            id="right-controller"
            oculus-touch-controls="hand: right"
            raycaster="objects: .raycaster-target; interval: 0;"
            raycaster-extras="controllerListenerId: #controller-data; 
                              beamImageSrc: #gradient; beamLength: 0.5;">
        </a-entity>

    </a-entity>

    <a-torus-knot 
        p="2" q="3" radius="0.5" radius-tubular="0.1"
        position = "-2.5 1.5 -4"
        color="#CC3333"
        raycaster-target>
    </a-torus-knot>

    <a-box
        width = "2" height = "1" depth = "1"
        position = "-1 0.5 -3"
        rotation = "0 45 0"  
        color = "#FF8800"
        class = ""
        raycaster-target>
    </a-box>

    <a-sphere
        radius = "1.25"
        position = "0 1.25 -5"
        color = "#DDBB00"
        raycaster-target>
    </a-sphere>

    <a-cylinder
        radius = "0.5" height = "1.5"
        position = " 1 0.75 -3"
        color = "#008800" 
        raycaster-target>
    </a-cylinder>

    <a-cone
        radius-bottom = "1" radius-top = "0" height = "2"
        position = "3 1 -4"
        color = "#4444CC"
        raycaster-target>
    </a-cone>

    <a-torus 
        radius="0.5" radius-tubular="0.1"
        position = "2 3 -4"
        rotation = "30 -20 0"
        color="#8800FF"
        raycaster-target>
    </a-torus>

    <!-- demo interaction boxes -->

    <a-dodecahedron
        radius = "0.5"
        position = "-0.8 1 -2"
        color = "#EEEEEE"
        raycaster-target="canGrab: true;"
        raycaster-color-change>
    </a-dodecahedron>

    <a-icosahedron
        radius = "0.5"
        position = "0.8 1 -2"
        color = "#EEEEEE"
        raycaster-target="canGrab: true;"
        raycaster-color-change>
    </a-icosahedron>

</a-scene>

</body>
</html>

I also admire the work of Ada Rose Canon too. You can find a very complete starter kit for AFrame.IO here:
https://aframe-xr-starterkit.glitch.me/. This example shows features like collision detection, AR integration, and more.

Let us know if you make anything cool!!

Top Stories on InspiredToEducate.NET