📜 ⬆️ ⬇️

Create a base scene in A-Frame



From the author of the translation: in the previous article we discussed the basic concepts of A-Frame. To continue the cycle, I wanted to create a lesson that would illustrate the main features of A-Frame, but I realized that it would be better to translate an article from the official site, which, in my opinion, was written very well, and there is no point in repeating such an article.

Let's make a basic scene in A-Frame to understand how the framework works. We need an initial understanding of HTML. In this lesson we will learn:


You can play with the code here.

Let's start with HTML


<html> <head> <script src="https://aframe.io/releases/0.8.0/aframe.min.js"></script> </head> <body> <a-scene> </a-scene> </body> </html> 

We connect A-Frame through the <script> tag added to the <head> section. The library code is on the CDN. An A-Frame connection must be added before <a-scene> since A-Frame registers custom HTML elements that must be defined before <a-scene> is added to the DOM, otherwise the scene will remain empty.

Next, we add the <a-scene> tag to the body of the <body> document. <a-scene> is the element with which the scene is controlled. Adding elements to the <a-scene> we add them to the actual 3D scene. <a-scene> is responsible for everything you need: installs WebGL, <canvas>, camera, light, renderer (rendering), as well as everything related to WebVR support on platforms such as HTC Vive, Oculus Rift, Samsung GearVR, Oculus Go and Google Cardboards.

Adding objects


Using <a-scene>, we add 3D objects using standard A-Frame primitives, for example <a-box>. We can use <a-box> simply as a normal HTML element, adding attributes to it for customization. Here are some more standard primitives available in A-Frame: <a-cylinder>, <a-plane>, <a-sphere>.

In the following code example, we will add a color <a-box>. More attributes for <a-box> can be found here .



It should be noted that the primitives in A-Frame are just wrappers for some components. This is convenient, but you need to understand that by <a-box> there is a <a-entity> with geometry and material components:

 <a-entity id="box" geometry="primitive: box" material="color: red"></a-entity> 

In any case, since the camera and the cube added to the scene are at the same point 0,0,0 , we will not be able to see the cube until we change its position. It's simple enough to do this: you just need to change the position attribute to move our cube in space.

Transformation of an object in 3D


Let's first talk about 3D space. A-Frame uses the right-handed coordinate system. The camera's default direction is: the positive X-axis goes to the right, the positive Y-axis goes up and the positive Z-axis goes back from the screen in the direction of us:



A-Frame measures distance in meters (and not in pixels as in React360), since the WebVR API returns position data and poses in meters. When you are designing a BP scene it is very important to consider the actual dimensions of the objects. A cube with a height of 10 meters may look normal on the screens of our computers, but it will be too massive when immersed in virtual reality.

The units of rotation in A-Frame are degrees (not radians as in Three.js), so they will automatically be converted to radians when they hit Three.js. To determine the positive direction of rotation, the right-hand rule is used. Direct the right hand finger along the positive direction of any axis, then the direction in which our fingers are bent and will be the positive direction of rotation.

To move, rotate or scale our cube, we can change the position , rotation and scale components (represented by the attributes of the <a-box> tag) respectively. Let's try to start with applying rotation and scaling:

 <a-scene> <a-box color="red" rotation="0 45 45" scale="2 2 2"></a-box> </a-scene> 

This should rotate our cube to given angles and stretch it along all axes in half.

Transformations of ancestors and descendants


The scene graph in A-Frame is implemented using HTML. Each element of such a structure can have several descendants and only one ancestor. Any descendant in such a structure inherits the transformation properties (position, rotation, scale) of its ancestor.

For example, we can add a sphere as a descendant of a cube:

 <a-scene> <a-box position="0 2 0" rotation="0 45 45" scale="2 4 2"> <a-sphere position="1 0 3"></a-sphere> </a-box> </a-scene> 

The position of the sphere on the stage will be 1 2 3 , not 1 0 3 , since by default the sphere is in the coordinates of its ancestor, that is, the cube is 0 2 0 . This point in this case will be the reference point for the sphere, which has its coordinates 1 0 3 . The same goes for rotation and scale. If any of the attributes of the cube are changed, this change will automatically affect all descendants (in our case, the sphere).

