• 18-19 College Green, Dublin 2
  • 01 685 9088
  • info@cunninghamwebsolutions.com
  • cunninghamwebsolutions
    Cunningham Web Solutions
    • Home
    • About Us
    • Our Services
      • Web Design
      • Digital Marketing
      • SEO Services
      • E-commerce Websites
      • Website Redevelopment
      • Social Media Services
    • Digital Marketing
      • Adwords
      • Social Media Services
      • Email Marketing
      • Display Advertising
      • Remarketing
    • Portfolio
    • FAQ’s
    • Blog
    • Contact Us
    MENU CLOSE back  

    How To Build An Endless Runner Game In Virtual Reality (Part 1)

    You are here:
    1. Home
    2. Web Design
    3. How To Build An Endless Runner Game In Virtual Reality (Part 1)
    Thumbnail for 22607

    How To Build An Endless Runner Game In Virtual Reality (Part 1)

    How To Build An Endless Runner Game In Virtual Reality (Part 1)

    Alvin Wan

    2019-03-06T14:00:35+01:00
    2019-03-07T12:34:45+00:00

    Today, I’d like to invite you to build an endless runner VR game with webVR — a framework that gives a dual advantage: It can be played with or without a VR headset. I’ll explain the magic behind the gaze-based controls for our VR-headset players by removing the game control’s dependence on a keyboard.

    In this tutorial, I’ll also show you how you can synchronize the game state between two devices which will move you one step closer to building a multiplayer game. I’ll specifically introduce more A-Frame VR concepts such as stylized low-poly entities, lights, and animation.

    To get started, you will need the following:

    • Internet access (specifically to glitch.com);
    • A new Glitch project;
    • A virtual reality headset (optional, recommended). (I use Google Cardboard, which is offered at $15 a piece.)

    Note: A demo of the final product can be viewed here.

    Getting workflow just right ain’t an easy task. So are proper estimates. Or alignment among different departments. That’s why we’ve set up “this-is-how-I-work”-sessions — with smart cookies sharing what works well for them. A part of the Smashing Membership, of course.

    Explore Smashing Membership ↬

    Step 1: Setting Up A Basic Scene

    In this step, we will set up the following scene for our game. It is composed of a few basic geometric shapes and includes custom lighting, which we will describe in more detail below. As you progress in the tutorial, you will add various animations and effects to transform these basic geometric entities into icebergs sitting in an ocean.

    A preview of the game scene's basic geometric objects

    A preview of the game scene’s basic geometric objects (Large preview)

    You will start by setting up a website with a single static HTML page. This allows you to code from your desktop and automatically deploy to the web. The deployed website can then be loaded on your mobile phone and placed inside a VR headset. Alternatively, the deployed website can be loaded by a standalone VR headset.

    Get started by navigating to glitch.com. Then, do the following:

  • Click on “New Project” in the top right.
  • Click on “hello-webpage” in the drop down.

    Glitch.com's homepage

    Glitch.com’s homepage (Large preview)

  • Next, click on index.html in the left sidebar. We will refer to this as your “editor”.
  • Glitch project index.html file

    Glitch project: the index.html file (Large preview)

    Start by deleting all existing code in the current index.html file. Then, type in the following for a basic webVR project, using A-Frame VR. This creates an empty scene by using A-Frame’s default lighting and camera.

    <!DOCTYPE html>
    <html>
      <head>
        <title>Ergo | Endless Runner Game in Virtual Reality</title>
        <script src="https://aframe.io/releases/0.7.0/aframe.min.js"></script>
      </head>
      <body>
        <a-scene>
        </a-scene>
      </body>
    </html>
    

    Note: You can learn more about A-Frame VR at aframe.io.

    To start, add a fog, which will obscure objects far away for us. Modify the a-scene tag on line 8.

    <a-scene fog="type: linear; color: #a3d0ed; near:5; far:20">
    

    Moving forward, all objects in the scene will be added between the ... tags. The first item is the sky. Between your a-scene tags, add the a-sky entity.

    <a-scene ...>
      <a-sky color="#a3d0ed"></a-sky>
    </a-scene>
    

    After your sky, add lighting to replace the default A-Frame lighting.

    There are three types of lighting:

    • Ambient
      This is an ever-present light that appears to emanate from all objects in the scene. If you wanted a blue tint on all objects, resulting in blue-ish shadows, you would add a blue ambient light. For example, the objects in this Low Poly Island scene are all white. However, a blue ambient light results in a blue hue.
    • Directional
      This is analogous to a flashlight which, as the name suggests, points in a certain direction.
    • Point
      Again, as the name suggests, this emanates light from a point.

    Just below your a-sky entity, add the following lights: one directional and one ambient. Both are light blue.

    <!-- Lights -->
    <a-light type="directional" castShadow="true" intensity="0.4" color="#D0EAF9;" position="5 3 1"></a-light>
    <a-light intensity="0.8" type="ambient" color="#B4C5EC"></a-light>
    

    Next, add a camera with a custom position to replace the default A-Frame camera. Just below your a-light entities, add the following:

    <!-- Camera -->
    <a-camera position="0 0 2.5"></a-camera>
    

    Just below your a-camera entity, add several icebergs using low-poly cones.

    <!-- Icebergs -->
    <a-cone class="iceberg" segments-radial="5" segments-height="3" height="1" radius-top="0.15" radius-bottom="0.5" position="3 -0.1 -1.5"></a-cone>
    <a-cone class="iceberg" segments-radial="7" segments-height="3" height="0.5" radius-top="0.25" radius-bottom="0.35" position="-3 -0.1 -0.5"></a-cone>
    <a-cone class="iceberg" segments-radial="6" segments-height="2" height="0.5" radius-top="0.25" radius-bottom="0.25" position="-5 -0.2 -3.5"></a-cone>
    

    Next, add an ocean, which we will temporarily represent with a box, among your icebergs. In your code, add the following after the cones from above.

    <!-- Ocean -->
    <a-box depth="50" width="50" height="1" color="#7AD2F7" position="0 -0.5 0"></a-box>
    

    Next, add a platform for our endless runner game to take place on. We will represent this platform using the side of a large cone. After the box above, add the following:

    <!-- Platform -->
    <a-cone scale="2 2 2" shadow position="0 -3.5 -1.5" rotation="90 0 0" radius-top="1.9" radius-bottom="1.9" segments-radial="20" segments-height="20" height="20" emissive="#005DED" emissive-intensity="0.1">
      <a-entity id="tree-container" position="0 .5 -1.5" rotation="-90 0 0">
      </a-entity>
    </a-cone>
    

    Finally, add the player, which we will represent using a small glowing sphere, on the platform we just created. Between the tags, add the following:

    <a-entity id="tree-container"...>
      <!-- Player -->
      <a-entity id="player" player>
        <a-sphere radius="0.05">
          <a-light type="point" intensity="0.35" color="#FF440C"></a-light>
        </a-sphere>
      </a-entity>
    </a-entity>
    

    Check that your code now matches the following, exactly. You can also view the full source code for step 1.

    <!DOCTYPE html>
    <html>
      <head>
        <title>Ergo | Endless Runner Game in Virtual Reality</title>
        <script src="https://aframe.io/releases/0.7.0/aframe.min.js"></script>
      </head>
      <body>
        <a-scene fog="type: linear; color: #a3d0ed; near:5; far:20">
    
          <a-sky color="#a3d0ed"></a-sky>
    
          <!-- Lights -->
          <a-light type="directional" castShadow="true" intensity="0.4" color="#D0EAF9;" position="5 3 1"></a-light>
          <a-light intensity="0.8" type="ambient" color="#B4C5EC"></a-light>
    
          <!-- Camera -->
          <a-camera position="0 0 2.5"></a-camera>
    
          <!-- Icebergs -->
          <a-cone class="iceberg" segments-radial="5" segments-height="3" height="1" radius-top="0.15" radius-bottom="0.5" position="3 -0.1 -1.5"></a-cone>
          <a-cone class="iceberg" segments-radial="7" segments-height="3" height="0.5" radius-top="0.25" radius-bottom="0.35" position="-3 -0.1 -0.5"></a-cone>
          <a-cone class="iceberg" segments-radial="6" segments-height="2" height="0.5" radius-top="0.25" radius-bottom="0.25" position="-5 -0.2 -3.5"></a-cone>
    
          <!-- Ocean -->
          <a-box depth="50" width="50" height="1" color="#7AD2F7" position="0 -0.5 0"></a-box>
    
          <!-- Platform -->
          <a-cone scale="2 2 2" shadow position="0 -3.5 -1.5" rotation="90 0 0" radius-top="1.9" radius-bottom="1.9" segments-radial="20" segments-height="20" height="20" emissive="#005DED" emissive-intensity="0.1">
            <a-entity id="tree-container" position="0 .5 -1.5" rotation="-90 0 0">
              <!-- Player -->
              <a-entity id="player" player>
                <a-sphere radius="0.05">
                  <a-light type="point" intensity="0.35" color="#FF440C"></a-light>
                </a-sphere>
              </a-entity>
            </a-entity>
          </a-cone>
        </a-scene>
      </body>
    </html>
    

    To preview the webpage, click on “Preview” in the top left. We will refer to this as your preview. Note that any changes in your editor will be automatically reflected in this preview, barring bugs or unsupported browsers.

    “Show Live” button in glitch project

    “Show Live” button in glitch project (Large preview)

    In your preview, you will see the following basic virtual reality scene. You can view this scene by using your favorite VR headset.

    Animating Ocean and the fixed white cursor (Large preview)

    This concludes the first step, setting up the game scene’s basic geometric objects. In the next step, you will add animations and use other A-Frame VR libraries for more visual effects.

    Step 2: Improve Aesthetics for Virtual Reality Scene

    In this step, you will add a number of aesthetic improvements to the scene:

  • Low-poly objects
    You will substitute some of the basic geometric objects with their low-poly equivalents for more convincing, irregular geometric shapes.
  • Animations
    You will have the player bob up and down, move the icebergs slightly, and make the ocean a moving body of water.
  • Your final product for this step will match the following:

    Low-poly icebergs bobbing aroundLow-poly icebergs bobbing around (Large preview)

    To start, import A-Frame low-poly components. In ..., add the following JavaScript import:

     <script src="https://aframe.io...></script>
      <script src="https://cdn.jsdelivr.net/gh/alvinwan/aframe-low-poly@0.0.2/dist/aframe-low-poly.min.js"></script>
    </head>
    

    The A-Frame low-poly library implements a number primitives, such as lp-cone and lp-sphere, each of which is a low-poly version of an A-Frame primitive. You can learn more about A-Frame primitives over here.

    Next, navigate to the section of your code. Replace all s with .

    <!-- Icebergs -->
    <lp-cone class="iceberg" ...></lp-cone>
    <lp-cone class="iceberg" ...></lp-cone>
    <lp-cone class="iceberg" ...></lp-cone>
    

    We will now configure the low-poly primitives. All low-poly primitive supports two attributes, which control how exaggerated the low-poly stylization is:

  • amplitude
    This is the degree of stylization. The greater this number, the more a low-poly shape can deviate from its original geometry.
  • amplitude-variance
    This is how much stylization can vary, from vertex to vertex. The greater this number, the more variety there is in how much each vertex may deviate from its original geometry.
  • To get a better intuition for what these two variables mean, you can modify these two attributes in the A-Frame low-poly demo.

    For the first iceberg, set amplitude-variance to 0.25. For the second iceberg, set amplitude to 0.12. For the last iceberg, set amplitude to 0.1.

    <!-- Icebergs -->
    <lp-cone class="iceberg" amplitude-variance="0.25" ...></lp-cone>
    <lp-cone class="iceberg" amplitude="0.12" ... ></lp-cone>
    <lp-cone class="iceberg" amplitude="0.1" ...></lp-cone>
    

    To finish the icebergs, animate both position and rotation for all three icebergs. Feel free to configure these positions and rotations as desired.

    The below features a sample setting:

    <lp-cone class="iceberg" amplitude-variance="0.25" ...>
            <a-animation attribute="rotation" from="-5 0 0" to="5 0 0" repeat="indefinite" direction="alternate"></a-animation>
            <a-animation attribute="position" from="3 -0.2 -1.5" to="4 -0.2 -2.5" repeat="indefinite" direction="alternate" dur="12000" easing="linear"></a-animation>
          </lp-cone>
          <lp-cone class="iceberg" amplitude="0.12" ...>
            <a-animation attribute="rotation" from="0 0 -5" to="5 0 0" repeat="indefinite" direction="alternate" dur="1500"></a-animation>
            <a-animation attribute="position" from="-4 -0.2 -0.5" to="-2 -0.2 -0.5" repeat="indefinite" direction="alternate" dur="15000" easing="linear"></a-animation>
          </lp-cone>
          <lp-cone class="iceberg" amplitude="0.1" ...>
            <a-animation attribute="rotation" from="5 0 -5" to="5 0 0" repeat="indefinite" direction="alternate" dur="800"></a-animation>
            <a-animation attribute="position" from="-3 -0.2 -3.5" to="-5 -0.2 -5.5" repeat="indefinite" direction="alternate" dur="15000" easing="linear"></a-animation>
          </lp-cone>
    

    Navigate to your preview, and you should see the low-poly icebergs bobbing around.

    Bobbing player with fluctuating lightBobbing player with fluctuating light (Large preview)

    Next, update the platform and associated player. Here, upgrade the cone to a low-poly object, changing a-cone to lp-cone for . Additionally, add configurations for amplitude.

    <!-- Platform -->
    <lp-cone amplitude="0.05" amplitude-variance="0.05" scale="2 2 2"...>
        ...
    </lp-cone>
    

    Next, still within the platform section, navigate to the subsection of your code. Add the following animations for position, size, and intensity.

    <!-- Player -->
    <a-entity id="player" ...>
      <a-sphere ...>
        <a-animation repeat="indefinite" direction="alternate" attribute="position" ease="ease-in-out" from="0 0.5 0.6" to="0 0.525 0.6"></a-animation>
        <a-animation repeat="indefinite" direction="alternate" attribute="radius" from="0.05" to="0.055" dur="1500"></a-animation>
        <a-light ...>
          <a-animation repeat="indefinite" direction="alternate-reverse" attribute="intensity" ease="ease-in-out" from="0.35" to="0.5"></a-animation>
        </a-light>
      </a-sphere>
    </a-entity>
    

    Navigate to your preview, and you will see your player bobbing up and down, with a fluctuating light on a low-poly platform.

    Bobbing player with fluctuating lightBobbing player with fluctuating light (Large preview)

    Next, let’s animate the ocean. Here, you can use a lightly-modified version of Don McCurdy’s ocean. The modifications allow us to configure how large and fast the ocean’s waves move.

    Create a new file via the Glitch interface, by clicking on “+ New File” on the left. Name this new file assets/ocean.js. Paste the following into your new ocean.js file:

    /**
     * Flat-shaded ocean primitive.
     * https://github.com/donmccurdy/aframe-extras
     *
     * Based on a Codrops tutorial:
     * http://tympanus.net/codrops/2016/04/26/the-aviator-animating-basic-3d-scene-threejs/
     */
    AFRAME.registerPrimitive('a-ocean', {
      defaultComponents: {
        ocean: {},
        rotation: {x: -90, y: 0, z: 0}
      },
      mappings: {
        width: 'ocean.width',
        depth: 'ocean.depth',
        density: 'ocean.density',
        amplitude: 'ocean.amplitude',
        'amplitude-variance': 'ocean.amplitudeVariance',
        speed: 'ocean.speed',
        'speed-variance': 'ocean.speedVariance',
        color: 'ocean.color',
        opacity: 'ocean.opacity'
      }
    });
    
    AFRAME.registerComponent('ocean', {
      schema: {
        // Dimensions of the ocean area.
        width: {default: 10, min: 0},
        depth: {default: 10, min: 0},
    
        // Density of waves.
        density: {default: 10},
    
        // Wave amplitude and variance.
        amplitude: {default: 0.1},
        amplitudeVariance: {default: 0.3},
    
        // Wave speed and variance.
        speed: {default: 1},
        speedVariance: {default: 2},
    
        // Material.
        color: {default: '#7AD2F7', type: 'color'},
        opacity: {default: 0.8}
      },
    
      /**
       * Use play() instead of init(), because component mappings – unavailable as dependencies – are
       * not guaranteed to have parsed when this component is initialized.
       * /
      play: function () {
        const el = this.el,
            data = this.data;
        let material = el.components.material;
    
        const geometry = new THREE.PlaneGeometry(data.width, data.depth, data.density, data.density);
        geometry.mergeVertices();
        this.waves = [];
        for (let v, i = 0, l = geometry.vertices.length; i 

    Navigate back to your index.html file. In the of your code, import the new JavaScript file:

     <script src="https://cdn.jsdelivr.net..."></script>
      <script src="./assets/ocean.js"></script>
    </head>
    

    Navigate to the section of your code. Replace the a-box to an a-ocean. Just as before, we set amplitude and amplitude-variance of our low-poly object.

    <!-- Ocean -->
    <a-ocean depth="50" width="50" amplitude="0" amplitude-variance="0.1" speed="1.5" speed-variance="1" opacity="1" density="50"></a-ocean>
    <a-ocean depth="50" width="50" opacity="0.5" amplitude="0" amplitude-variance="0.15" speed="1.5" speed-variance="1" density="50"></a-ocean>
    

    For your final aesthetic modification, add a white round cursor to indicate where the user is pointing. Navigate to the .

    <!-- Camera -->
    <a-camera ...>
      <a-entity id="cursor-mobile" cursor="fuse: true; fuseTimeout: 250"
        position="0 0 -1"
        geometry="primitive: ring; radiusInner: 0.02; radiusOuter: 0.03"
        material="color: white; shader: flat"
        scale="0.5 0.5 0.5"
        raycaster="far: 50; interval: 1000; objects: .clickable">
      <a-animation begin="fusing" easing="ease-in" attribute="scale"
        fill="backwards" from="1 1 1" to="0.2 0.2 0.2" dur="250"></a-animation>
      </a-camera>
    

    Ensure that your index.html code matches the Step 2 source code. Navigate to your preview, and you’ll find the updated ocean along with a white circle fixed to the center of your view.

    Bobbing player with fluctuating lightBobbing player with fluctuating light (Large preview)

    This concludes your aesthetic improvements to the scene. In this section, you learned how to use and configure low-poly versions of A-Frame primitives, e.g. lp-cone. In addition, you added a number of animations for different object attributes, such as position, rotation, and light intensity. In the next step, you will add the ability for the user to control the player — just by looking at different lanes.

    Step 3: Add Virtual Reality Gaze Controls

    Recall that our audience is a user wearing a virtual reality headset. As a result, your game cannot depend on keyboard input for controls. To make this game accessible, our VR controls will rely only on the user’s head rotation. Simply look to the right to move the player to the right, look to the center to move to the middle, and look to the left to move to the left. Our final product will look like the following.

    Note: The demo GIF below was recorded on a desktop, with user drag as a substitute for head rotation.

    Controlling game character with head rotationControlling game character with head rotation (Large preview)

    Start from your index.html file. In the ... tag, import your new JavaScript file, assets/ergo.js. This new JavaScript file will contain the game’s logic.

     <script src=...></script>
      <script src="./assets/ergo.js"></script>
    </head>
    

    Then, add a new lane-controls attribute to your a-camera object:

    <!-- Camera -->
    <a-camera lane-controls position...>
    </a-camera>
    

    Next, create your new JavaScript file using “+ New File” to the left. Use assets/ergo.js for the filename. For the remainder of this step, you will be working in this new JavaScript file. In this new file, define a new function to setup controls, and invoke it immediately. Make sure to include the comments below, as we will refer to sections of code by those names.

    /************
     * CONTROLS *
     ************/
    
    function setupControls() {
    }
    
    /********
     * GAME *
     ********/
    
    setupControls();
    

    Note: The setupControls function is invoked in the global scope, because A-Frame components must be registered before the tag. I will explain what a component is below.

    In your setupControls function, register a new A-Frame component. A component modifies an entity in A-Frame, allowing you to add custom animations, change how an entity initializes, or respond to user input. There are many other use cases, but you will focus on the last one: responding to user input. Specifically, you will read user rotation and move the player accordingly.

    In the setupControls function, register the A-Frame component we added to the camera earlier, lane-controls. We will add an event listener for the tick event. This event triggers at every animation frame. In this event listener, hlog output at every tick.

    function setupControls() {
        AFRAME.registerComponent('lane-controls', {
            tick: function(time, timeDelta) {
                console.log(time);
            }
        });
    }
    

    Navigate to your preview. Open your browser developer console by right-clicking anywhere and selecting “Inspect”. This applies to Firefox, Chrome, and Safari. Then, select “Console” from the top navigation bar. Ensure that you see timestamps flowing into the console.

    Timestamps in console

    Timestamps in console (Large preview)

    Navigate back to your editor. Still in assets/ergo.js, replace the body of setupControls with the following. Fetch the camera rotation using this.el.object3D.rotation, and log the lane to move the player to.

    function setupControls() {
      AFRAME.registerComponent('lane-controls', {
        tick: function (time, timeDelta) {
          var rotation = this.el.object3D.rotation;
    
          if      (rotation.y > 0.1)  console.log("left");
          else if (rotation.y 

    Navigate back to your preview. Again, open your developer console. Try rotating the camera slightly, and observe console output update accordingly.

    Lane log based on camera rotationLane log based on camera rotation (Large preview)

    Before the controls section, add three constants representing the left, middle, and right lane x values.

    const POSITION_X_LEFT = -0.5;
    const POSITION_X_CENTER = 0;
    const POSITION_X_RIGHT = 0.5;
    
    /************
     * CONTROLS *
     ************/
    
    ...
    

    At the start of the controls section, define a new global variable representing the player position.

    /************
     * CONTROLS *
     ************/
    
    // Position is one of 0 (left), 1 (center), or 2 (right)
    var player_position_index = 1;
    
    function setupControls() {
    ...
    

    After the new global variable, define a new function that will move the player to each lane.

    var player_position_index = 1;
    
    /**
     * Move player to provided index
     * @param {int} Lane to move player to
     */
    function movePlayerTo(position_index) {
    }
    
    function setupControls() {
    ...
    

    Inside this new function, start by updating the global variable. Then, define a dummy position.

    function movePlayerTo(position_index) {
      player_position_index = position_index;
    
      var position = {x: 0, y: 0, z: 0}
    }
    

    After defining the position, update it according to the function input.

    function movePlayerTo(position_index) {
      ...
      if      (position_index == 0) position.x = POSITION_X_LEFT;
      else if (position_index == 1) position.x = POSITION_X_CENTER;
      else                          position.x = POSITION_X_RIGHT;
    }
    

    Finally, update the player position.

    function movePlayerTo(position_index) {
        ...
    document.getElementById('player').setAttribute('position', position);
    }
    

    Double-check that your function matches the following.

    /**
     * Move player to provided index
     * @param {int} Lane to move player to
     */
    function movePlayerTo(position_index) {
      player_position_index = position_index;
      
      var position = {x: 0, y: 0, z: 0}
      if      (position_index == 0) position.x = POSITION_X_LEFT;
      else if (position_index == 1) position.x = POSITION_X_CENTER;
      else                          position.x = POSITION_X_RIGHT;
      document.getElementById('player').setAttribute('position', position);
    }
    

    Navigate back to your preview. Open the developer console. Invoke your new movePlayerTo function from the console to ensure that it functions.

    > movePlayerTo(2)  # should move to right
    

    Navigate back to your editor. For the final step, update your setupControls to move the player depending on camera rotation. Here, we replace the console.log with movePlayerTo invocations.

    function setupControls() {
      AFRAME.registerComponent('lane-controls', {
        tick: function (time, timeDelta) {
          var rotation = this.el.object3D.rotation;
    
          if      (rotation.y > 0.1)  movePlayerTo(0);
          else if (rotation.y 

    Ensure that your assets/ergo.js matches the corresponding file in the Step 3 source code. Navigate back to your preview. Rotate the camera from side to side, and your player will now track the user’s rotation.

    Controlling game character with head rotationControlling game character with head rotation (Large preview)

    This concludes gaze controls for your virtual reality endless runner game.

    In this section, we learned how to use A-Frame components and saw how to modify A-Frame entity properties. This also concludes part 1 of our endless runner game tutorial. You now have a virtual reality model equipped with aesthetic improvements like low-poly stylization and animations, in addition to a virtual-reality-headset-friendly gaze control for players to use.

    Conclusion

    We created a simple, interactive virtual reality model, as a start for our VR endless runner game. We covered a number of A-Frame concepts such as primitives, animations, and components — all of which are necessary for building a game on top of A-Frame VR.

    Here are extra resources and next steps for working more with these technologies:

    • A-Frame VR
      Official documentation for A-Frame VR, covering the topics used above in more detail.
    • A-Frame Homepage
      Examples of A-Frame projects, exhibiting different A-Frame capabilities.
    • Low-Poly Island
      VR model using the same lighting, textures, and animations as the ones used for this endless runner game.

    In the next part of this article series, I’ll show you how you can implement the game’s core logic and use more advanced A-Frame VR scene manipulations in JavaScript.

    Stay tuned for next week!

    Smashing Editorial
    (rb, ra, il)

    From our sponsors: How To Build An Endless Runner Game In Virtual Reality (Part 1)

    Posted on 7th March 2019Web Design
    FacebookshareTwittertweetGoogle+share

    Related posts

    Archived
    22nd March 2023
    Archived
    18th March 2023
    Archived
    20th January 2023
    Thumbnail for 25788
    Handling Continuous Integration And Delivery With GitHub Actions
    19th October 2020
    Thumbnail for 25778
    A Monthly Update With New Guides And Community Resources
    19th October 2020
    Thumbnail for 25781
    Supercharge Testing React Applications With Wallaby.js
    19th October 2020
    Latest News
    • Archived
      22nd March 2023
    • Archived
      18th March 2023
    • Archived
      20th January 2023
    • 20201019 ML Brief
      19th October 2020
    • Thumbnail for 25788
      Handling Continuous Integration And Delivery With GitHub Actions
      19th October 2020
    • Thumbnail for 25786
      The Future of CX with Larry Ellison
      19th October 2020
    News Categories
    • Digital Marketing
    • Web Design

    Our services

    Website Design
    Website Design

    A website is an important part of any business. Professional website development is an essential element of a successful online business.

    We provide website design services for every type of website imaginable. We supply brochure websites, E-commerce websites, bespoke website design, custom website development and a range of website applications. We love developing websites, come and talk to us about your project and we will tailor make a solution to match your requirements.

    You can contact us by phone, email or send us a request through our online form and we can give you a call back.

    More Information

    Digital Marketing
    Digital Marketing

    Our digital marketeers have years of experience in developing and excuting digital marketing strategies. We can help you promote your business online with the most effective methods to achieve the greatest return for your marketing budget. We offer a full service with includes the following:

    1. Social Media Marketing

    2. Email & Newsletter Advertising

    3. PPC - Pay Per Click

    4. A range of other methods are available

    More Information

    SEO
    SEO Services

    SEO is an essential part of owning an online property. The higher up the search engines that your website appears, the more visitors you will have and therefore the greater the potential for more business and increased profits.

    We offer a range of SEO services and packages. Our packages are very popular due to the expanse of on-page and off-page SEO services that they cover. Contact us to discuss your website and the SEO services that would best suit to increase your websites ranking.

    More Information

    E-commerce
    E-commerce Websites

    E-commerce is a rapidly growing area with sales online increasing year on year. A professional E-commerce store online is essential to increase sales and is a reflection of your business to potential customers. We provide professional E-commerce websites custom built to meet our clients requirements.

    Starting to sell online can be a daunting task and we are here to make that journey as smooth as possible. When you work with Cunningham Web Solutions on your E-commerce website, you will benefit from the experience of our team and every detail from the website design to stock management is carefully planned and designed with you in mind.

    More Information

    Social Media Services
    Social Media Services

    Social Media is becoming an increasingly effective method of marketing online. The opportunities that social media marketing can offer are endless and when managed correctly can bring great benefits to every business.

    Social Media Marketing is a low cost form of advertising that continues to bring a very good ROI for our clients. In conjuction with excellent website development and SEO, social media marketing should be an essential part of every digital marketing strategy.

    We offer Social Media Management packages and we also offer Social Media Training to individuals and to companies. Contact us to find out more.

    More Information

    Cunningham Web Solutions
    © Copyright 2025 | Cunningham Web Solutions
    • Home
    • Our Services
    • FAQ's
    • Account Services
    • Privacy Policy
    • Contact Us