📜 ⬆️ ⬇️

A-Frame Review



A-Frame is a web framework that allows you to create various applications, games, scenes in virtual reality (VR). All of the above will be available directly from the browser of your VR helmet. This tool will be useful both for those who want to engage in the development of VR games in the browser, and for example, it can be useful as a platform for creating web BP applications, sites, landing pages. The use of BP Web is limited only by your imagination. Offhand, I can bring a couple of areas of human activity where BP can be useful: education, medicine, sports, sales, recreation.

What's inside?


A-Frame is not written from 0 on pure WebGL, it is based on the Three.js library. Therefore, I recommend to start with the basic concepts of Three.js before starting to work with A-Frame, although this is not necessary, since A-Frame is designed in such a way that you least think about rendering geometry and concentrate more on the logic of your application . That's what the framework is for.

To do this, A-Frame postulates three main points, which we will discuss later.

A-Frame works with HTML


Many of the basic elements of A-Frame, such as scene, camera, box, sphere, etc., are added to the scene via tags of the same name with the prefix a- . Each such element is registered as user. Today, A-Frame (0.8.0) uses the v0 specification.

<a-scene> <a-box position="-1 0.5 -3" rotation="0 45 0" color="#4CC3D9"></a-box> <a-sphere position="0 1.25 -5" radius="1.25" color="#EF2D5E"></a-sphere> </a-scene> 

This small code fragment will draw a WebGL scene to which two objects will be added: a cube and a sphere with the specified parameters. In addition to the above two elements, there are a number of other primitives that can be added to the scene in the same way: <a-circle>, <a-cone>, <a-cylinder>, <a-dodecahedron>, <a-icosahedron> , <a-octahedron>, <a-plane>, <a-ring>, <a-tetrahedron>, <a-torus-knot>, <a-torus>, <a-triangle> . Also in A-Frame there are a number of other elements that perform certain functions:


I would also like to note that we work with DOM elements, and therefore we can use the standard DOM API, including querySelector, getElementById, appendChild, and so on.

A-Frame uses ECS



ECS (Entity Component System) - application and game design pattern. A widespread prevalence was just the same in the second segment. As the name implies, the three basic concepts of the pattern are Entity (Entity), Component (Component), System (System). In the classical form, they are interrelated with each other as follows: we have some container object (Entity) to which we can add components. Usually the component is responsible for a separate part of the logic. For example, we have a Player object (Player), it has a Health component. This component will contain all the logic associated with the replenishment or loss of health of the player (object). And systems, in turn, are needed in order to manage a set of entities united by some components. Typically, a component can register an entity within a system of the same name.

In A-Frame, this pattern is implemented very simply and elegantly - using attributes. As entities, any A-Frame elements are used - <a-scene>, <a-box>, <a-sphere>, etc. But the <a-entity> element stands apart of course. His name speaks for itself. All other elements are essentially wrappers for the components and are made for convenience, since any element can be created using the <a-entity> . For example <a-box> :

 <a-entity geometry="primitive: box; width: 1; height: 1; depth: 1"></a-entity> 

geometry is in this case the component that was added to the <a-entity> entity . By itself, the <a-entity> does not have any logic (in the global sense), and the geometry component essentially turns it into a cube or something else. Another equally important component is the material . It adds material to geometry. The material is responsible for whether our cube will shine like metal, will it have any textures, etc. In pure Three.js we would have to create a separate geometry, a separate material, and then it would all have to be combined in the mesh. In general, this approach saves time.

Any component in A-Frame must be registered globally through a special design:

 AFRAME.registerComponent('hello-world', { init: function () { console.log('Hello, World!'); } }); 

Then this component can be added to the scene, or any other element.

 <a-entity hello-world></a-entity> 

Since we added init callback for our component, as soon as the element is added to the DOM, this callback will work and we will see our message in the console. There are other lifecycle callbacks in the A-Frame components. Let's look at them in more detail:


Another important component concept in A-Frame is the schema. The schema describes the properties of the component. It is defined as follows:

 AFRAME.registerComponent('my-component', { schema: { arrayProperty: {type: 'array', default: []}, integerProperty: {type: 'int', default: 5} } } 

In this case, our component my-component will contain two properties arrayProperty and integerProperty . To pass them to the component, you must set the value of the corresponding attribute.

 <a-entity my-component="arrayProperty: 1,2,3; integerProperty: 7"></a-entity> 

You can get these properties inside the component through the data property.

 AFRAME.registerComponent('my-component', { schema: { arrayProperty: {type: 'array', default: []}, integerProperty: {type: 'int', default: 5} }, init: function () { console.log(this.data); } } 

To get the properties of the component from the entity to which it is added, you can use the slightly modified getAttribute function. When referring to an A-Frame entity, it returns not just the string value of the attribute, but the data object mentioned above.

  console.log(this.el.getAttribute('my-component')); // {arrayProperty: [1,2,3], integerProperty: 7} 

In approximately the same way, you can change the properties of a component:

 this.el.setAttribute('my-component',{arrayProperty: [], integerProperty: 5}) 

Now let's talk about systems. Systems in an A-Frame are registered in a similar way as components:

 AFRAME.registerSystem('my-system', { schema: {}, init: function () { console.log('Hello, System!'); }, }); 

As well as the component, the system has a scheme and callbacks. Only the system has only 5 of them: init, play, pause, tick, tock . The system does not need to be added as a component to an entity. It will automatically be added to the scene.

 this.el.sceneEl.systems['my-system']; 

If the component has the same name as the system, then the system will be accessible by the link this.system .

 AFRAME.registerSystem('enemy', { schema: {}, init: function () {}, }); AFRAME.registerComponent('enemy', { schema: {}, init: function () { const enemySystem = this.system; }, }); 

Usually systems are needed in order to assemble and manage entities with the appropriate components. Here, in theory, should be the logic that belongs to the collection of entities. For example, the management of the appearance of enemies in the game.

Communication in A-Frame takes place via browser events.


And indeed, why reinvent the wheel, if at the moment there is an excellent embedded implementation of a publisher-subscriber for DOM elements. DOM events allow you to listen to browser events, such as pressing a keyboard key, clicking a mouse, and others, as well as custom events. A-Frame offers us a convenient and easy way to communicate between entities, components and systems, through user events. To do this, each A-Frame element is patched by the emit function, it takes three parameters: the first is the name of the event, the second is the data that needs to be transmitted, the third is whether the event should float.

 this.el.emit('start-game', {level: 1}, false); 

You can subscribe to this event in our usual way using addEventListener:

 const someEntity = document.getElementById('someEntity'); someEntity.addEventListener('start-game', () => {...}); 

The bubbling of events here is a very important point, because sometimes two entities that must communicate with each other are on the same level, in which case you can add an event listener to the scene element. He as you could see earlier, is available inside each element through the sceneEl link.

 this.el.sceneEl.addEventListener('start-game', () => {...}); 

Finally


Here, perhaps, all that about what I wanted to tell in this article. In A-Frame there are still many different topics that could be sanctified, but this article is an overview and I wanted to focus the attention of the reader only on the main points. In the next article we will try to create a basic scene in order to test all the knowledge gained in practice. Thanks to all!

Source: https://habr.com/ru/post/439416/