If we added a cylinder as a descendant of a sphere, the transformations of a cylinder would be calculated based on the transformations of its ancestors: the cube and the sphere.

Place our cube ahead of the camera


Now let's make our cube visible to our camera. We can move our cube 5 meters along the Z axis in a negative direction (that is, away from us). Let's also raise it two meters up the Y axis. All this can be done by changing the position attribute:

 <a-scene> <a-box color="red" position="0 2 -5" rotation="0 45 45" scale="2 2 2"></a-box> </a-scene> 

Now we see him! Juha!



Control


For flat displays (laptops, computers), the camera is controlled by default by dragging with the mouse and WASD or the arrows on the keyboard. In the phones, the accelerometer is responsible for this. Although A-Frame is a framework for WebVR, it supports these control schemes so that the scene can be viewed without a virtual reality helmet.



If you use a virtual reality helmet, then the control will be carried out with the help of controllers and a helmet. To get into virtual reality from the browser of your helmet, you need to click on the icon with the virtual reality helmet, which is located in the lower right corner. If you use a helmet with 6 degrees of freedom, and you have real space, you can physically walk along the scene you created.

Add the environment


A-Frame allows developers to create and share components that are then easy to use in their projects. The environment component created by Diego Goburn generates various environments, and all this is achieved with just one line of code! The Environment Component is a great and easy way to create a visual platform for our BP applications. It allows you to create dozens of environments, customizable by many parameters.

First you need to connect the script to the section after A-Frame:

 <head> <script src="https://aframe.io/releases/0.8.0/aframe.min.js"></script> <script src="https://unpkg.com/aframe-environment-component/dist/aframe-environment-component.min.js"></script> </head> 

After that, already in the scene, you need to add the <a-entity> tag with the environment attribute and the specified settings, let it be a forest with 200 trees:

 <a-scene> <a-box color="red" position="0 2 -5" rotation="0 45 45" scale="2 2 2"></a-box> <!-- Окружение, прямо из коробки! --> <a-entity environment="preset: forest; dressingAmount: 200"></a-entity> </a-scene> 

Note : keep in mind that when using weak “iron” such a scene can also slow down. For example, this can happen on mobile devices like the Oculus Go. So if you support mobile devices, watch the number of objects generated.

Apply textures


We can apply a texture to our cube using an image, video or canvas using the src attribute, just as we do with a regular tag. We also need to remove color = “red” as the color will still not be displayed when using the texture.

 <a-scene> <a-box src="https://i.imgur.com/mYmmbrp.jpg" position="0 2 -5" rotation="0 45 45" scale="2 2 2"></a-box> <a-sky color="#222"></a-sky> </a-scene> 

Now we have to see our cube already with the texture loaded online.


We use the file upload system


To improve the performance of your A-Frame application, we recommend that you use a file upload management system . This system allows the browser to cache different files (images, videos, 3d models). A-Frame is responsible for ensuring that all of these files are uploaded before rendering.

If we write a tag inside <a-assets>, A-Frame will not display it as usual HTML, it will process the source and send it to Three.js. It is important to note that by downloading an image once we can use it anywhere, for example, as a texture. A-Frame will also take care of cross-domain and other possible problems associated with transferring files over the network.

To use the file upload control system to add texture you need:


 <a-scene> <a-assets> <img id="boxTexture" src="https://i.imgur.com/mYmmbrp.jpg"> </a-assets> <a-box src="#boxTexture" position="0 2 -5" rotation="0 45 45" scale="2 2 2"></a-box> <a-sky color="#222"></a-sky> </a-scene> 

Create an arbitrary environment


We already talked about the environment component above. It is generated automatically. But what if we need to make our own environment: add clouds, earth, other objects? I propose to talk about this further.

Add a background image for the scene.


In order to add a background image for the scene in A-Frame there is a special element <a-sky> . <a-sky> allows you to add both pure color and 360 images or videos. For example, to add a dark gray background you need to write the following code:

 <a-scene> <a-box color="red" position="0 2 -5" rotation="0 45 45" scale="2 2 2"></a-box> <a-sky color="#222"></a-sky> </a-scene> 

