Translation: Crazy geek

www.smashingmagazine.com/2018/11/vir…

In this tutorial, we will create 3d objects and add simple interactions to them. In addition, you will learn how to set up a simple messaging system between the client and server.

Virtual reality (VR) is an experience that relies on a computer-generated environment. It is widely used: the United States uses VR to train for the Winter Olympics; Surgeons are experimenting with virtual medical training; The use of virtual reality in games is one of the most common.

We’re going to focus on the last category, and we’re going to focus on point-and-click adventures. This is a casual game where the goal is to complete a jigsaw puzzle by selecting three-dimensional objects in the scene. In this tutorial, we will build a simple version in virtual reality. This is an introduction to 3D programming and a stand-alone how-to guide to deploying virtual reality models on the Web. You’ll be building with webVR, a framework that has the dual advantage of allowing users to play games in VR, while users without VR glasses can play games on their phones or desktops.

In the second half of this tutorial, you will build a “mirror” for your desktop. This means that all movements made on the mobile device will be mirrored in the desktop preview. This allows you to see what the player is seeing, allowing you to provide guidance, record the game, or simply entertain your guests.

As for why “entertainment” and “joy” in the middle of a strange symbol, because, these two words together in nuggets is illegal, ha ha, do not believe you can try the time of the post ~

The premise condition

Here’s what you need to prepare before you start. For the second half of this tutorial, you will need a Mac OSX. While the code can be applied to any platform, the installation instructions for the dependencies below are for Mac.

  • Internet access, especially Glitch.com;
  • VR glasses (optional, recommended). I’m using Google Cardboard, which costs $15 each.

Step 1: Set up the virtual reality (VR) model

In this step, we will set up a web site with a single static HTML page. This allows you to code from your desktop and automatically deploy to the Web, which can then be loaded onto your phone and placed inside your VR glasses. Or deployed websites can be loaded with separate VR glasses. First go to https://glitch.com/. then

  1. Click “New Project” in the upper right corner.
  2. Click Hello-Express in the drop-down list box.

Next, click on views/index.html in the left sidebar. We call this your “editor.”

To Preview the web page, click Preview in the upper left corner. We’ll use this as your preview. Please note that any changes in the editor will automatically be reflected in the preview, unless there is an error or an unsupported browser.

Go back to the editor and replace the current HTML with the code frame for the VR model below.


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

      <! -- blue sky -->
      <a-sky color="#a3d0ed"></a-sky>

      <! -- camera with wasd and panning controls -->
      <a-entity camera look-controls wasd-controls position=0.5 2 "0" rotation="0 0 0"></a-entity>

      <! -- brown ground -->
      <a-box shadow id="ground" shadow="receive:true" color="# 847452" width="10" height="0.1" depth="10"></a-box>

      <! -- start code here -->
      <! -- end code here -->
    </a-scene>
  </body>
</html>
Copy the code

You can then see the following:

To preview this feature on VR glasses, use the URL in the OmniBar. In the figure above, the URL is https://point-and-click-vr-game.glitch.me/. Your work environment is now set up and ready to share this URL with family and friends. In the next step, you will create a virtual reality model.

Step 2: Create a model of the tree

Now, we’ll create a tree using primitives from Aframe. IO. These are standard objects that Aframe preprogrammed for ease of use. Specifically, Aframe refers to an object as an entity. There are three concepts related to entities:

  1. Geometry and materials,
  2. Convert shaft,
  3. Relative transformation.

First, geometry and materials are the two building blocks of all 3d objects in the code. Geometry defines a series of “shapes” — cubes, spheres, pyramids, etc. Materials define the static properties of a shape, such as color, reflectivity, and roughness.

Aframe simplifies this concept by defining primitives such as

, < A-sphere >,

and many other basic principles to simplify geometry and its materials. Let’s start by defining a green sphere. In line 19 of the code,
Add the following.

<! -- start code here -->
<a-sphere color="green" radius="0.5"></a-sphere>  <! -- new line -->
<! -- end code here -->
Copy the code

Second, there are three axes to transform objects. The x axis is moving horizontally, and as we move to the right, the x value increases. The y axis is running vertically, and the y value increases as we move up. The Z axis is used perpendicular to your screen, increasing the z value as the object moves towards you. We can translate, rotate, or scale entities along these three axes.

For example, to move an object “to the right,” we need to increase its x value. To rotate an object up, we need to rotate it along the Y-axis. Next we modify line 19 to move the sphere “up” — this means you need to increase the y-value of the sphere. Note that all conversions are specified as



, meaning that to increase their y value, a second value needs to be added. By default, all objects are in position 0,0,0. Add Position below.


