Talking about A-Frame Creating a Web 3D Interactive Game from scratch

Instead of learning a new language, AR can be done only with the knowledge available at the front end. And in our familiar Web environment, it can be done without APP.

1. What is A-Frame

The naming of this framework matches the "and" of mobile 4G, resulting in a perfect miss of all keywords.
A will be ignored by browsers - conjunctions, frame has too many ambiguities. If typed out, a frame will also be "smart" browser split into a frame into "a framework".
This leads to the fact that it is not easy to search for information, and the feeling of seeking gold in the sand, but it may also be due to the lack of personal data.

Updated on May 02, 2017, it is uninformed. When searching, quotation marks are used to contain keywords, which represent the full matching search. Searching "A-Frame" can get accurate results.

A-Frame A framework for creating 3d scenarios in HTML, using Three.js and WebGL to create VR scenarios.
You don't need to know the details of low-level rendering, but the A-Frame documentation is very long.

2. Simplest demo

stay http://codepen.io open CodePreview in codepen.io

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

<a-scene>
  <a-sphere position="0 1.25 -5" radius="1.25" color="#EF2D5E"></a-sphere>
  <a-box position="-1 0.5 -3" rotation="0 45 0" width="1" height="1" depth="1" color="#4CC3D9"></a-box>
  <a-cylinder position="1 0.75 -3" radius="0.5" height="1.5" color="#FFC65D"></a-cylinder>
  <a-plane position="0 0 -4" rotation="-90 0 0" width="4" height="4" color="#7BC8A4"></a-plane>
  
  <a-sky color="#ECECEC"></a-sky>
</a-scene>

The <a-sky> in the code is a pure color background, and the others can see which one is by name.
This demo can also be navigated with a mouse and keyboard. If you open it with a mobile browser, it will be a VR effect.

3. Make your own scene from scratch

3.1 template

All elements of A-Frame are placed in <a-scene> with the initial code as follows:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8"/>
    <title>Our First A-Frame Experience</title>
    <script src="https://aframe.io/releases/0.4.0/aframe.min.js"></script>
  </head>
  <body>
    <a-scene>
      
    </a-scene>
  </body>
</html>

3.2 sky

The elements used in the sky are <a-sky>, coded as follows:

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

At this point a purple-red sky will be created. The sky can also be a panorama.
flickr There are many panoramas. Let's choose one as the background, for example This one:

Now change the sky into this panorama.

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8"/>
    <title>Our First A-Frame Experience</title>
    <script src="https://aframe.io/releases/0.4.0/aframe.min.js"></script>
  </head>
  <body>
    <a-scene>
      <a-sky src="https://c1.staticflickr.com/8/7376/16218590470_468084c950_h.jpg"></a-sky>
    </a-scene>
  </body>
</html>