Or we can use 360 ​​photo:

 <a-scene> <a-assets> <img id="boxTexture" src="https://i.imgur.com/mYmmbrp.jpg"> <img id="skyTexture" src="https://cdn.aframe.io/360-image-gallery-boilerplate/img/sechelt.jpg"> </a-assets> <a-box src="#boxTexture" position="0 2 -5" rotation="0 45 45" scale="2 2 2"></a-box> <a-sky src="#skyTexture"></a-sky> </a-scene> 

Add land


To add ground, we will use the <a-plane> element. By default, planes in A-Frame are oriented parallel to the XY axis. To make the plane parallel to the earth, we need to rotate it so that it is parallel to the XZ axis. This can be done by turning the plane by -90 ° along the X axis.

 <a-plane src="#groundTexture" rotation="-90 0 0" width="30" height="30" repeat="10 10"></a-plane> 

We work on the light


To change the lighting of our scene, we can add or reconfigure the <a-light> element. By default, we had no light sources, but the A-Frame itself adds ambient light and directional light. If A-Frame had not added these lights, the scene would have been completely black. If we add our own light sources, then those sources that A-Frame adds by default will be deleted.

We will add one source of diffused light that will have a bluish-green hue (to harmonize with the sky). The scattered light should fall on all elements of our scene (of course, if they have materials, but by default it is).

We will also add a point light source. A point light source is like a light bulb. The effect of lighting such a source will depend entirely on the distance to the object.

 <a-scene> <a-assets> <img id="boxTexture" src="https://i.imgur.com/mYmmbrp.jpg"> <img id="skyTexture" src="https://cdn.aframe.io/360-image-gallery-boilerplate/img/sechelt.jpg"> <img id="groundTexture" src="https://cdn.aframe.io/a-painter/images/floor.jpg"> </a-assets> <a-box src="#boxTexture" position="0 2 -5" rotation="0 45 45" scale="2 2 2"></a-box> <a-sky src="#skyTexture"></a-sky> <a-light type="ambient" color="#445451"></a-light> <a-light type="point" intensity="2" position="2 4 4"></a-light> </a-scene> 

Adding animation


We have the ability to add animation using the built-in A-Frame animation system. An animation changes a value (a component property) over time. We only need to add the <a-animation> element as a descendant of some object, for example <a-box>. Let's try to animate the cube so that it moves up and down.

 <a-scene> <a-assets> <img id="boxTexture" src="https://i.imgur.com/mYmmbrp.jpg"> </a-assets> <a-box src="#boxTexture" position="0 2 -5" rotation="0 45 45" scale="2 2 2"> <a-animation attribute="position" to="0 2.2 -5" direction="alternate" dur="2000" repeat="indefinite"></a-animation> </a-box> </a-scene> 

We say <a-animation>:



Additional details


<a-animation> uses an A-Frame render cycle, so every change to an object's properties occurs once per frame. If you need more control and you want to change the values ​​manually, you can write an A-Frame component with a tick callback and a library such as Tween.js (which is available by the way AFRAME.TWEEN by the way). For best performance, frame-by-frame operations should be performed at the A-Frame level, you do not need to create your own requestAnimationFrame function when A-Frame already has it .

Add interactivity


Let's add the ability to interact with our cube: when we look at it, we will increase its size, and when clicked, it scrolls around its axis.

Assuming that most developers do not have VR helmets with controllers, we will focus on using the base component for typing on computers and mobile devices - the cursor . The cursor provides the ability to “click” on the object through the guidance for mobile devices and by clicking the mouse for computers. But you need to understand that the cursor is just one of the ways to interact, everything will be a little different if we have real BP controllers.

To bind the cursor to the camera, we need to add it as a child to the camera element (<a-camera>).

Since we have not yet defined the camera, A-Frame did it automatically. but since we need to add a cursor to the camera, we will define <a-camera> manually and add <a-cursor> there:

 <a-scene> <a-assets> <img id="boxTexture" src="https://i.imgur.com/mYmmbrp.jpg"> </a-assets> <a-box src="#boxTexture" position="0 2 -5" rotation="0 45 45" scale="2 2 2"> <a-animation attribute="position" to="0 2.2 -5" direction="alternate" dur="2000" repeat="indefinite"></a-animation> </a-box> <a-camera> <a-cursor></a-cursor> </a-camera> </a-scene> 