<! -- start code here -->
<a-sphere color="green" radius="0.5" position="0 1 0"></a-sphere> <! -- edited line -->
<! -- end code here -->
Copy the code

Third, all transformations are relative to their parent. To add a trunk to a tree, add a cylinder to the top of the sphere. This will determine the position of the trunk relative to the sphere and will integrate your tree into a unit. In “a – sphere… Add

entities between > and
tags.

<a-sphere color="green" radius="0.5" position="0 1 0">
	<a-cylinder color="#84651e" position="0-0.9 0" radius="0.05"></a-cylinder> <! -- new line -->
</a-sphere>
Copy the code

Then add two green spheres for more leaves.

<a-sphere color="green" radius="0.5" position="0 0.75 0">
	<a-cylinder color="#84651e" position="0-0.9 0" radius="0.05"></a-cylinder>
	<a-sphere color="green" radius="0.35" position="0 0.5 0"></a-sphere> <! -- new line -->
	<a-sphere color="green" radius="0.2" position="0 0.8 0"></a-sphere> <! -- new line -->
</a-sphere>
Copy the code

Switch back to Preview and you should see the following tree:

Reload the website preview on VR glasses and view it. In the next section, we’ll make the tree interactive.

Step 3: Add Click Interaction to the Model

To make an entity interactive, you need:

  • Add animation,
  • Click to trigger animation.

Since the end user uses VR glasses, the action of clicking is the equivalent of staring: in other words, staring at an object is “clicking” it. To implement these changes, we’ll start with the cursor. Replace line 13 with the following to redefine the camera.

<a-entity camera look-controls wasd-controls position=0.5 2 "0" rotation="0 0 0">
  <a-entity cursor="fuse: true; fuseTimeout: 250"
            position="0 0-1"
            geometry="primitive: ring; radiusInner: 0.02; radiusOuter: 0.03"
            material="color: black; shader: flat"
            scale="0.5 0.5 0.5"
            raycaster="far: 20; interval: 1000; objects: .clickable">
    <! -- add animation here -->
  </a-entity>
</a-entity>
Copy the code

The above code adds a cursor that triggers a click action. Note the objects:.clickable property. This means that all objects with a clickable class will trigger an animation and receive a click command when appropriate. We will also animate the click cursor so that the user knows when the cursor triggers the click. When pointing to a clickable object, the cursor slowly shrinks, catching after one second to represent the clicked object. Replace the comment

<a-animation begin="fusing" easing="ease-in" attribute="scale"
  fill="backwards" from="1 1" to="0.2 0.2 0.2" dur="250"></a-animation>
Copy the code

Move the tree 2 units to the right, and modify act 29 to add the class “clickable” to the tree.

<a-sphere color="green" radius="0.5" position="2, 0.75 0" class="clickable">
Copy the code

Next, we will:

  • Specify the animation,
  • Click to trigger the animation.

Thanks to Aframe’s easy-to-use animation entities, both steps can be done in quick succession.

Add an

tag at line 33, immediately after the

tag but before the end of .

<a-animation begin="click" attribute="position" from="2, 0.75 0" to="2.2 0.75 0" fill="both" direction="alternate" repeat="1"></a-animation>
Copy the code

The above properties specify many configurations for the animation. Animation:

  • Triggered by the “click” event
  • Modification of the treeposition
  • From original position2, 0.75 0start
  • End in2.2 0.75 0(moved 0.2 units to the right)
  • Animation of the trip to and from the destination
  • Alternate animations between destinations to and from
  • Repeat this animation once. This means that the object animation plays twice in total: once to its destination and once back to its original location.

Finally, switch to Preview, and then drag from the cursor to the tree. Once the black circle is placed on the tree, the tree moves to the right and back.