Now the code (in http://codepen.io open CodePreview in codepen.io)

Does it feel immersive to see the effect with mobile phone now?

3.3 Put a goal in

<a-scene>
  <a-sky src="https://c1.staticflickr.com/8/7376/16218590470_468084c950_h.jpg"></a-sky>
  <a-sphere position="0 1.25 -5" radius="1.25" color="#66ffcc"></a-sphere>
</a-scene>
  

Now there's a blue ball in the scene. Look directly at the effect.

The ball can also not be pure color, so we need to map the surface of the ball. subtlepatterns Choose a material.
on This wood grain Bar:

<a-scene>
  <a-sky src="https://c1.staticflickr.com/8/7376/16218590470_468084c950_h.jpg"></a-sky>
  <a-sphere position="0 1.25 -5" radius="1.25" src="https://www.toptal.com/designers/subtlepatterns/patterns/retina_wood.png"></a-sphere>
</a-scene>
  

The effect is this.

3.4 Cursor Interaction

There are also corresponding interaction schemes in VR. We now add animation and event libraries. Add a camera and curosr to the scene.

<script src="https://aframe.io/releases/0.4.0/aframe.min.js"></script>
<script src="https://npmcdn.com/aframe-animation-component@3.0.1"></script>
<script src="https://npmcdn.com/aframe-event-set-component@3.0.1"></script>

<a-scene>
  <a-sky src="https://c1.staticflickr.com/8/7376/16218590470_468084c950_h.jpg"></a-sky>
  <a-sphere position="0 1.25 -5" radius="1.25" src="https://www.toptal.com/designers/subtlepatterns/patterns/retina_wood.png"></a-sphere>

  <!-- Camera + cursor. -->
  <a-entity camera look-controls>
    <a-cursor id="cursor" 
        animation__click="property: scale; startEvents: click; from: 0.1 0.1 0.1; to: 1 1 1; dur: 150"
        animation__fusing="property: fusing; startEvents: fusing; from: 1 1 1; to: 0.1 0.1 0.1; dur: 1500"
        event-set__1="_event: mouseenter; color: #0092d8" 
        event-set__2="_event: mouseleave; color: black"></a-cursor>
  </a-entity>
</a-scene>

Now code( View the code in codepen.ioPreview in codepen.io).


Now as the viewport (camera) moves, the cursor (fixed relative to the camera) in the center of the screen moves with the camera.

Like the mouse, the cursor encounters a ball, triggers the mouseenter event, and leaves the ball to trigger the mouseleave event. Now we have added these two events, the cursor turns blue when entering, and the default black when leaving. Click with cursor zooming effect.

3.5 Interaction with the Sphere in the Scene

Just like clicking a button with a mouse, this cursor can also trigger element events in the scene, but there is no ready-made code base at this time, so you have to write event bindings yourself.

<script src="https://aframe.io/releases/0.4.0/aframe.min.js"></script>
<script src="https://npmcdn.com/aframe-animation-component@3.0.1"></script>
<script src="https://npmcdn.com/aframe-event-set-component@3.0.1"></script>
<script>
    AFRAME.registerComponent('hide-on-click', {
        dependencies: ['raycaster'],
        schema: {
            target:{type: 'selector'}
        },
        init: function () {
            var data = this.data;
            var el = this.el;
            el.addEventListener('click', function () {
                el.setAttribute('visible', false);
                data.target.setAttribute('visible', true);
            });
        }
    });
</script>

<a-scene>
  <a-sky src="https://c1.staticflickr.com/8/7376/16218590470_468084c950_h.jpg"></a-sky>
  <a-sphere hide-on-click="target:#another_cube" position="0 1.25 -5" radius="1.25" src="https://www.toptal.com/designers/subtlepatterns/patterns/retina_wood.png"></a-sphere>
  <a-box id="another_cube" visible="false" position="-1 1.5 -4" rotation="0 45 0" width="1" height="1" depth="1" color="#4CC3D9"></a-box>

  <!-- Camera + cursor. -->
  <a-entity camera look-controls>
    <a-cursor id="cursor" 
        animation__click="property: scale; startEvents: click; from: 0.1 0.1 0.1; to: 1 1 1; dur: 150"
        animation__fusing="property: fusing; startEvents: fusing; from: 1 1 1; to: 0.1 0.1 0.1; dur: 1500"
        event-set__1="_event: mouseenter; color: #0092d8" 
        event-set__2="_event: mouseleave; color: black"></a-cursor>
  </a-entity>
</a-scene>

Now code( Open the code in codepen.ioPreview in codepen.io).

Now if you use PC browser to see the effect, drag the mouse around to move the camera (while moving the cursor), the mouse clicks anywhere, and the cursor triggers the click event. After clicking on the ball, the ball disappears and the box of the cube appears.

4 Make an Interactive Game

My game process is to enter the page and generate an egg at random. The user clicks on the egg with the cursor, hides the egg, then plays the egg shell breaking animation, and finally triggers the pop-up window to prompt the user that you get a chicken.

4.1 Basic Code

It's similar to the one above, except that the binding event and the ball are removed. Because the ball is too round, not like an egg. You can only replace it with a static picture of eggs. Why not use elliptical 3d elements, because there are too many polygons and too much memory for objects other than square, cylinder, circle and plane.

Another library, aframe-html-shader, is needed to show graphic graphics.

<script src="https://aframe.io/releases/0.4.0/aframe.min.js"></script>
<script src="https://npmcdn.com/aframe-animation-component@3.0.1"></script>
<script src="https://npmcdn.com/aframe-event-set-component@3.0.1"></script>
<script src="https://npmcdn.com/aframe-layout-component@3.0.1"></script>
<script src="https://rawgit.com/mayognaise/aframe-html-shader/master/dist/aframe-html-shader.min.js"></script>

<script>
    AFRAME.registerComponent('hide-on-click', {
        dependencies: ['raycaster'],
        schema: {
            target:{type: 'selector'}
        },
        init: function () {
            var data = this.data;
            var el = this.el;
            el.addEventListener('click', function () {
                el.setAttribute('visible', false);
                data.target.setAttribute('visible', true);
                
            });
        }
    });
</script>

<div id="textToDisplay"><img src="./frame_1.png" alt=""></div>
<a-scene>
  <a-sky src="https://c1.staticflickr.com/8/7376/16218590470_468084c950_h.jpg"></a-sky>
  
    <a-entity hide-on-click="target:#chickenAnimate">
        <a-entity id="default_egg"
                  geometry="primitive:plane;width:2;height:2;"
                  position="-3 2 -10"
                  material="shader:html;target:#textToDisplay;transparent:true;fps:0;">
        </a-entity>
    </a-entity>
    <a-box id="chickenAnimate" visible="false" position="-1 1.5 -4" rotation="0 45 0" width="1" height="1" depth="1" color="#4CC3D9"></a-box>


  <!-- Camera + cursor. -->
  <a-entity camera look-controls>
    <a-cursor id="cursor" 
        animation__click="property: scale; startEvents: click; from: 0.1 0.1 0.1; to: 1 1 1; dur: 150"
        animation__fusing="property: fusing; startEvents: fusing; from: 1 1 1; to: 0.1 0.1 0.1; dur: 1500"
        event-set__1="_event: mouseenter; color: #0092d8" 
        event-set__2="_event: mouseleave; color: black"></a-cursor>
  </a-entity>
</a-scene>

The above code change adds a div#textToDisplay image with only one egg, which will be used to render to a-entity#default_egg. The grammar is to set the value of matrix. Specific reference
github aframe-html-shader Project Home.

There is a problem here. The aframe-html-shader principle is to use canvas to render elements in DOM. If there is a picture, it will need to be processed by canvas, which involves cross-domain issues.

Cross-domain problems can be solved by html2canvas-php-proxy Solve,
But there is a security risk in deploying this agent on the server, so our demo is tested in the same domain.

The demo address is: demo7

When you click on the egg with the cursor, the egg will disappear and the cube will appear.

4.2 Play Animation

The animation of eggs also needs to be rendered with canvas. Here we introduce a library:

aframe-gif-shader github address

<script src="https://aframe.io/releases/0.5.0/aframe.min.js"></script>
<script src="https://npmcdn.com/aframe-animation-component@3.0.1"></script>
<script src="https://npmcdn.com/aframe-event-set-component@3.0.1"></script>
<script src="https://npmcdn.com/aframe-layout-component@3.0.1"></script>
<script src="https://rawgit.com/mayognaise/aframe-html-shader/master/dist/aframe-html-shader.min.js"></script>
<script src="./aframe-gif-shader.js"></script>

Then replace the cube box with the incubator animation:

<a-entity id="chickenAnimate" visible="false">
    <a-entity id="animate_egg" geometry="primitive:plane;width:2;height:2;"
              position="-3 2 -10"
              material="shader:gif;src:url(./egg_2017_1.gif);transparent:true;"
              gif=""></a-entity>
</a-entity>

The gif rendering principle here is to load and parse each frame of animation with js and render it with canvas.

Here are two pits:

  • Forgetting to empty the canvas before rendering each frame in the aframe-gif-shader component results in overlapping effects. (No wonder demo uses opaque gif images.) I've fixed this problem, so I refer to local files.

  • The third-party optimization algorithm is used in the aframe-gif-shader component, but there are bug s in this optimization algorithm. Too many transparent parts in the upper part of two consecutive frames are considered redundant frames to be optimized. Here you can modify the gif material to bypass it.

The script also adds a little content. After playing the animation of hatching eggs, wait for 3 seconds to pop up alert to remind users of the game results, and then reset the game.

AFRAME.registerComponent('hide-on-click', {
    dependencies: ['raycaster'],
    schema: {
        target: {type: 'selector'}
    },
    init: function () {
        var data = this.data;
        var el = this.el;
        el.addEventListener('click', function () {
            el.setAttribute('visible', false);
            data.target.setAttribute('visible', true);
            setTimeout(function(){
                alert('Congratulations on getting a chicken!');
                window.location.reload();
            },5000);
        });
    }
});

See the full effect and source code: demo8

4.3 Increase Interesting, Random Location

Now the game can be played, but every time the eggs are in a unified position, there is no need to find. To add interest, we randomly change the location of the eggs.


But according to my test, completely random games are not fun at all. Too far away, they will become several pixels in size. They can't be found at all. Too close, they have a feeling of pasting on the face. There are also left and right positions can not be too absurd.


After many tests, it is decided to put the position on the circle of fixed distance around the user station. When the height is a little higher than the height, it is more reasonable.

I turned out the junior high school mathematics textbook and found the following formula:

var R = Math.random() * 360; // radian
var r = 8; // radius
var X = Math.sin(R) * r; // x coordinates
var Z = Math.cos(R) * r; // Top view z coordinates

$('#default_egg,#animate_egg').attr('position',{ x: X, y: 2, z: Z });

This position is random, but the eggs are plane graphics, facing the camera, or there will be a feeling of sitting next to the window in the first row of the classroom looking at the blackboard.

At this point, another library is needed, separated from the official plug-in library. kframe Plug-in libraries, including one look-at Plug-ins, by name, know that the purpose is to make elements face the lens. There are so many js references added:

<script src="https://aframe.io/releases/0.5.0/aframe.min.js"></script>
<script src="https://npmcdn.com/aframe-animation-component@3.0.1"></script>
<script src="https://npmcdn.com/aframe-event-set-component@3.0.1"></script>
<script src="https://npmcdn.com/aframe-layout-component@3.0.1"></script>
<script src="https://rawgit.com/mayognaise/aframe-html-shader/master/dist/aframe-html-shader.min.js"></script>
<script src="./aframe-gif-shader.js"></script>
<script src="./aframe-look-at-component.min.js"></script>

There is no cdn address found using the local version.

Attribute binding is added to the element:

<a-entity hide-on-click="target:#chickenAnimate">
    <a-entity id="default_egg"
              geometry="primitive:plane;width:2;height:2;"
              look-at="[camera]"
              position="-3 2 -10"
              material="shader:html;target:#textToDisplay;transparent:true;fps:0;">
    </a-entity>
</a-entity>
<a-entity id="chickenAnimate" visible="false">
    <a-entity id="animate_egg" geometry="primitive:plane;width:2;height:2;"
              look-at="[camera]"
              position="-3 2 -10"
              material="shader:gif;src:url(./egg_2017_1.gif);transparent:true;"
              gif=""></a-entity>
</a-entity>

At this time, the complete game is ready, the source code and demonstration are in: demo9


4.4 Add a little more fun, AR?

At present, the browser's computing power is still unable to recognize complex content through the camera, and can only recognize a limited number of two-dimensional codes at most. But we can also step back and put the scene around the user first.

We call the camera to simulate the scene as the background of the web page.

The iOS system has no access to open cameras and can only be tested on android phones.

We refer to a threex.webcamgrabbing component in a threex.webar library.

etUserMedia() no longer works on insecure origins. 
To use this feature, you should consider switching your application to a secure origin, 
such as HTTPS. See https://goo.gl/rStTGz for more details.

Tragically, the MediaStreamTrack.getSources interface was abandoned by Chrome after the end of the year, and Wechat X5 stopped supporting it, but the new interface navigator.mediaDevices.getUserMedia old mobile phone did not support it.

So we use _____________ webrtc adapter Compatible with new versions of resource acquisition methods. Acquire the content of the camera by oneself.

The navigator.mediaDevices.getUserMedia interface can only be invoked in HTTPS addresses.

Is the final version of the game: demo10

At the bottom of the message, visit the original page.

The final version judges that the device that supports the acquisition of the camera uses the current environment as the background, and the device that does not support the search for eggs in the woods.

Here the game is made, if you want to make a product for users to play, you still need to do a lot of beautification work. At least there should be Loading pages before loading the game, decorative effects of the game interface, tips for float beautification, record results of the game back-end interface, and award to users.


Reference material

Keywords: Mobile shell PHP github

Added by Elarion on Sat, 01 Jun 2019 23:58:10 +0300