If we look at the documentation of the cursor , we see that it handles hover events such as mouseenter, mouseleave and also the click event.

Event listener component


One way to handle cursor events is to add an event listener through JavaScript just like for the usual DOM element. If you are not sure about your JavaScript knowledge, you can skip this section until the next one.

In JavaScript, we get the element via querySelector, and using addEventListener , and setAttribute to increase the size of the cube when the cursor is moved . Note: A-Frame modifies the setAttribute so that it can work with several components at once. We can add {x, y, z} as the second argument.

 <script> var boxEl = document.querySelector('a-box'); boxEl.addEventListener('mouseenter', function () { boxEl.setAttribute('scale', {x: 2, y: 2, z: 2}); }); </script> 

But the faster way is to encapsulate the logic inside the A-Frame component. This method does not require waiting for the scene to load; we do not need to use selectors because the component gives us context:

 <script> AFRAME.registerComponent('scale-on-mouseenter', { schema: { to: {default: '2.5 2.5 2.5'} }, init: function () { var data = this.data; this.el.addEventListener('mouseenter', function () { this.setAttribute('scale', data.to); }); } }); </script> 

We can add this component via HTML attribute:

 <script> AFRAME.registerComponent('scale-on-mouseenter', { // ... }); </script> <a-scene> <!-- ... --> <a-box src="#boxTexture" position="0 2 -5" rotation="0 45 45" scale="2 2 2" scale-on-mouseenter="to: 2.2 2.2 2.2"> <a-animation attribute="position" to="0 2.2 -5" direction="alternate" dur="2000" repeat="indefinite"></a-animation> </a-box> <!-- ... --> </a-scene> 

Event Animation


<a-animation> has the ability to start and end its animation when an object produces an event. This can be done through the begin attribute by specifying the name of the event.

We can add two animations for the cursor component — mouseenter and onemouseleave events to resize our cube, and one for cube turns around the Y axis at the click event:

 <a-box color="#FFF" width="4" height="10" depth="2" position="-10 2 -5" rotation="0 0 45" scale="2 0.5 3" src="#texture"> <a-animation attribute="position" to="0 2.2 -5" direction="alternate" dur="2000" repeat="indefinite"></a-animation> <!-- These animations will start when the box is looked at. --> <a-animation attribute="scale" begin="mouseenter" dur="300" to="2.3 2.3 2.3"></a-animation> <a-animation attribute="scale" begin="mouseleave" dur="300" to="2 2 2"></a-animation> <a-animation attribute="rotation" begin="click" dur="2000" to="360 405 45"></a-animation> </a-box> 

Add audio


Audio is very important for complete immersion in virtual reality. Even adding just white noise to the background can take time. We recommend using sound for each scene. One way to add sound is to add the <audio> element to the <a-assets> and autoplay attribute:

 <a-scene> <a-assets> <audio src="https://cdn.aframe.io/basic-guide/audio/backgroundnoise.wav" autoplay preload></audio> </a-assets> <!-- ... --> </a-scene> 

Or we can add spatial audio using the <a-sound> element. This component makes the sound louder when we get closer to the source. We can position the <a-sound> using the position attribute:

 <a-scene> <!-- ... --> <a-sound src="https://cdn.aframe.io/basic-guide/audio/backgroundnoise.wav" autoplay="true" position="-3 1 -4"></a-sound> <!-- ... --> </a-scene> 

Add text


A-Frame has a text component. There are several ways to display it and each method has its advantages and disadvantages. A-Frame has SDF implementation through three-bmfont-text which is quite productive.

For this lesson we suggest using the simplest form of the text, <a-text>:

 <a-text value="Hello, A-Frame!" color="#BBB" position="-0.9 0.2 -3" scale="1.5 1.5 1.5"></a-text> 

There are other ways to add text:


Conclusion


You can play with the code here . View the scene in real time here .

From the author pervoda: Thank you all for your attention! In the next article I will try to tell you about my experience of creating a game on A-Frame: about all the subtleties and difficulties that were waiting for me in the development process.

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