This ends all the basics needed to build a point-and-click adventure game in virtual reality. To view and play a more complete version of this game, see the video below (alvinwan.com/shift/scene…

Next, let’s set up a simple nodeJS server to provide static demonstrations.

Step 4: Set up the NodeJS server

In this step, we will set up a basic, functional nodeJS server to serve your existing VR model. In the left sidebar of the editor, select package.json.

First delete lines 2-4.

"/ / 1": "describes your app and its dependencies".2 "/ /": "https://docs.npmjs.com/files/package.json"."/ / 3": "updating this file will download and update your packages".Copy the code

Change the name to MirrorVR.

{
  "name": "mirrorvr".// change me
  "version": "0.0.1".Copy the code

Under dependencies, add socket. IO.

"dependencies": {
  "express": "^ 4.16.3"."socketio": "^ 1.0.0",},Copy the code

Update the repository URL to match current Glitch. The example Glitch project is called Point-and-click-VR-game. Replace it with the name of your Glitch project.

"repository": {
  "url": "https://glitch.com/edit/#! /point-and-click-vr-game"
},
Copy the code

Finally, change the “Glitch” label to “VR”.

"keywords": [
  "node"."vr".// change me
  "express"
]
Copy the code

Double check your package.json to see if it matches the following.

{
  "name": "mirrorvr"."version": "0.0.1"."description": "Mirror virtual reality models"."main": "server.js"."scripts": {
    "start": "node server.js"
  },
  "dependencies": {
    "express": "^ 4.16.3"."socketio": "^ 1.0.0"
  },
  "engines": {
    "node": "8.x"
  },
  "repository": {
    "url": "https://glitch.com/edit/#! /point-and-click-vr-game"
  },
  "license": "MIT"."keywords": [
    "node"."vr"."express"]}Copy the code

In views/index.html, double check the code in the previous section to see if it matches the following.


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

      <! -- blue sky -->
      <a-sky color="#a3d0ed"></a-sky>

      <! -- camera with wasd and panning controls -->
      <a-entity camera look-controls wasd-controls position=0.5 2 "0" rotation="0 0 0">
        <a-entity cursor="fuse: true; fuseTimeout: 250"
                  position="0 0-1"
                  geometry="primitive: ring; radiusInner: 0.02; radiusOuter: 0.03"
                  material="color: black; shader: flat"
                  scale="0.5 0.5 0.5"
                  raycaster="far: 20; interval: 1000; objects: .clickable">
            <a-animation begin="fusing" easing="ease-in" attribute="scale"
               fill="backwards" from="1 1" to="0.2 0.2 0.2" dur="250"></a-animation>
        </a-entity>
      </a-entity>

      <! -- brown ground -->
      <a-box shadow id="ground" shadow="receive:true" color="# 847452" width="10" height="0.1" depth="10"></a-box>

      <! -- start code here -->
      <a-sphere color="green" radius="0.5" position="2, 0.75 0" class="clickable">
        <a-cylinder color="#84651e" position="0-0.9 0" radius="0.05"></a-cylinder>
        <a-sphere color="green" radius="0.35" position="0 0.5 0"></a-sphere>
        <a-sphere color="green" radius="0.2" position="0 0.8 0"></a-sphere>
        <a-animation begin="click" attribute="position" from="2, 0.75 0" to="2.2 0.75 0" fill="both" direction="alternate" repeat="1"></a-animation>
      </a-sphere>
      <! -- end code here -->
    </a-scene>
  </body>
</html>
Copy the code

Then modify server.js.

Start by importing a few NodeJS packages.

  • Express is the Web framework for the server.

  • http

    This allows us to start a daemon that listens for activity on various ports.

  • Socket. IO with a socket implementation allows us to communicate in real time between the client and server.

We also initialize the ExpressJS program when we import these packages. Note that the first two lines are already written for you.

var express = require('express');
var app = express();

/* start new code */
var http = require('http').Server(app);
var io = require('socket.io')(http);
/* end new code */

// we've started you off with Express, 
Copy the code

After the package is loaded, the server returns index.html as the home page. Note that there is no new code below; This is just an interpretation of the existing source code.

// http://expressjs.com/en/starter/basic-routing.html
app.get('/'.function(request, response) {
  response.sendFile(__dirname + '/views/index.html');
});
Copy the code

Finally, the existing source code instructs the program to bind and listen for a port of 3000 by default unless otherwise noted.

// listen for requests :)
var listener = app.listen(process.env.PORT, function() {
  console.log('Your app is listening on port ' + listener.address().port);
});
Copy the code

After editing, Glitch automatically reloads the server. Click “Show” in the upper left corner to preview your application.

Your Web application is now up and running. Next, we will send messages from the client to the server.

Step 5: Send information from the client to the server

In this step, we will initialize the connection to the server with the client. The client will also notify the server whether it is a phone or a desktop. First, import the Javascript file to be added in views/index.html.

After line 4, include a new script.

<script src="/client.js" type="text/javascript"></script>
Copy the code

At line 14, add camera-Listener to the property list of the camera entity.

<a-entity camera-listener camera look-controls...>.</a-entity>
Copy the code

Then, switch to public/client.js in the left sidebar. Remove all Javascript code from this file. Then, define a utility function that checks if the client is a mobile device.

/** * Check if client is on mobile */
function mobilecheck() {
  var check = false;
  (function(a){if(/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|ki ndle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)? |phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.test(a)||/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|cra w|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc |fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01| 02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)| oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12| 21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1 )|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1 |si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0.4))) check = true; })(navigator.userAgent||navigator.vendor||window.opera);
  return check;
};
Copy the code

Next, we will define a series of messages to exchange with the server side. Defines a new socket. IO object to represent the connection between the client and server. Once the socket is connected, the message is logged to the console.

var socket = io();

socket.on('connect'.function() {
  console.log(' * Connection established');
});
Copy the code

Check if it is a mobile device and send the corresponding information to the server using the EMIT function.

if (mobilecheck()) {
  socket.emit('newHost');
} else {
  socket.emit('newMirror');
}
Copy the code

This completes the client’s message sending. Now modify the server code so that it can receive this message and react appropriately. Open the server.js file on the server side.

Process the new connection and listen for the client type immediately. Add the following at the end of the file.

/** * Handle socket interactions */

io.on('connection'.function(socket) {

  socket.on('newMirror'.function() {
    console.log(" * Participant registered as 'mirror'")}); socket.on('newHost'.function() {
    console.log(" * Participant registered as 'host'");
  });
});
Copy the code

Preview the program again by clicking “Show” in the upper left corner. Load the same url on your mobile device. In your terminal, you will see the following.

listening on *: 3000
 * Participant registered as 'host'
 * Participant registered as 'mirror'
Copy the code

This is the first simple message passing where our client sends the information back to the server. Exit the running NodeJS process. For the last part of this step, we will have the client send the camera information back to the server. Open the public/client. Js.

At the end of the file, add the following.

var camera;
if (mobilecheck()) {
  AFRAME.registerComponent('camera-listener', {
    tick: function () {
      camera = this.el.sceneEl.camera.el;
      var position = camera.getAttribute('position');
      var rotation = camera.getAttribute('rotation');
      socket.emit('onMove', {
        "position": position,
        "rotation": rotation }); }}); }Copy the code

Save and close. Open your server code file server.js to listen for the onMove event.

Add the following to the newHost block of the socket code:

socket.on('newHost'.function() {
    console.log(" * Participant registered as 'host'");
    /* start new code */
    socket.on('onMove'.function(data) {
      console.log(data);
    });
    /* end new code */
  });
Copy the code

Load the preview again on desktop and mobile devices. Once the mobile client is connected, the server immediately starts recording the camera position and rotation information sent from the client to the server. Then do the opposite, sending the information from the server back to the client.

Step 6: Send information from the server to the client

In this step, you will send the host’s camera information to all images. Open the main server source file server.js.

Change the onMove event handling to the following:

socket.on('onMove'.function(data) {
  console.log(data);  // delete me
  socket.broadcast.emit('move', data)
});
Copy the code

The broadcast modifier ensures that the server sends this information to all clients connected to the socket. After sending this information to the client, you need to set up the camera for the mirror accordingly. Open the client script public/client.js.

Check here to see if the client is desktop. If so, the movement data is received and logged accordingly.

if(! mobilecheck()) { socket.on('move'.function(data) {
    console.log(data);
  });
}
Copy the code

Load the preview on desktop and mobile devices. In a desktop browser, open the development console. Then, load the application on the phone. Once the phone has successfully loaded the program, the development console on the desktop displays information such as camera position and rotation.

Open the client script public/client.js again. We will finally adjust the client camera based on the message sent.

Modify the event handler above to get the Move event.

socket.on('move'.function(data) {
  /* start new code */
  camera.setAttribute('rotation', data["rotation"]);
  camera.setAttribute('position', data["position"]);
  /* end new code */
});
Copy the code

Load apps on your desktop and phone. Every action you make on your phone is reflected in a mirror on your desktop! This completes the mirror part of the program. As a desktop user, you can now preview what a mobile user will see. The concepts presented in this section will be crucial to further developing the game, as we will also move from single player to multiplayer.

conclusion

In this tutorial, we create 3d objects and add simple interactions to them. A simple messaging system has also been built between the client and server to enable a desktop preview of what the user sees.

These concepts even go beyond webVR, as the concepts of geometry and materials extend to SceneKit (related to ARKit), three.js (the backbone of Aframe), and other 3D libraries on iOS. Together, these simple building blocks give us the flexibility to create a fully fledged point-and-click adventure game. More importantly, they allow us to create any game with a click-based interface.

Here are a few resources and examples for you to explore further:

  • Full implementation of live preview feature above MirrorVR. A live preview of any VIRTUAL reality model on your mobile device can be added to your desktop with just a Javascript link.
  • Virtual reality models at the Bit by Bit Children’s Painting gallery.
  • Aframe virtual reality development examples, developer documentation, and other resources.
  • Google Cardboard Experiences offers custom tools for teachers.

Next time we will build an entire game that uses network sockets for real-time communication between players in a virtual reality game.

Welcome to pay attention to front-end public number: front-end pioneer, get more front-end dry goods.